#!/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()