|
2 | 2 |
|
3 | 3 | from __future__ import annotations |
4 | 4 |
|
| 5 | +import shutil |
| 6 | +from pathlib import Path |
5 | 7 | from typing import TYPE_CHECKING, Any |
6 | 8 |
|
7 | 9 | import sphinx.domains.python |
8 | 10 | from docutils import nodes |
9 | 11 | from sphinx.addnodes import pending_xref, pending_xref_condition |
10 | 12 | from sphinx.domains.python import parse_reftarget |
| 13 | +from sphinx.ext.apidoc import main as sphinx_apidoc |
11 | 14 |
|
12 | 15 | if TYPE_CHECKING: |
13 | 16 | from sphinx.application import Sphinx |
|
17 | 20 | def setup(app: Sphinx) -> dict[str, Any]: |
18 | 21 | app.add_config_value("api_target_substitutions", default={}, rebuild="env") |
19 | 22 | app.add_config_value("api_target_types", default={}, rebuild="env") |
| 23 | + app.add_config_value("generate_apidoc_directory", default="api", rebuild="env") |
| 24 | + app.add_config_value("generate_apidoc_excludes", default=None, rebuild="env") |
| 25 | + app.add_config_value("generate_apidoc_package_path", default=None, rebuild="env") |
| 26 | + app.add_config_value("generate_apidoc_use_compwa_template", True, rebuild="env") |
| 27 | + app.connect("config-inited", generate_apidoc) |
20 | 28 | app.connect("config-inited", replace_type_to_xref) |
21 | 29 | return { |
22 | 30 | "parallel_read_safe": True, |
23 | 31 | "parallel_write_safe": True, |
24 | 32 | } |
25 | 33 |
|
26 | 34 |
|
| 35 | +def generate_apidoc(app: Sphinx, _: BuildEnvironment) -> None: |
| 36 | + config_key = "generate_apidoc_package_path" |
| 37 | + package_path: str | None = getattr(app.config, config_key, None) |
| 38 | + if package_path is None: |
| 39 | + return |
| 40 | + abs_package_path = Path(app.srcdir) / package_path |
| 41 | + apidoc_dir = Path(app.srcdir) / app.config.generate_apidoc_directory |
| 42 | + _run_sphinx_apidoc( |
| 43 | + abs_package_path, |
| 44 | + apidoc_dir, |
| 45 | + excludes=app.config.generate_apidoc_excludes, |
| 46 | + use_compwa_template=app.config.generate_apidoc_use_compwa_template, |
| 47 | + ) |
| 48 | + |
| 49 | + |
| 50 | +def _run_sphinx_apidoc( |
| 51 | + package_path: Path, |
| 52 | + apidoc_dir: str = "api", |
| 53 | + excludes: list[str] | None = None, |
| 54 | + use_compwa_template: bool = True, |
| 55 | +) -> None: |
| 56 | + if not package_path.exists(): |
| 57 | + msg = f"Package under {package_path} does not exist" |
| 58 | + raise FileNotFoundError(msg) |
| 59 | + shutil.rmtree(apidoc_dir, ignore_errors=True) |
| 60 | + args: list[str] = [str(package_path)] |
| 61 | + if excludes is None: |
| 62 | + excludes = [] |
| 63 | + version_file = "version.py" |
| 64 | + if (package_path / version_file).exists(): |
| 65 | + excludes.append(version_file) |
| 66 | + for file in excludes: |
| 67 | + excluded_path = package_path / file |
| 68 | + if not excluded_path.exists(): |
| 69 | + msg = f"Excluded file {excluded_path} does not exist" |
| 70 | + raise FileNotFoundError(msg) |
| 71 | + args.append(str(package_path / file)) |
| 72 | + args.extend(f"-o {apidoc_dir} --force --no-toc --separate".split()) |
| 73 | + if use_compwa_template: |
| 74 | + template_dir = Path(__file__).parent / "templates" |
| 75 | + args.extend(f"--templatedir {template_dir}".split()) |
| 76 | + sphinx_apidoc(args) |
| 77 | + |
| 78 | + |
27 | 79 | def replace_type_to_xref(app: Sphinx, _: BuildEnvironment) -> None: |
28 | 80 | target_substitutions = _get_target_substitutions(app) |
29 | 81 | ref_targets = { |
|
0 commit comments