from pathlib import Path from importlib import resources import click import yaml from docforge.loaders import GriffeLoader, discover_module_paths from docforge.renderers import MkDocsRenderer from docforge.nav import load_nav_spec, resolve_nav, MkDocsNavEmitter def generate_sources( module: str, docs_dir: Path, project_name: str | None = None, module_is_source: bool | None = None, ) -> None: """ Generate MkDocs Markdown sources for a Python module. This function introspects the specified module, builds the internal documentation model, and renders Markdown documentation files for use with MkDocs. Args: module: Python module import path used as the entry point for documentation generation. docs_dir: Directory where the generated Markdown files will be written. project_name: Optional override for the project name used in documentation metadata. module_is_source: If True, treat the specified module directory as the project root rather than a nested module. """ loader = GriffeLoader() discovered_paths = discover_module_paths(module) project = loader.load_project(discovered_paths, project_name) renderer = MkDocsRenderer() renderer.generate_sources( project, docs_dir, module_is_source, ) renderer.generate_readme( project, docs_dir, module_is_source, ) def generate_config( docs_dir: Path, nav_file: Path, template: Path | None, out: Path, site_name: str, ) -> None: """ Generate an ``mkdocs.yml`` configuration file. The configuration is created by combining a template configuration with a navigation structure derived from the docforge navigation specification. Args: docs_dir: Directory containing generated documentation Markdown files. nav_file: Path to the ``docforge.nav.yml`` navigation specification. template: Optional path to a custom MkDocs configuration template. If not provided, a built-in template will be used. out: Destination path where the generated ``mkdocs.yml`` file will be written. site_name: Display name for the generated documentation site. Raises: click.FileError: If the navigation specification or template file cannot be found. """ if not nav_file.exists(): raise click.FileError(str(nav_file), hint="Nav spec not found") spec = load_nav_spec(nav_file) resolved = resolve_nav(spec, docs_dir) nav_block = MkDocsNavEmitter().emit(resolved) # Load template if template is not None: if not template.exists(): raise click.FileError(str(template), hint="Template not found") data = yaml.safe_load(template.read_text(encoding="utf-8")) else: text = ( resources.files("docforge.templates") .joinpath("mkdocs.sample.yml") .read_text(encoding="utf-8") ) data = yaml.safe_load(text) data["site_name"] = site_name data["nav"] = nav_block out.write_text(yaml.safe_dump(data, sort_keys=False), encoding="utf-8") def build(mkdocs_yml: Path) -> None: """ Build the MkDocs documentation site. This function loads the MkDocs configuration and runs the MkDocs build command to generate the final static documentation site. Args: mkdocs_yml: Path to the ``mkdocs.yml`` configuration file. Raises: click.ClickException: If the configuration file does not exist. """ if not mkdocs_yml.exists(): raise click.ClickException(f"mkdocs.yml not found: {mkdocs_yml}") from mkdocs.config import load_config from mkdocs.commands.build import build as mkdocs_build mkdocs_build(load_config(str(mkdocs_yml))) def serve(mkdocs_yml: Path) -> None: """ Start an MkDocs development server with live reload. The server watches documentation files and automatically reloads the site when changes are detected. Args: mkdocs_yml: Path to the ``mkdocs.yml`` configuration file. Raises: click.ClickException: If the configuration file does not exist. """ if not mkdocs_yml.exists(): raise click.ClickException(f"mkdocs.yml not found: {mkdocs_yml}") from mkdocs.commands.serve import serve as mkdocs_serve mkdocs_serve(config_file=str(mkdocs_yml))