- scripts/generate_docs.py: Generate .md files from docstrings - docs/api/*.md: Auto-generated API documentation - Pre-commit hook: Auto-generate markdown on commit - Uses griffe AST parser (no code execution) Generated files: - docs/api/index.md - docs/api/kworkclient.md - docs/api/client/*.md - docs/api/models/*.md - docs/api/errors/*.md
126 lines
3.3 KiB
Python
126 lines
3.3 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Generate Markdown API documentation using griffe.
|
|
|
|
Usage:
|
|
python scripts/generate_docs.py
|
|
|
|
Generates docs/api/*.md from source code docstrings.
|
|
"""
|
|
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
from griffe import GriffeLoader, ObjectKind
|
|
|
|
|
|
def format_signature(obj):
|
|
"""Format object signature."""
|
|
params = []
|
|
for param in obj.parameters:
|
|
if param.name in ("self", "cls"):
|
|
continue
|
|
p = param.name
|
|
if param.annotation:
|
|
p += f": {param.annotation}"
|
|
if param.default:
|
|
p += f" = {param.default}"
|
|
params.append(p)
|
|
return f"({', '.join(params)})"
|
|
|
|
|
|
def generate_class_md(cls, output_path: Path):
|
|
"""Generate markdown for a class."""
|
|
lines = [
|
|
f"# {cls.name}",
|
|
"",
|
|
cls.docstring.value if cls.docstring else "",
|
|
"",
|
|
]
|
|
|
|
# Methods
|
|
methods = [m for m in cls.members.values() if m.kind == ObjectKind.FUNCTION and not m.name.startswith("_")]
|
|
|
|
if methods:
|
|
lines.append("## Methods")
|
|
lines.append("")
|
|
|
|
for method in methods:
|
|
sig = format_signature(method)
|
|
lines.append(f"### `{method.name}{sig}`")
|
|
lines.append("")
|
|
|
|
if method.docstring:
|
|
lines.append(method.docstring.value)
|
|
lines.append("")
|
|
|
|
lines.append("")
|
|
|
|
output_path.write_text("\n".join(lines))
|
|
|
|
|
|
def generate_module_md(module, output_dir: Path):
|
|
"""Generate markdown files for a module."""
|
|
output_dir.mkdir(parents=True, exist_ok=True)
|
|
|
|
# Generate index for module
|
|
index_path = output_dir / "index.md"
|
|
lines = [
|
|
f"# {module.name}",
|
|
"",
|
|
module.docstring.value if module.docstring else "",
|
|
"",
|
|
"## Classes",
|
|
"",
|
|
]
|
|
|
|
for name, obj in module.members.items():
|
|
if obj.kind == ObjectKind.CLASS and not name.startswith("_"):
|
|
lines.append(f"- [{name}]({name.lower()}.md)")
|
|
# Generate class file
|
|
class_path = output_dir / f"{name.lower()}.md"
|
|
generate_class_md(obj, class_path)
|
|
|
|
lines.append("")
|
|
index_path.write_text("\n".join(lines))
|
|
|
|
|
|
def main():
|
|
"""Main entry point."""
|
|
root_dir = Path(__file__).parent.parent
|
|
src_dir = root_dir / "src" / "kwork_api"
|
|
docs_dir = root_dir / "docs" / "api"
|
|
|
|
if not src_dir.exists():
|
|
print(f"❌ Source directory not found: {src_dir}")
|
|
sys.exit(1)
|
|
|
|
print("📝 Loading source code with griffe...")
|
|
|
|
# Load with griffe (AST parsing, no code execution)
|
|
loader = GriffeLoader(search_paths=[src_dir.parent])
|
|
module = loader.load("kwork_api")
|
|
|
|
print("📄 Generating markdown documentation...")
|
|
|
|
# Generate for main modules
|
|
generate_module_md(module, docs_dir)
|
|
|
|
# Generate client.md
|
|
if "client" in module.members:
|
|
generate_module_md(module["client"], docs_dir / "client")
|
|
|
|
# Generate models.md
|
|
if "models" in module.members:
|
|
generate_module_md(module["models"], docs_dir / "models")
|
|
|
|
# Generate errors.md
|
|
if "errors" in module.members:
|
|
generate_module_md(module["errors"], docs_dir / "errors")
|
|
|
|
print(f"✅ Generated markdown in {docs_dir}")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|