Skip to content

Commit 5464185

Browse files
committed
docs: fix summary truncation and harden the API generator
Docstring summaries took the first line, not the first paragraph, so wrapped one-sentence docstrings shipped cut off mid-sentence on the package index. The private-module filter only looked at the path leaf, so a private subpackage would have published its whole subtree; and an export resolving outside the documented packages now fails with a named error instead of a raw griffe traceback.
1 parent a05482d commit 5464185

1 file changed

Lines changed: 25 additions & 19 deletions

File tree

scripts/docs/gen_ref_pages.py

Lines changed: 25 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
"""Generate the API reference pages and navigation.
22
3-
Zensical does not run MkDocs plugins, so the work that ``mkdocs-gen-files`` and
4-
``mkdocs-literate-nav`` used to do at build time happens here as a plain
5-
pre-build step: this module writes a mkdocstrings stub (``::: <module>``) for
6-
every public module under ``docs/api/`` and returns the matching nested
7-
navigation, which ``scripts/docs/build_config.py`` splices into the build config.
3+
Zensical does not run MkDocs plugins, so the work that `mkdocs-gen-files` and
4+
`mkdocs-literate-nav` used to do at build time happens here as a plain
5+
pre-build step: this module writes a mkdocstrings stub (`::: <module>`) for
6+
every public module under `docs/api/` and returns the matching nested
7+
navigation, which `scripts/docs/build_config.py` splices into the build config.
88
9-
Run as a script it just (re)generates ``docs/api/``; imported, :func:`generate`
9+
Run as a script it just (re)generates `docs/api/`; imported, `generate`
1010
also returns the nav so the config builder can consume it.
1111
"""
1212

@@ -17,9 +17,9 @@
1717

1818
import griffe
1919

20-
# A MkDocs/Zensical nav is a list of entries, each either ``{title: url}`` for a
21-
# page or ``{title: [children]}`` for a section (a bare ``url`` string attaches
22-
# a section index page, courtesy of the ``navigation.indexes`` feature).
20+
# A MkDocs/Zensical nav is a list of entries, each either `{title: url}` for a
21+
# page or `{title: [children]}` for a section (a bare `url` string attaches
22+
# a section index page, courtesy of the `navigation.indexes` feature).
2323
NavItem = "str | dict[str, str | list[NavItem]]"
2424

2525
ROOT = Path(__file__).parent.parent.parent
@@ -40,7 +40,7 @@
4040

4141

4242
class _Node:
43-
"""A module (``url``) and/or a package with child modules (``children``)."""
43+
"""A module (`url`) and/or a package with child modules (`children`)."""
4444

4545
def __init__(self) -> None:
4646
self.url: str | None = None
@@ -64,19 +64,19 @@ def _compact_index(package: griffe.Module, documented: set[str]) -> str | None:
6464
"""Build a compact index body for a package that re-exports another package's API.
6565
6666
mkdocstrings renders a member re-exported across a package boundary
67-
(``from other_package import y`` + ``__all__``) as a full duplicate of its
67+
(`from other_package import y` + `__all__`) as a full duplicate of its
6868
canonical documentation whenever the other package happens to be loaded
6969
already, and silently omits it when it isn't — which of the two a package
7070
index gets depends on page rendering order. Same-package re-exports
71-
(``mcp_types`` re-exporting its private ``._types`` module) always resolve
72-
and are unaffected, so such packages keep the plain ``::: package`` stub
73-
(return ``None``) and their index remains the full, canonical rendering.
71+
(`mcp_types` re-exporting its private `._types` module) always resolve
72+
and are unaffected, so such packages keep the plain `::: package` stub
73+
(return `None`) and their index remains the full, canonical rendering.
7474
7575
For an affected package, pin the semantics instead of inheriting the
7676
accident: every export whose canonical page exists elsewhere under the API
7777
reference becomes a link to it, and only exports documented nowhere else
7878
(re-exports from private modules) keep their full body here, via an
79-
explicit ``members:`` list.
79+
explicit `members:` list.
8080
"""
8181
prefix = f"{package.path}."
8282
exports = {str(export): package.members[str(export)] for export in package.exports or ()}
@@ -96,9 +96,13 @@ def _compact_index(package: griffe.Module, documented: set[str]) -> str | None:
9696
inline.append(name)
9797
link_target = f"{package.path}.{name}"
9898
entry = f"- [`{name}`][{link_target}]"
99-
target = member.final_target if member.is_alias else member
99+
try:
100+
target = member.final_target if member.is_alias else member
101+
except griffe.AliasResolutionError as exc:
102+
msg = f"gen_ref_pages: export {package.path}.{name} resolves outside the documented packages"
103+
raise SystemExit(msg) from exc
100104
if docstring := target.docstring:
101-
summary = docstring.value.split("\n", 1)[0]
105+
summary = " ".join(docstring.value.split("\n\n", 1)[0].split("\n"))
102106
entry += f" — {summary}"
103107
sections.setdefault(_KIND_SECTIONS[target.kind], []).append(entry)
104108

@@ -115,7 +119,7 @@ def _compact_index(package: griffe.Module, documented: set[str]) -> str | None:
115119

116120

117121
def generate() -> list[NavItem]:
118-
"""Write ``docs/api/**.md`` stubs and return the API-section navigation."""
122+
"""Write `docs/api/**.md` stubs and return the API-section navigation."""
119123
if API_DIR.exists():
120124
shutil.rmtree(API_DIR)
121125

@@ -134,7 +138,9 @@ def generate() -> list[NavItem]:
134138
if parts[-1] == "__init__":
135139
parts = parts[:-1]
136140
doc_path = doc_path.with_name("index.md")
137-
elif parts[-1].startswith("_"):
141+
# A private component anywhere makes the module private: checking
142+
# only the leaf would publish pages for e.g. mcp._vendor.util.
143+
if any(part.startswith("_") for part in parts):
138144
continue
139145

140146
ident = ".".join(parts)

0 commit comments

Comments
 (0)