Compare commits
3 Commits
b5379cd82e
...
f8ca6075fb
| Author | SHA1 | Date | |
|---|---|---|---|
| f8ca6075fb | |||
| 3f68e51e58 | |||
| 87a7f4eee1 |
@@ -1,270 +1,536 @@
|
|||||||
"""
|
"""
|
||||||
Renderer-agnostic Python documentation compiler that converts docstrings into
|
Renderer-agnostic Python documentation compiler that converts Python docstrings
|
||||||
structured documentation for both humans (MkDocs) and machines (MCP / AI agents).
|
into structured documentation for both humans (MkDocs) and machines (MCP / AI agents).
|
||||||
|
|
||||||
|
`doc-forge` statically analyzes source code, builds a semantic model of modules,
|
||||||
|
classes, functions, and attributes, and renders that model into documentation
|
||||||
|
outputs without executing user code.
|
||||||
|
|
||||||
`doc-forge` statically analyzes your source code, builds a semantic model of
|
|
||||||
modules and objects, and renders that model into documentation outputs without
|
|
||||||
executing your code.
|
|
||||||
---
|
---
|
||||||
|
|
||||||
# Core Philosophy
|
## Installation
|
||||||
|
|
||||||
|
Install using pip:
|
||||||
|
|
||||||
|
pip install doc-forge
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Quick start
|
||||||
|
|
||||||
|
Generate a MkDocs site from a Python package:
|
||||||
|
|
||||||
|
doc-forge build --mkdocs --module my_package
|
||||||
|
|
||||||
|
Generate MCP JSON documentation:
|
||||||
|
|
||||||
|
doc-forge build --mcp --module my_package
|
||||||
|
|
||||||
|
Serve documentation locally:
|
||||||
|
|
||||||
|
doc-forge serve --mkdocs --module my_package
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Core concepts
|
||||||
|
|
||||||
|
**Loader**
|
||||||
|
|
||||||
|
Extracts symbols, signatures, and docstrings using static analysis.
|
||||||
|
|
||||||
|
**Semantic model**
|
||||||
|
|
||||||
|
Structured, renderer-agnostic representation of the API.
|
||||||
|
|
||||||
|
**Renderer**
|
||||||
|
|
||||||
|
Converts the semantic model into output formats such as MkDocs or MCP JSON.
|
||||||
|
|
||||||
|
**Symbol**
|
||||||
|
|
||||||
|
Any documentable object:
|
||||||
|
|
||||||
|
- module
|
||||||
|
- class
|
||||||
|
- function
|
||||||
|
- method
|
||||||
|
- property
|
||||||
|
- attribute
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
`doc-forge` follows a compiler architecture:
|
`doc-forge` follows a compiler architecture:
|
||||||
|
|
||||||
1. **Front-end (Introspection)**
|
Front-end:
|
||||||
Static analysis of modules, classes, functions, signatures, and docstrings.
|
|
||||||
|
|
||||||
2. **Middle-end (Semantic Model)**
|
Static analysis of modules, classes, functions, type hints, and docstrings.
|
||||||
Renderer-neutral structured representation of your API.
|
|
||||||
|
|
||||||
3. **Back-end (Renderers)**
|
Middle-end:
|
||||||
|
|
||||||
|
Builds a semantic model describing symbols and relationships.
|
||||||
|
|
||||||
|
Back-end:
|
||||||
|
|
||||||
|
Renders documentation using interchangeable renderers.
|
||||||
|
|
||||||
|
This architecture ensures deterministic documentation generation.
|
||||||
|
|
||||||
* MkDocs → human documentation
|
|
||||||
* MCP JSON → AI-readable documentation
|
|
||||||
---
|
---
|
||||||
|
|
||||||
# Docstring Writing Standard
|
## Rendering pipeline
|
||||||
|
|
||||||
Docstrings are the single source of truth. `doc-forge` does not generate content.
|
Typical flow:
|
||||||
It compiles and renders what you write.
|
|
||||||
|
Python package
|
||||||
|
↓
|
||||||
|
Loader (static analysis)
|
||||||
|
↓
|
||||||
|
Semantic model
|
||||||
|
↓
|
||||||
|
Renderer
|
||||||
|
↓
|
||||||
|
MkDocs site or MCP JSON
|
||||||
|
|
||||||
Documentation follows the Python import hierarchy.
|
|
||||||
---
|
---
|
||||||
|
|
||||||
# Package docstring (`package/__init__.py`) — Full user guide
|
## CLI usage
|
||||||
|
|
||||||
This is the landing page. A developer must be able to install and use the
|
Build MkDocs documentation:
|
||||||
package after reading only this docstring.
|
|
||||||
|
|
||||||
## Example:
|
doc-forge build --mkdocs --module my_package
|
||||||
|
|
||||||
|
Build MCP documentation:
|
||||||
|
|
||||||
|
doc-forge build --mcp --module my_package
|
||||||
|
|
||||||
|
Serve MkDocs locally:
|
||||||
|
|
||||||
|
doc-forge serve --module my_package
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Public API
|
||||||
|
|
||||||
|
Loaders:
|
||||||
|
|
||||||
|
GriffeLoader
|
||||||
|
discover_module_paths
|
||||||
|
|
||||||
|
Renderers:
|
||||||
|
|
||||||
|
MkDocsRenderer
|
||||||
|
MCPRenderer
|
||||||
|
|
||||||
|
CLI:
|
||||||
|
|
||||||
|
main
|
||||||
|
|
||||||
|
Models:
|
||||||
|
|
||||||
|
models
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Google-Styled Doc-Forge Convention (GSDFC)
|
||||||
|
|
||||||
|
GSDFC defines how docstrings must be written so they render correctly in MkDocs and remain machine-parsable by doc-forge and AI tooling.
|
||||||
|
|
||||||
|
- Docstrings are the single source of truth.
|
||||||
|
- doc-forge compiles docstrings but does not generate documentation content.
|
||||||
|
- Documentation follows the Python import hierarchy.
|
||||||
|
- Every public symbol should have a complete and accurate docstring.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## General rules
|
||||||
|
|
||||||
|
- Use **Markdown headings** at package and module level.
|
||||||
|
- Use **Google-style structured sections** at class, function, and method level.
|
||||||
|
- Indent section contents using four spaces.
|
||||||
|
- Use type hints in signatures instead of duplicating types in prose.
|
||||||
|
- Write summaries in imperative form.
|
||||||
|
- Sections are separated by ```---```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Package docstrings
|
||||||
|
|
||||||
|
- Package docstrings act as the documentation home page.
|
||||||
|
|
||||||
|
Recommended sections:
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
## Installation
|
||||||
|
## Quick start
|
||||||
|
## Core concepts
|
||||||
|
## Architecture
|
||||||
|
## Rendering pipeline
|
||||||
|
## CLI usage
|
||||||
|
## Public API
|
||||||
|
## Examples
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
Example:
|
||||||
|
Package Doc String:
|
||||||
|
|
||||||
'''
|
'''
|
||||||
Short description of what this package provides.
|
Foo-bar processing framework.
|
||||||
|
|
||||||
|
Provides tools for defining Foo objects and executing Bar pipelines.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
# Installation
|
# Installation
|
||||||
|
|
||||||
```bash
|
pip install foo-bar
|
||||||
pip install my-package
|
|
||||||
```
|
---
|
||||||
|
|
||||||
# Quick start
|
# Quick start
|
||||||
|
|
||||||
```python
|
from foobar import Foo, BarEngine
|
||||||
from my_package.foo import Bar
|
|
||||||
|
|
||||||
bar = Bar()
|
foo = Foo("example")
|
||||||
result = bar.process("example")
|
engine = BarEngine([foo])
|
||||||
```
|
|
||||||
---
|
|
||||||
|
|
||||||
# Core concepts
|
result = engine.run()
|
||||||
|
|
||||||
## Bar
|
|
||||||
- Primary object exposed by the package.
|
|
||||||
|
|
||||||
## foo module
|
|
||||||
- Provides core functionality.
|
|
||||||
---
|
|
||||||
|
|
||||||
# Typical workflow
|
|
||||||
|
|
||||||
1. Import public objects
|
|
||||||
2. Initialize objects
|
|
||||||
3. Call methods
|
|
||||||
---
|
---
|
||||||
|
|
||||||
# Public API
|
# Public API
|
||||||
|
|
||||||
foo.Bar
|
Foo
|
||||||
foo.helper_function
|
Bar
|
||||||
|
BarEngine
|
||||||
|
|
||||||
---
|
---
|
||||||
'''
|
'''
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
# Submodule docstring (`package/foo/__init__.py`) — Subsystem guide
|
## Module docstrings
|
||||||
|
|
||||||
Explains a specific subsystem.
|
- Module docstrings describe a subsystem.
|
||||||
|
|
||||||
## Example:
|
Recommended sections:
|
||||||
|
|
||||||
'''
|
## Summary
|
||||||
Provides core functionality.
|
## Examples
|
||||||
|
## Notes
|
||||||
|
## Public API
|
||||||
|
|
||||||
# Usage
|
|
||||||
|
|
||||||
```python
|
|
||||||
from my_package.foo import Bar
|
|
||||||
|
|
||||||
bar = Bar()
|
|
||||||
bar.process("example")
|
|
||||||
```
|
|
||||||
---
|
|
||||||
'''
|
|
||||||
---
|
|
||||||
|
|
||||||
# Class docstring — Object contract
|
|
||||||
|
|
||||||
Defines responsibility and behavior.
|
|
||||||
|
|
||||||
## Example:
|
|
||||||
|
|
||||||
```python
|
|
||||||
class Bar:
|
|
||||||
'''
|
|
||||||
Performs processing on input data.
|
|
||||||
|
|
||||||
Instances may be reused across multiple calls.
|
|
||||||
---
|
|
||||||
'''
|
|
||||||
```
|
|
||||||
|
|
||||||
Include:
|
|
||||||
|
|
||||||
* Responsibility
|
|
||||||
* Lifecycle expectations
|
|
||||||
* Thread safety (if relevant)
|
|
||||||
* Performance characteristics (if relevant)
|
|
||||||
---
|
|
||||||
|
|
||||||
# Function and method docstrings — API specification
|
|
||||||
|
|
||||||
## Example:
|
|
||||||
|
|
||||||
```python
|
|
||||||
def process(
|
|
||||||
self,
|
|
||||||
value1: str,
|
|
||||||
value2: str | None = "default value",
|
|
||||||
value3: str | None = None,
|
|
||||||
) -> str:
|
|
||||||
'''
|
|
||||||
Process an input value.
|
|
||||||
---
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
----------
|
|
||||||
value1 : str
|
|
||||||
required: True
|
|
||||||
value to be processed
|
|
||||||
Example:
|
Example:
|
||||||
'string'
|
Module Doc String:
|
||||||
|
|
||||||
|
'''
|
||||||
|
Foo execution subsystem.
|
||||||
|
|
||||||
|
Provides utilities for executing Foo objects through Bar stages.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
value2 : str
|
|
||||||
required: False
|
|
||||||
default: "default value"
|
|
||||||
value to be processed
|
|
||||||
Example:
|
Example:
|
||||||
'string'
|
|
||||||
|
|
||||||
value3 : str
|
from foobar.engine import BarEngine
|
||||||
required: False
|
from foobar.foo import Foo
|
||||||
value to be processed
|
|
||||||
|
foo = Foo("example")
|
||||||
|
|
||||||
|
engine = BarEngine([foo])
|
||||||
|
engine.run()
|
||||||
|
|
||||||
|
---
|
||||||
|
'''
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Class docstrings
|
||||||
|
|
||||||
|
- Class docstrings define object responsibility, lifecycle, and attributes.
|
||||||
|
|
||||||
|
Recommended sections:
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
Notes:
|
||||||
Example:
|
Example:
|
||||||
'string'
|
Raises:
|
||||||
---
|
|
||||||
|
|
||||||
Returns
|
Example:
|
||||||
-------
|
Simple Foo:
|
||||||
processed value : str
|
|
||||||
result after processing value
|
|
||||||
---
|
|
||||||
|
|
||||||
Behavior
|
class Foo:
|
||||||
--------
|
|
||||||
- behaviour 1
|
|
||||||
- behaviour 2
|
|
||||||
---
|
|
||||||
'''
|
'''
|
||||||
```
|
Represents a unit of work.
|
||||||
---
|
|
||||||
|
|
||||||
# Attribute docstrings (optional)
|
Attributes:
|
||||||
|
name (str):
|
||||||
|
Identifier of the foo instance.
|
||||||
|
|
||||||
## Example:
|
value (int):
|
||||||
|
Numeric value associated with foo.
|
||||||
|
|
||||||
```python
|
Notes:
|
||||||
class Class
|
Guarantees:
|
||||||
'''
|
|
||||||
attribute1 : str
|
|
||||||
required: True
|
|
||||||
default: "default value"
|
|
||||||
attribute description
|
|
||||||
|
|
||||||
attribute2 : str
|
- instances are immutable after creation
|
||||||
required: False
|
|
||||||
attribute description
|
|
||||||
|
|
||||||
attribute2 : str
|
Lifecycle:
|
||||||
required: False
|
|
||||||
default: "default value"
|
- create instance
|
||||||
attribute description
|
- pass to processing engine
|
||||||
|
|
||||||
|
Example:
|
||||||
|
Create and inspect a Foo:
|
||||||
|
|
||||||
|
foo = Foo("example", value=42)
|
||||||
|
print(foo.name)
|
||||||
|
'''
|
||||||
|
|
||||||
|
Complex Bar:
|
||||||
|
|
||||||
|
class BarEngine:
|
||||||
|
'''
|
||||||
|
Executes Foo objects through Bar stages.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
foos (tuple[Foo, ...]):
|
||||||
|
Foo instances managed by the engine.
|
||||||
|
|
||||||
|
Notes:
|
||||||
|
Guarantees:
|
||||||
|
|
||||||
|
- deterministic execution order
|
||||||
|
|
||||||
|
Example:
|
||||||
|
Run engine:
|
||||||
|
|
||||||
|
foo1 = Foo("a")
|
||||||
|
foo2 = Foo("b")
|
||||||
|
|
||||||
|
engine = BarEngine([foo1, foo2])
|
||||||
|
engine.run()
|
||||||
'''
|
'''
|
||||||
|
|
||||||
attribute1: str = "default value"
|
|
||||||
attribute2: str | None = None
|
|
||||||
attribute3: str | None = "default value"
|
|
||||||
```
|
|
||||||
---
|
---
|
||||||
|
|
||||||
# Writing Rules
|
## Function and method docstrings
|
||||||
|
|
||||||
**Heading hierarchy**
|
- Function docstrings define API contracts.
|
||||||
|
|
||||||
Module docstring
|
Recommended sections:
|
||||||
|
|
||||||
- Examples
|
Args:
|
||||||
- Usage
|
Returns:
|
||||||
- Core concepts
|
Raises:
|
||||||
- Public API
|
Yields:
|
||||||
|
Notes:
|
||||||
|
Example:
|
||||||
|
|
||||||
Class docstring
|
Example:
|
||||||
|
Simple process method:
|
||||||
|
|
||||||
- Attributes
|
def process(foo: Foo, multiplier: int) -> int:
|
||||||
- Execution contract
|
'''
|
||||||
- Lifecycle
|
Process a Foo instance.
|
||||||
- Thread safety
|
|
||||||
- Notes
|
|
||||||
|
|
||||||
Method docstring
|
Args:
|
||||||
|
foo (Foo):
|
||||||
|
Foo instance to process.
|
||||||
|
|
||||||
- Parameters
|
multiplier (int):
|
||||||
- Returns
|
Value used to scale foo.
|
||||||
- Raises
|
|
||||||
- Yields
|
|
||||||
- Behavior
|
|
||||||
|
|
||||||
**Required**
|
Returns:
|
||||||
|
int:
|
||||||
|
Processed result.
|
||||||
|
|
||||||
* Use Markdown headings
|
Raises:
|
||||||
* Use Markdown line separator `---`
|
ValueError:
|
||||||
* Line separator should be followed by a blank line
|
If multiplier is negative.
|
||||||
* Provide real import examples
|
|
||||||
* Document all public APIs
|
|
||||||
* Keep descriptions precise and factual
|
|
||||||
|
|
||||||
**Avoid**
|
Notes:
|
||||||
|
Guarantees:
|
||||||
|
|
||||||
|
- foo is not modified
|
||||||
|
|
||||||
|
Example:
|
||||||
|
Process foo:
|
||||||
|
|
||||||
|
foo = Foo("example", value=10)
|
||||||
|
|
||||||
|
result = process(foo, multiplier=2)
|
||||||
|
print(result)
|
||||||
|
'''
|
||||||
|
|
||||||
|
Multiple Examples:
|
||||||
|
|
||||||
|
def combine(foo_a: Foo, foo_b: Foo) -> Foo:
|
||||||
|
'''
|
||||||
|
Combine two Foo instances.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
foo_a (Foo):
|
||||||
|
First foo.
|
||||||
|
|
||||||
|
foo_b (Foo):
|
||||||
|
Second foo.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Foo:
|
||||||
|
Combined foo.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
Basic usage:
|
||||||
|
|
||||||
|
foo1 = Foo("a")
|
||||||
|
foo2 = Foo("b")
|
||||||
|
|
||||||
|
combined = combine(foo1, foo2)
|
||||||
|
|
||||||
|
Pipeline usage:
|
||||||
|
|
||||||
|
engine = BarEngine([foo1, foo2])
|
||||||
|
engine.run()
|
||||||
|
'''
|
||||||
|
|
||||||
* Plain-text separators like `====`
|
|
||||||
* Duplicate external documentation
|
|
||||||
* Informal or conversational language
|
|
||||||
---
|
---
|
||||||
|
|
||||||
# How doc-forge uses these docstrings
|
## Property docstrings
|
||||||
|
|
||||||
## Build MkDocs site:
|
- Properties must document return values.
|
||||||
|
|
||||||
```bash
|
Example:
|
||||||
doc-forge build --mkdocs --module my_package
|
Property Doc String:
|
||||||
```
|
|
||||||
|
|
||||||
## Build MCP documentation:
|
@property
|
||||||
|
def foos(self) -> tuple[Foo, ...]:
|
||||||
|
'''
|
||||||
|
Return contained Foo instances.
|
||||||
|
|
||||||
```bash
|
Returns:
|
||||||
doc-forge build --mcp --module my_package
|
tuple[Foo, ...]:
|
||||||
```
|
Stored foo objects.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
container = FooContainer()
|
||||||
|
|
||||||
|
foos = container.foos
|
||||||
|
'''
|
||||||
|
|
||||||
Both outputs are generated directly from docstrings.
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## Attribute documentation
|
||||||
|
|
||||||
|
- Document attributes in class docstrings using Attributes:.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
Attribute Doc String:
|
||||||
|
|
||||||
|
'''
|
||||||
|
Represents a processing stage.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
id (str):
|
||||||
|
Unique identifier.
|
||||||
|
|
||||||
|
enabled (bool):
|
||||||
|
Whether the stage is active.
|
||||||
|
'''
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Notes subsection grouping
|
||||||
|
|
||||||
|
- Group related information using labeled subsections.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
Notes Example:
|
||||||
|
|
||||||
|
Notes:
|
||||||
|
**Guarantees:**
|
||||||
|
|
||||||
|
- deterministic behavior
|
||||||
|
|
||||||
|
**Lifecycle:**
|
||||||
|
|
||||||
|
- created during initialization
|
||||||
|
- reused across executions
|
||||||
|
|
||||||
|
**Thread safety:**
|
||||||
|
|
||||||
|
- safe for concurrent reads
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Example formatting
|
||||||
|
|
||||||
|
- Use indentation for examples.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
Single example:
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
foo = Foo("example")
|
||||||
|
process(foo, multiplier=2)
|
||||||
|
|
||||||
|
Multiple examples:
|
||||||
|
|
||||||
|
Example:
|
||||||
|
Create foo:
|
||||||
|
|
||||||
|
foo = Foo("example")
|
||||||
|
|
||||||
|
Run engine:
|
||||||
|
|
||||||
|
engine = BarEngine([foo])
|
||||||
|
engine.run()
|
||||||
|
|
||||||
|
Avoid fenced code blocks inside structured sections.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Separator rules
|
||||||
|
|
||||||
|
Use horizontal separators only at docstring root level to separate sections:
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Allowed locations:
|
||||||
|
|
||||||
|
- package docstrings
|
||||||
|
- module docstrings
|
||||||
|
- major documentation sections
|
||||||
|
|
||||||
|
Do not use separators inside code sections.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Parsing guarantees
|
||||||
|
|
||||||
|
GSDFC ensures doc-forge can deterministically extract:
|
||||||
|
|
||||||
|
- symbol kind (module, class, function, property, attribute)
|
||||||
|
- symbol name
|
||||||
|
- parameters
|
||||||
|
- return values
|
||||||
|
- attributes
|
||||||
|
- examples
|
||||||
|
- structured Notes subsections
|
||||||
|
|
||||||
|
This enables:
|
||||||
|
|
||||||
|
- reliable MkDocs rendering
|
||||||
|
- deterministic MCP export
|
||||||
|
- accurate AI semantic interpretation
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Notes:
|
||||||
|
- doc-forge never executes analyzed modules.
|
||||||
|
- Documentation is generated entirely through static analysis.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from .loaders import GriffeLoader, discover_module_paths
|
from .loaders import GriffeLoader, discover_module_paths
|
||||||
|
|||||||
Reference in New Issue
Block a user