docs: bring docforge docstrings and wiki to GSDFC standard
- fix GSDFC spec contradictions in __init__ docstring (parenthesized types, fenced-block rule) and sync generated README - rewrite docstrings across loaders, models, nav, servers, renderers, cli; sync .pyi stubs - add pydoclint (google style) gate to dev extras and pyproject config - fix mcp nav resources doc:// -> docs:// - refresh docs/lib and docs/mcp, drop stale docforge/ duplicate group - update wiki pages and add GSDFC + MCP guides under 05_development
This commit is contained in:
@@ -41,6 +41,18 @@ doc-forge build --mcp --module my_package
|
||||
doc-forge build --mcp --mkdocs --module my_package
|
||||
```
|
||||
|
||||
## Include a hand-written wiki in the MkDocs site:
|
||||
|
||||
```bash
|
||||
doc-forge build --wiki --mkdocs --module my_package
|
||||
```
|
||||
|
||||
## Build wiki pages only (no module required):
|
||||
|
||||
```bash
|
||||
doc-forge build --wiki --site-name my_package
|
||||
```
|
||||
|
||||
## Serve MkDocs locally:
|
||||
|
||||
```bash
|
||||
@@ -129,7 +141,9 @@ GSDFC defines how docstrings must be written so they render correctly in MkDocs
|
||||
|
||||
- Use **Markdown headings** at package and module level.
|
||||
- Use **Google-style structured sections** at class, function, and method level.
|
||||
- Use type hints in signatures instead of duplicating types in prose.
|
||||
- Use type hints in signatures.
|
||||
- Use parenthesized types in prose entries (`name (Type):`) that match the
|
||||
signature types. This keeps docstrings self-contained and machine-parseable.
|
||||
- Write summaries in imperative form.
|
||||
- Sections are separated by `---`
|
||||
|
||||
@@ -189,7 +203,10 @@ Example:
|
||||
engine.run()
|
||||
```
|
||||
|
||||
Avoid fenced code blocks inside structured sections.
|
||||
Avoid fenced code blocks inside argument descriptions and other prose lines.
|
||||
|
||||
Inside `Example:` sections, fenced `python` code blocks are allowed and must be
|
||||
indented four spaces, matching the examples below.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -35,7 +35,8 @@ def load_openapi_spec(spec_path: Path) -> dict:
|
||||
Load and validate an OpenAPI specification from a JSON file.
|
||||
|
||||
Args:
|
||||
spec_path: Path to the OpenAPI JSON specification file.
|
||||
spec_path (Path):
|
||||
Path to the OpenAPI JSON specification file.
|
||||
|
||||
Returns:
|
||||
dict:
|
||||
@@ -68,7 +69,8 @@ def derive_metadata(spec: dict) -> OpenAPIMetadata:
|
||||
Derive MkDocs site metadata from an OpenAPI spec ``info`` block.
|
||||
|
||||
Args:
|
||||
spec: Parsed OpenAPI specification.
|
||||
spec (dict):
|
||||
Parsed OpenAPI specification.
|
||||
|
||||
Returns:
|
||||
OpenAPIMetadata:
|
||||
@@ -97,8 +99,10 @@ def generate_api_sources(spec: dict, docs_dir: Path) -> None:
|
||||
an ``index.md`` embedding the swagger UI is generated alongside it.
|
||||
|
||||
Args:
|
||||
spec: Parsed OpenAPI specification.
|
||||
docs_dir: Directory (for example ``docs/api``) where the swagger
|
||||
spec (dict):
|
||||
Parsed OpenAPI specification.
|
||||
docs_dir (Path):
|
||||
Directory (for example ``docs/api``) where the swagger
|
||||
sources are written.
|
||||
"""
|
||||
docs_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
@@ -4,6 +4,15 @@
|
||||
Command definitions for the doc-forge CLI.
|
||||
|
||||
Provides the CLI structure using Click, including build, serve, and tree commands.
|
||||
|
||||
---
|
||||
|
||||
Notes:
|
||||
- The `build` command validates requested modes before generating anything.
|
||||
- `--mkdocs`, `--api`, and `--wiki` share a single MkDocs build; `--mcp`
|
||||
generates a machine-readable bundle independently.
|
||||
|
||||
---
|
||||
"""
|
||||
|
||||
from pathlib import Path
|
||||
@@ -12,6 +21,7 @@ import click
|
||||
|
||||
from docforge.cli import api_utils, mcp_utils, mkdocs_utils
|
||||
from docforge.loaders import GriffeLoader
|
||||
from docforge.models import DocObject
|
||||
|
||||
|
||||
@click.group()
|
||||
@@ -99,9 +109,9 @@ def build(
|
||||
"""
|
||||
Build documentation artifacts.
|
||||
|
||||
This command performs the full documentation build pipeline:
|
||||
style of the selected platform, generates renderer-specific
|
||||
documentation sources, and optionally builds the final output.
|
||||
This command runs the full documentation pipeline: it loads Python
|
||||
modules, generates renderer-specific documentation sources, and
|
||||
optionally builds or serves the final output.
|
||||
|
||||
Depending on the selected options, the build can target:
|
||||
|
||||
@@ -110,6 +120,12 @@ def build(
|
||||
- Hand-written wiki pages included in the MkDocs site
|
||||
- MCP structured documentation resources
|
||||
|
||||
Notes:
|
||||
- At least one of `--mcp`, `--mkdocs`, `--wiki`, or `--api` must be
|
||||
provided.
|
||||
- `--mkdocs`, `--api`, and `--wiki` are combined into a single MkDocs
|
||||
build, while `--mcp` emits a machine-readable bundle.
|
||||
|
||||
Args:
|
||||
mcp (bool):
|
||||
Enable MCP documentation generation.
|
||||
@@ -126,16 +142,16 @@ def build(
|
||||
module_is_source (bool):
|
||||
Treat the specified module directory as the project root.
|
||||
|
||||
module (Optional[str]):
|
||||
module (str | None):
|
||||
Python module import path to document.
|
||||
|
||||
openapi_spec (Optional[Path]):
|
||||
openapi_spec (Path | None):
|
||||
Path to the OpenAPI JSON specification used for API docs.
|
||||
|
||||
project_name (Optional[str]):
|
||||
project_name (str | None):
|
||||
Optional override for the project name.
|
||||
|
||||
site_name (Optional[str]):
|
||||
site_name (str | None):
|
||||
Display name for the MkDocs site.
|
||||
|
||||
docs_dir (Path):
|
||||
@@ -147,7 +163,7 @@ def build(
|
||||
nav_file (Path):
|
||||
Path to the navigation specification file.
|
||||
|
||||
template (Optional[Path]):
|
||||
template (Path | None):
|
||||
Optional custom MkDocs configuration template.
|
||||
|
||||
mkdocs_yml (Path):
|
||||
@@ -282,7 +298,7 @@ def serve(
|
||||
mkdocs (bool):
|
||||
Serve the MkDocs development site.
|
||||
|
||||
module (Optional[str]):
|
||||
module (str | None):
|
||||
Python module import path to serve via MCP.
|
||||
|
||||
mkdocs_yml (Path):
|
||||
@@ -333,7 +349,7 @@ def tree(
|
||||
module (str):
|
||||
Python module import path to introspect.
|
||||
|
||||
project_name (Optional[str]):
|
||||
project_name (str | None):
|
||||
Optional name to display as the project root.
|
||||
"""
|
||||
loader = GriffeLoader()
|
||||
@@ -347,7 +363,7 @@ def tree(
|
||||
_print_object(obj, indent="│ ")
|
||||
|
||||
|
||||
def _print_object(obj, indent: str) -> None:
|
||||
def _print_object(obj: DocObject, indent: str) -> None:
|
||||
"""
|
||||
Recursively print a documentation object and its members.
|
||||
|
||||
@@ -355,7 +371,7 @@ def _print_object(obj, indent: str) -> None:
|
||||
and prints each object with indentation to represent hierarchy.
|
||||
|
||||
Args:
|
||||
obj:
|
||||
obj (DocObject):
|
||||
Documentation object to print.
|
||||
|
||||
indent (str):
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
from click.core import Group
|
||||
|
||||
from docforge.models import DocObject
|
||||
|
||||
cli: Group
|
||||
|
||||
def build(
|
||||
@@ -33,4 +34,4 @@ def tree(
|
||||
module: str,
|
||||
project_name: str | None,
|
||||
) -> None: ...
|
||||
def _print_object(obj: Any, indent: str) -> None: ...
|
||||
def _print_object(obj: DocObject, indent: str) -> None: ...
|
||||
|
||||
@@ -2,6 +2,16 @@
|
||||
# Summary
|
||||
|
||||
Utilities for working with MCP in the doc-forge CLI.
|
||||
|
||||
---
|
||||
|
||||
Notes:
|
||||
- `generate_resources` produces the bundle consumed by `MCPServer`:
|
||||
`index.json`, `nav.json`, and per-module resources under `modules/`.
|
||||
- Resource URIs use the `docs://` scheme: `docs://index`, `docs://nav`,
|
||||
and `docs://modules/{module}`.
|
||||
|
||||
---
|
||||
"""
|
||||
|
||||
from pathlib import Path
|
||||
@@ -26,7 +36,7 @@ def generate_resources(module: str, project_name: str | None, out_dir: Path) ->
|
||||
Python module import path used as the entry point for
|
||||
documentation generation.
|
||||
|
||||
project_name (Optional[str]):
|
||||
project_name (str | None):
|
||||
Optional override for the project name used in generated
|
||||
documentation metadata.
|
||||
|
||||
|
||||
@@ -2,6 +2,15 @@
|
||||
# Summary
|
||||
|
||||
Utilities for working with MkDocs in the doc-forge CLI.
|
||||
|
||||
---
|
||||
|
||||
Notes:
|
||||
- A single generated `mkdocs.yml` serves lib, api, and wiki content with
|
||||
merged navigation. Wiki navigation, when enabled, precedes every other
|
||||
group and its `index.md` becomes the site `Home`.
|
||||
|
||||
---
|
||||
"""
|
||||
|
||||
import os
|
||||
@@ -44,14 +53,14 @@ def generate_sources(
|
||||
docs_dir (Path):
|
||||
Directory where the generated Markdown files will be written.
|
||||
|
||||
project_name (Optional[str]):
|
||||
project_name (str | None):
|
||||
Optional override for the project name used in documentation metadata.
|
||||
|
||||
module_is_source (Optional[bool]):
|
||||
module_is_source (bool | None):
|
||||
If True, treat the specified module directory as the project root
|
||||
rather than a nested module.
|
||||
|
||||
readme_dir (Optional[Path]):
|
||||
readme_dir (Path | None):
|
||||
Directory where the generated README.md should be written. If not
|
||||
provided, defaults to the parent of ``docs_dir``.
|
||||
"""
|
||||
@@ -105,7 +114,7 @@ def generate_config(
|
||||
nav_file (Path):
|
||||
Path to the `docforge.nav.yml` navigation specification.
|
||||
|
||||
template (Optional[Path]):
|
||||
template (Path | None):
|
||||
Optional path to a fully custom MkDocs configuration template.
|
||||
If not provided, built-in templates are merged; the provided
|
||||
template replaces the built-in templates entirely.
|
||||
@@ -116,19 +125,19 @@ def generate_config(
|
||||
site_name (str):
|
||||
Display name for the generated documentation site.
|
||||
|
||||
modes (Optional[Iterable[str]]):
|
||||
modes (Iterable[str] | None):
|
||||
Documentation modes to enable. Each mode contributes its own
|
||||
built-in template fragment (for example ``lib``, ``api``, or
|
||||
``wiki``), merged on top of the shared ``mkdocs.common.yml``
|
||||
template.
|
||||
|
||||
site_description (Optional[str]):
|
||||
site_description (str | None):
|
||||
Optional site description written into the configuration.
|
||||
|
||||
site_author (Optional[str]):
|
||||
site_author (str | None):
|
||||
Optional site author written into the configuration.
|
||||
|
||||
wiki_dir (Optional[Path]):
|
||||
wiki_dir (Path | None):
|
||||
Optional path to a hand-written wiki directory (for example
|
||||
``docs/wiki``). When provided, the site navigation is derived
|
||||
from the wiki file structure and placed before the navigation
|
||||
@@ -186,13 +195,13 @@ def _load_template(
|
||||
|
||||
When a custom template path is provided, it is used as-is. Otherwise the
|
||||
shared ``mkdocs.common.yml`` template is deep-merged with the fragments
|
||||
contributed by each enabled mode (``lib`` or ``api``).
|
||||
contributed by each enabled mode (``lib``, ``api``, or ``wiki``).
|
||||
|
||||
Args:
|
||||
template (Optional[Path]):
|
||||
template (Path | None):
|
||||
Optional fully custom template that replaces the built-ins.
|
||||
|
||||
modes (Optional[Iterable[str]]):
|
||||
modes (Iterable[str] | None):
|
||||
Documentation modes whose template fragments should be merged.
|
||||
|
||||
Returns:
|
||||
@@ -237,7 +246,8 @@ def _item_name(item: object) -> str:
|
||||
first key. This is used to deduplicate plugin and extension lists.
|
||||
|
||||
Args:
|
||||
item: List entry, either a string or a single-key mapping.
|
||||
item (object):
|
||||
List entry, either a string or a single-key mapping.
|
||||
|
||||
Returns:
|
||||
str:
|
||||
@@ -253,8 +263,10 @@ def _merge_list(base: list, added: list) -> list:
|
||||
Merge two lists, preserving order and dropping duplicates by name.
|
||||
|
||||
Args:
|
||||
base: Existing list entries.
|
||||
added: Entries to append when not already present.
|
||||
base (list):
|
||||
Existing list entries.
|
||||
added (list):
|
||||
Entries to append when not already present.
|
||||
|
||||
Returns:
|
||||
list:
|
||||
@@ -279,8 +291,10 @@ def _deep_merge(base: dict, part: dict) -> dict:
|
||||
the fragment override the base.
|
||||
|
||||
Args:
|
||||
base: Configuration being built up.
|
||||
part: Template fragment to merge into the base.
|
||||
base (dict):
|
||||
Configuration being built up.
|
||||
part (dict):
|
||||
Template fragment to merge into the base.
|
||||
|
||||
Returns:
|
||||
dict:
|
||||
|
||||
@@ -6,6 +6,14 @@ Utilities for loading and introspecting Python modules using Griffe.
|
||||
This module provides the `GriffeLoader` class and helper utilities used to
|
||||
discover Python modules, introspect their structure, and convert the results
|
||||
into doc-forge documentation models.
|
||||
|
||||
---
|
||||
|
||||
Notes:
|
||||
- All analysis is static; analyzed modules are never executed.
|
||||
- Private members (names starting with `_`) are skipped during conversion.
|
||||
|
||||
---
|
||||
"""
|
||||
|
||||
import logging
|
||||
@@ -46,12 +54,12 @@ def discover_module_paths(
|
||||
module_name (str):
|
||||
Top-level package name to discover modules from.
|
||||
|
||||
project_root (Path, optional):
|
||||
project_root (Path | None):
|
||||
Root directory used to resolve module paths. If not provided, the
|
||||
current working directory is used.
|
||||
|
||||
Returns:
|
||||
List[str]:
|
||||
list[str]:
|
||||
A sorted list of unique dotted module import paths.
|
||||
|
||||
Raises:
|
||||
@@ -88,6 +96,10 @@ class GriffeLoader:
|
||||
This loader uses the Griffe introspection engine to analyze Python source
|
||||
code and transform the extracted information into `Project`, `Module`,
|
||||
and `DocObject` instances used by doc-forge.
|
||||
|
||||
Attributes:
|
||||
_loader (_GriffeLoader):
|
||||
Internal Griffe loader with dedicated module and line collections.
|
||||
"""
|
||||
|
||||
def __init__(self) -> None:
|
||||
@@ -106,7 +118,7 @@ class GriffeLoader:
|
||||
self,
|
||||
module_paths: list[str],
|
||||
project_name: str | None = None,
|
||||
skip_import_errors: bool = None,
|
||||
skip_import_errors: bool | None = None,
|
||||
) -> Project:
|
||||
"""
|
||||
Load multiple modules and assemble them into a Project model.
|
||||
@@ -116,14 +128,14 @@ class GriffeLoader:
|
||||
object.
|
||||
|
||||
Args:
|
||||
module_paths (List[str]):
|
||||
module_paths (list[str]):
|
||||
List of dotted module import paths to load.
|
||||
|
||||
project_name (str, optional):
|
||||
project_name (str | None):
|
||||
Optional override for the project name. Defaults to the top-level
|
||||
name of the first module.
|
||||
|
||||
skip_import_errors (bool, optional):
|
||||
skip_import_errors (bool | None):
|
||||
If True, modules that fail to load will be skipped instead of raising an error.
|
||||
|
||||
Returns:
|
||||
@@ -172,6 +184,21 @@ class GriffeLoader:
|
||||
Returns:
|
||||
Module:
|
||||
A populated `Module` instance.
|
||||
|
||||
Raises:
|
||||
ImportError:
|
||||
If the module cannot be loaded by Griffe.
|
||||
|
||||
KeyError:
|
||||
If the loaded module is missing from the module collection.
|
||||
|
||||
Example:
|
||||
Load a single module:
|
||||
|
||||
```python
|
||||
loader = GriffeLoader()
|
||||
module = loader.load_module("mypackage.submodule")
|
||||
```
|
||||
"""
|
||||
self._loader.load(path)
|
||||
griffe_module = self._loader.modules_collection[path]
|
||||
@@ -260,7 +287,7 @@ class GriffeLoader:
|
||||
Griffe object to inspect.
|
||||
|
||||
Returns:
|
||||
Optional[str]:
|
||||
str | None:
|
||||
The raw docstring text if available, otherwise `None`.
|
||||
"""
|
||||
try:
|
||||
@@ -277,7 +304,7 @@ class GriffeLoader:
|
||||
Griffe object to inspect.
|
||||
|
||||
Returns:
|
||||
Optional[str]:
|
||||
str | None:
|
||||
String representation of the object's signature if available, otherwise `None`.
|
||||
"""
|
||||
try:
|
||||
|
||||
@@ -11,6 +11,7 @@ class GriffeLoader:
|
||||
"""Griffe-based introspection loaders.
|
||||
|
||||
This is the only supported introspection backend in doc-forge.
|
||||
Converts Griffe results into `Project`, `Module`, and `DocObject` models.
|
||||
"""
|
||||
|
||||
def __init__(self) -> None: ...
|
||||
@@ -18,7 +19,7 @@ class GriffeLoader:
|
||||
self,
|
||||
module_paths: list[str],
|
||||
project_name: str | None = ...,
|
||||
skip_import_errors: bool = ...,
|
||||
skip_import_errors: bool | None = ...,
|
||||
) -> Project:
|
||||
"""Load a documentation project from Python modules."""
|
||||
|
||||
|
||||
@@ -7,6 +7,13 @@ This module defines the `Module` class used in the doc-forge documentation
|
||||
model. A `Module` acts as a container for top-level documented objects
|
||||
(classes, functions, variables, and other members) discovered during
|
||||
introspection.
|
||||
|
||||
---
|
||||
|
||||
Notes:
|
||||
- Only public members are stored; private names are filtered by the loader.
|
||||
|
||||
---
|
||||
"""
|
||||
|
||||
from collections.abc import Iterable
|
||||
@@ -26,10 +33,10 @@ class Module:
|
||||
path (str):
|
||||
Dotted import path of the module.
|
||||
|
||||
docstring (Optional[str]):
|
||||
docstring (str | None):
|
||||
Module-level documentation string, if present.
|
||||
|
||||
members (Dict[str, DocObject]):
|
||||
members (dict[str, DocObject]):
|
||||
Mapping of object names to their corresponding `DocObject` representations.
|
||||
"""
|
||||
|
||||
@@ -45,7 +52,7 @@ class Module:
|
||||
path (str):
|
||||
Dotted import path identifying the module.
|
||||
|
||||
docstring (Optional[str]):
|
||||
docstring (str | None):
|
||||
Module-level documentation text, if available.
|
||||
"""
|
||||
self.path = path
|
||||
|
||||
@@ -7,6 +7,14 @@ This module defines the `DocObject` class, the fundamental recursive unit of
|
||||
the doc-forge documentation model. Each `DocObject` represents a Python
|
||||
entity such as a class, function, method, or attribute, and may contain nested
|
||||
members that form a hierarchical documentation structure.
|
||||
|
||||
---
|
||||
|
||||
Notes:
|
||||
- `DocObject` instances form a tree mirroring the Python import hierarchy.
|
||||
- Objects are renderer-agnostic and may be consumed by any renderer.
|
||||
|
||||
---
|
||||
"""
|
||||
|
||||
from collections.abc import Iterable
|
||||
@@ -30,13 +38,13 @@ class DocObject:
|
||||
path (str):
|
||||
Fully qualified dotted path to the object.
|
||||
|
||||
signature (Optional[str]):
|
||||
signature (str | None):
|
||||
Callable signature if the object represents a callable.
|
||||
|
||||
docstring (Optional[str]):
|
||||
docstring (str | None):
|
||||
Raw docstring text extracted from the source code.
|
||||
|
||||
members (Dict[str, DocObject]):
|
||||
members (dict[str, DocObject]):
|
||||
Mapping of member names to child `DocObject` instances.
|
||||
"""
|
||||
|
||||
@@ -61,10 +69,10 @@ class DocObject:
|
||||
path (str):
|
||||
Fully qualified dotted path of the object.
|
||||
|
||||
signature (Optional[str]):
|
||||
signature (str | None):
|
||||
Callable signature if applicable.
|
||||
|
||||
docstring (Optional[str]):
|
||||
docstring (str | None):
|
||||
Documentation string associated with the object.
|
||||
"""
|
||||
self.name = name
|
||||
@@ -82,7 +90,8 @@ class DocObject:
|
||||
nested objects to their parent containers.
|
||||
|
||||
Args:
|
||||
obj: Documentation object to add as a member.
|
||||
obj (DocObject):
|
||||
Documentation object to add as a member.
|
||||
"""
|
||||
self.members[obj.name] = obj
|
||||
|
||||
|
||||
@@ -6,6 +6,14 @@ Documentation model representing a project.
|
||||
This module defines the `Project` class, the top-level container used by
|
||||
doc-forge to represent a documented codebase. A `Project` aggregates multiple
|
||||
modules and provides access to them through a unified interface.
|
||||
|
||||
---
|
||||
|
||||
Notes:
|
||||
- Modules are keyed by their dotted import path.
|
||||
- Objects are renderer-agnostic; the same model feeds every renderer.
|
||||
|
||||
---
|
||||
"""
|
||||
|
||||
from collections.abc import Iterable
|
||||
@@ -24,7 +32,7 @@ class Project:
|
||||
name (str):
|
||||
Name of the project.
|
||||
|
||||
modules (Dict[str, Module]):
|
||||
modules (dict[str, Module]):
|
||||
Mapping of module paths to `Module` instances.
|
||||
"""
|
||||
|
||||
@@ -35,6 +43,14 @@ class Project:
|
||||
Args:
|
||||
name (str):
|
||||
Name used to identify the documentation project.
|
||||
|
||||
Example:
|
||||
Create a project and register a module:
|
||||
|
||||
```python
|
||||
project = Project("mypackage")
|
||||
project.add_module(module)
|
||||
```
|
||||
"""
|
||||
self.name = name
|
||||
self.modules: dict[str, Module] = {}
|
||||
|
||||
@@ -4,6 +4,14 @@ MkDocs navigation emitter.
|
||||
This module provides the ``MkDocsNavEmitter`` class, which converts a
|
||||
``ResolvedNav`` instance into the navigation structure required by the
|
||||
MkDocs ``nav`` configuration.
|
||||
|
||||
---
|
||||
|
||||
Notes:
|
||||
- The emitted structure is a list of dictionaries, one per top-level nav
|
||||
entry, matching the MkDocs ``nav`` YAML format.
|
||||
|
||||
---
|
||||
"""
|
||||
|
||||
from pathlib import Path
|
||||
@@ -25,13 +33,15 @@ class MkDocsNavEmitter:
|
||||
Generate a navigation structure for ``mkdocs.yml``.
|
||||
|
||||
Args:
|
||||
nav: Resolved navigation data describing documentation groups
|
||||
nav (ResolvedNav):
|
||||
Resolved navigation data describing documentation groups
|
||||
and their associated Markdown files.
|
||||
|
||||
Returns:
|
||||
A list of dictionaries representing the MkDocs navigation layout.
|
||||
Each dictionary maps a navigation label to a page or a list of
|
||||
pages.
|
||||
list[dict[str, Any]]:
|
||||
A list of dictionaries representing the MkDocs navigation layout.
|
||||
Each dictionary maps a navigation label to a page or a list of
|
||||
pages.
|
||||
"""
|
||||
result: list[dict[str, Any]] = []
|
||||
|
||||
@@ -59,11 +69,14 @@ class MkDocsNavEmitter:
|
||||
resulting path is relative to the documentation root.
|
||||
|
||||
Args:
|
||||
path: Filesystem path to convert.
|
||||
docs_root: Root directory of the documentation sources.
|
||||
path (Path):
|
||||
Filesystem path to convert.
|
||||
docs_root (Path | None):
|
||||
Root directory of the documentation sources.
|
||||
|
||||
Returns:
|
||||
POSIX-style path relative to the documentation root.
|
||||
str:
|
||||
POSIX-style path relative to the documentation root.
|
||||
"""
|
||||
if docs_root and path.is_absolute():
|
||||
try:
|
||||
|
||||
@@ -3,6 +3,14 @@ Navigation resolution utilities.
|
||||
|
||||
This module resolves a ``NavSpec`` against the filesystem by expanding glob
|
||||
patterns and validating that referenced documentation files exist.
|
||||
|
||||
---
|
||||
|
||||
Notes:
|
||||
- Glob resolution is recursive and returns paths in sorted order.
|
||||
- Unmatched patterns raise ``FileNotFoundError`` to fail fast on typos.
|
||||
|
||||
---
|
||||
"""
|
||||
|
||||
import glob
|
||||
@@ -35,9 +43,12 @@ class ResolvedNav:
|
||||
Initialize a ResolvedNav instance.
|
||||
|
||||
Args:
|
||||
home: Relative path to the home page within the documentation root.
|
||||
groups: Mapping of group titles to resolved documentation file paths.
|
||||
docs_root: Root directory of the documentation source files.
|
||||
home (str | None):
|
||||
Relative path to the home page within the documentation root.
|
||||
groups (dict[str, list[Path]]):
|
||||
Mapping of group titles to resolved documentation file paths.
|
||||
docs_root (Path | None):
|
||||
Root directory of the documentation source files.
|
||||
"""
|
||||
self.home = home
|
||||
self.groups = groups
|
||||
@@ -47,8 +58,10 @@ class ResolvedNav:
|
||||
"""
|
||||
Iterate over all files referenced by the navigation structure.
|
||||
|
||||
Returns:
|
||||
An iterable of ``Path`` objects representing documentation files.
|
||||
Yields:
|
||||
Path:
|
||||
A documentation file referenced by the navigation, including
|
||||
the home page when defined.
|
||||
|
||||
Raises:
|
||||
RuntimeError: If the home page is defined but the documentation
|
||||
@@ -74,11 +87,14 @@ def resolve_nav(
|
||||
that referenced documentation files exist within the documentation root.
|
||||
|
||||
Args:
|
||||
spec: Navigation specification describing documentation layout.
|
||||
docs_root: Root directory containing documentation Markdown files.
|
||||
spec (NavSpec):
|
||||
Navigation specification describing documentation layout.
|
||||
docs_root (Path):
|
||||
Root directory containing documentation Markdown files.
|
||||
|
||||
Returns:
|
||||
A ``ResolvedNav`` instance containing validated navigation paths.
|
||||
ResolvedNav:
|
||||
A `ResolvedNav` instance containing validated navigation paths.
|
||||
|
||||
Raises:
|
||||
FileNotFoundError: If the documentation root does not exist or a
|
||||
@@ -92,10 +108,12 @@ def resolve_nav(
|
||||
Resolve a glob pattern relative to the documentation root.
|
||||
|
||||
Args:
|
||||
pattern: Glob pattern used to match documentation files.
|
||||
pattern (str):
|
||||
Glob pattern used to match documentation files.
|
||||
|
||||
Returns:
|
||||
A sorted list of matching ``Path`` objects.
|
||||
list[Path]:
|
||||
A sorted list of matching `Path` objects.
|
||||
|
||||
Raises:
|
||||
FileNotFoundError: If the pattern does not match any files.
|
||||
|
||||
@@ -4,8 +4,19 @@ Navigation specification model.
|
||||
This module defines the ``NavSpec`` class, which represents the navigation
|
||||
structure defined by the user in the doc-forge navigation specification
|
||||
(typically ``docforge.nav.yml``).
|
||||
|
||||
---
|
||||
|
||||
Notes:
|
||||
- The spec file supports an optional ``icon`` mapping for MkDocs theme
|
||||
customization.
|
||||
- All file references in ``groups`` are relative to the documentation root.
|
||||
|
||||
---
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
import yaml
|
||||
@@ -38,10 +49,13 @@ class NavSpec:
|
||||
Initialize a NavSpec instance.
|
||||
|
||||
Args:
|
||||
home: Relative path to the home document.
|
||||
groups: Mapping of group names to lists of path patterns
|
||||
home (str | None):
|
||||
Relative path to the home document.
|
||||
groups (dict[str, list[str]]):
|
||||
Mapping of group names to lists of path patterns
|
||||
(glob expressions).
|
||||
icon: Optional mapping of theme icon entries applied to the
|
||||
icon (dict[str, str] | None):
|
||||
Optional mapping of theme icon entries applied to the
|
||||
generated MkDocs configuration.
|
||||
"""
|
||||
self.home = home
|
||||
@@ -49,15 +63,17 @@ class NavSpec:
|
||||
self.icon = icon
|
||||
|
||||
@classmethod
|
||||
def load(cls, path: Path) -> "NavSpec":
|
||||
def load(cls, path: Path) -> NavSpec:
|
||||
"""
|
||||
Load a navigation specification from a YAML file.
|
||||
|
||||
Args:
|
||||
path: Filesystem path to the navigation specification file.
|
||||
path (Path):
|
||||
Filesystem path to the navigation specification file.
|
||||
|
||||
Returns:
|
||||
A ``NavSpec`` instance representing the parsed configuration.
|
||||
NavSpec:
|
||||
A ``NavSpec`` instance representing the parsed configuration.
|
||||
|
||||
Raises:
|
||||
FileNotFoundError: If the specified file does not exist.
|
||||
@@ -105,8 +121,9 @@ class NavSpec:
|
||||
Return all path patterns referenced by the specification.
|
||||
|
||||
Returns:
|
||||
A list containing the home document (if defined) and all
|
||||
group pattern entries.
|
||||
list[str]:
|
||||
A list containing the home document (if defined) and all
|
||||
group pattern entries.
|
||||
"""
|
||||
patterns: list[str] = []
|
||||
|
||||
@@ -127,10 +144,12 @@ def load_nav_spec(path: Path) -> NavSpec:
|
||||
corresponding ``NavSpec`` instance.
|
||||
|
||||
Args:
|
||||
path: Path to the navigation specification file.
|
||||
path (Path):
|
||||
Path to the navigation specification file.
|
||||
|
||||
Returns:
|
||||
A ``NavSpec`` instance representing the parsed specification.
|
||||
NavSpec:
|
||||
A ``NavSpec`` instance representing the parsed specification.
|
||||
|
||||
Raises:
|
||||
FileNotFoundError: If the specification file does not exist.
|
||||
|
||||
@@ -20,6 +20,7 @@ modified by doc-forge; only the navigation layout is inferred.
|
||||
"""
|
||||
|
||||
import re
|
||||
from collections.abc import Callable
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
@@ -46,7 +47,7 @@ def build_wiki_nav(wiki_dir: Path) -> list[dict[str, Any]]:
|
||||
Path to the hand-written wiki directory, for example ``docs/wiki``.
|
||||
|
||||
Returns:
|
||||
List[Dict[str, Any]]:
|
||||
list[dict[str, Any]]:
|
||||
Navigation entries compatible with the MkDocs ``nav`` configuration.
|
||||
The list is empty if the wiki contains no Markdown files.
|
||||
|
||||
@@ -75,7 +76,10 @@ def build_wiki_nav(wiki_dir: Path) -> list[dict[str, Any]]:
|
||||
return nav
|
||||
|
||||
|
||||
def _render_entries(base_dir: Path, rel) -> list[dict[str, Any]]:
|
||||
def _render_entries(
|
||||
base_dir: Path,
|
||||
rel: Callable[[Path], str],
|
||||
) -> list[dict[str, Any]]:
|
||||
"""
|
||||
Render navigation entries for the children of a wiki directory.
|
||||
|
||||
@@ -87,11 +91,11 @@ def _render_entries(base_dir: Path, rel) -> list[dict[str, Any]]:
|
||||
base_dir (Path):
|
||||
Directory whose children are rendered.
|
||||
|
||||
rel:
|
||||
rel (Callable[[Path], str]):
|
||||
Callable converting a wiki file path into a docs-relative path.
|
||||
|
||||
Returns:
|
||||
List[Dict[str, Any]]:
|
||||
list[dict[str, Any]]:
|
||||
Navigation entries for ``base_dir`` in natural sort order.
|
||||
"""
|
||||
children = sorted(
|
||||
@@ -152,7 +156,7 @@ def _natural_key(name: str) -> list[object]:
|
||||
Filename or directory name to key.
|
||||
|
||||
Returns:
|
||||
List[object]:
|
||||
list[object]:
|
||||
Mixed list of lowercased strings and integers used for sorting.
|
||||
"""
|
||||
return [
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
from collections.abc import Callable
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
@@ -5,11 +6,36 @@ def build_wiki_nav(wiki_dir: Path) -> list[dict[str, Any]]:
|
||||
"""
|
||||
Derive an MkDocs navigation block from a wiki directory.
|
||||
|
||||
Args:
|
||||
wiki_dir (Path):
|
||||
Path to the hand-written wiki directory.
|
||||
|
||||
Returns:
|
||||
Wiki navigation entries compatible with the MkDocs
|
||||
`nav` configuration.
|
||||
list[dict[str, Any]]:
|
||||
Wiki navigation entries compatible with the MkDocs `nav`
|
||||
configuration.
|
||||
|
||||
Raises:
|
||||
FileNotFoundError: if the wiki directory does not exist
|
||||
FileNotFoundError:
|
||||
If the wiki directory does not exist.
|
||||
"""
|
||||
...
|
||||
|
||||
def _render_entries(
|
||||
base_dir: Path,
|
||||
rel: Callable[[Path], str],
|
||||
) -> list[dict[str, Any]]:
|
||||
"""
|
||||
Render navigation entries for the children of a wiki directory.
|
||||
|
||||
Args:
|
||||
base_dir (Path):
|
||||
Directory whose children are rendered.
|
||||
rel (Callable[[Path], str]):
|
||||
Callable converting a wiki file path into a docs-relative path.
|
||||
|
||||
Returns:
|
||||
list[dict[str, Any]]:
|
||||
Navigation entries for `base_dir` in natural sort order.
|
||||
"""
|
||||
...
|
||||
|
||||
@@ -49,7 +49,7 @@ class MCPRenderer:
|
||||
nav.append(
|
||||
{
|
||||
"module": module.path,
|
||||
"resource": f"doc://modules/{module.path}",
|
||||
"resource": f"docs://modules/{module.path}",
|
||||
}
|
||||
)
|
||||
|
||||
@@ -101,7 +101,7 @@ class MCPRenderer:
|
||||
Module instance to convert.
|
||||
|
||||
Returns:
|
||||
Dict:
|
||||
dict:
|
||||
Dictionary representing the module and its documented objects.
|
||||
"""
|
||||
data: dict = {
|
||||
@@ -124,7 +124,7 @@ class MCPRenderer:
|
||||
Documentation object to convert.
|
||||
|
||||
Returns:
|
||||
Dict:
|
||||
dict:
|
||||
Dictionary describing the object and any nested members.
|
||||
"""
|
||||
data: dict = {
|
||||
@@ -149,7 +149,7 @@ class MCPRenderer:
|
||||
Serialize data to formatted JSON.
|
||||
|
||||
Args:
|
||||
data (Dict):
|
||||
data (dict):
|
||||
Dictionary to serialize.
|
||||
|
||||
Returns:
|
||||
|
||||
@@ -54,7 +54,7 @@ class MkDocsRenderer:
|
||||
out_dir (Path):
|
||||
Directory where generated Markdown files will be written.
|
||||
|
||||
module_is_source (bool, optional):
|
||||
module_is_source (bool | None):
|
||||
If True, treat the specified module as the documentation root
|
||||
rather than nesting it inside a folder.
|
||||
"""
|
||||
@@ -87,11 +87,10 @@ class MkDocsRenderer:
|
||||
"""
|
||||
Generate a `README.md` file from the root module docstring.
|
||||
|
||||
Behavior:
|
||||
|
||||
- If `module_is_source` is True, `README.md` is written to the project
|
||||
root directory.
|
||||
- If False, README generation is currently not implemented.
|
||||
Notes:
|
||||
- If `module_is_source` is True, `README.md` is written to the
|
||||
project root directory.
|
||||
- If False, README generation is currently not implemented.
|
||||
|
||||
Args:
|
||||
project (Project):
|
||||
@@ -100,10 +99,10 @@ class MkDocsRenderer:
|
||||
docs_dir (Path):
|
||||
Directory containing generated documentation sources.
|
||||
|
||||
module_is_source (Optional[bool]):
|
||||
module_is_source (bool | None):
|
||||
Whether the module is treated as the project source root.
|
||||
|
||||
readme_dir (Optional[Path]):
|
||||
readme_dir (Path | None):
|
||||
Directory where the generated README.md should be written.
|
||||
Defaults to the parent of `docs_dir`.
|
||||
"""
|
||||
@@ -157,7 +156,7 @@ class MkDocsRenderer:
|
||||
Project model to inspect.
|
||||
|
||||
Returns:
|
||||
Optional[Module]:
|
||||
Module | None:
|
||||
The root `Module` if found, otherwise `None`.
|
||||
"""
|
||||
for module in project.get_all_modules():
|
||||
@@ -189,7 +188,7 @@ class MkDocsRenderer:
|
||||
out_dir (Path):
|
||||
Base directory for generated documentation files.
|
||||
|
||||
module_is_source (bool, optional):
|
||||
module_is_source (bool | None):
|
||||
Whether the module acts as the documentation root directory.
|
||||
"""
|
||||
|
||||
|
||||
@@ -5,6 +5,16 @@ MCP server implementation.
|
||||
|
||||
This module defines the `MCPServer` class, which serves pre-generated
|
||||
documentation bundles through the Model Context Protocol (MCP).
|
||||
|
||||
---
|
||||
|
||||
Notes:
|
||||
- The served bundle is generated offline by `MCPRenderer`.
|
||||
- Missing resources are reported as structured error dictionaries rather
|
||||
than raising exceptions.
|
||||
- The server exposes read-only resources and a single health-check tool.
|
||||
|
||||
---
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
@@ -22,6 +32,14 @@ class MCPServer:
|
||||
|
||||
The server exposes documentation resources and diagnostic tools through
|
||||
MCP endpoints backed by JSON files generated by the MCP renderer.
|
||||
|
||||
Attributes:
|
||||
mcp_root (Path):
|
||||
Directory containing the generated MCP documentation bundle.
|
||||
|
||||
app (FastMCP):
|
||||
Underlying FastMCP application instance that registers resources
|
||||
and tools.
|
||||
"""
|
||||
|
||||
def __init__(self, mcp_root: Path, name: str) -> None:
|
||||
|
||||
@@ -4,7 +4,10 @@ from typing import Any, Literal
|
||||
from mcp.server.fastmcp import FastMCP
|
||||
|
||||
class MCPServer:
|
||||
"""MCP server for serving documentation."""
|
||||
"""MCP server serving a pre-generated documentation bundle.
|
||||
|
||||
Exposes read-only documentation resources and a health-check tool.
|
||||
"""
|
||||
|
||||
mcp_root: Path
|
||||
app: FastMCP
|
||||
|
||||
Reference in New Issue
Block a user