Files
soleprint/soleprint/atlas2/docgen/book/__main__.py
2026-09-14 06:13:22 -03:00

81 lines
3.3 KiB
Python

""" python3 -m docgen.book --root ../station -o out/book/station
One book: larder measure, the steps, the web output, book measure. The step
artifacts land in `steps/` and are ordinary files — nothing here needs this
command to have been the thing that produced them.
"""
import argparse
import sys
from pathlib import Path
KINDS = ("python", "code", "db", "openapi", "usage")
def main(argv=None) -> int:
p = argparse.ArgumentParser(
prog="python3 -m docgen.book",
description="Run one docgen operation, measured at both ends.",
)
src = p.add_mutually_exclusive_group(required=True)
src.add_argument("--root", type=Path, help="A source tree (Python, or --lang code).")
src.add_argument("--schema", type=Path, help="A graphgen-compatible schema.json.")
src.add_argument("--openapi", type=Path, help="An OpenAPI document.")
src.add_argument("--har", type=Path, help="A HAR recording.")
p.add_argument("--lang", choices=("python", "code"), default="python",
help="With --root: the stdlib ast reader, or tree-sitter. Default python.")
p.add_argument("--output", "-o", type=Path, required=True,
help="The book directory. Created if absent.")
p.add_argument("--slug", help="What to call it. Defaults to the source's name.")
p.add_argument("--style", default="lucid")
p.add_argument("--theme", default=None, help="dark (default) or lucid.")
p.add_argument("--overlay", type=Path,
help="A hand-written overlay, re-applied on every build.")
p.add_argument("--exclude", action="append", default=[],
help="Directory name to skip. Repeatable.")
p.add_argument("--quiet", "-q", action="store_true")
args = p.parse_args(argv)
if args.root is not None:
kind, source = args.lang, args.root
elif args.schema is not None:
kind, source = "db", args.schema
elif args.openapi is not None:
kind, source = "openapi", args.openapi
else:
kind, source = "usage", args.har
if not Path(source).exists():
print(f"Error: {source} does not exist", file=sys.stderr)
return 1
overlay = None
if args.overlay:
if not args.overlay.exists():
# Absent is fine and is the documented default; named-but-missing is
# a typo, and quietly building without it would hide the typo.
print(f"Error: overlay {args.overlay} does not exist", file=sys.stderr)
return 1
from ..notebook import spec as spec_mod
overlay = spec_mod.load(args.overlay)
from .build import run
try:
book = run(kind, source, args.output, slug=args.slug, style=args.style,
theme=args.theme, exclude=tuple(args.exclude), overlay=overlay,
quiet=args.quiet)
except Exception as e: # noqa: BLE001 - the CLI reports, it does not traceback
print(f"Error: {type(e).__name__}: {e}", file=sys.stderr)
return 1
# Exit 1 when the two ends do not reconcile. The book is still written —
# the evidence is the point — but a build that lost input should fail a
# pipeline rather than pass quietly.
lost = [r for r in book.compare() if not r.get("ok")]
return 1 if lost else 0
if __name__ == "__main__":
sys.exit(main())