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`
1010also returns the nav so the config builder can consume it.
1111"""
1212
1717
1818import 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).
2323NavItem = "str | dict[str, str | list[NavItem]]"
2424
2525ROOT = Path (__file__ ).parent .parent .parent
4040
4141
4242class _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
117121def 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