Sphinx API-reference tools: survey¶
Status¶
Research snapshot kept from the former ADR-038 (last refreshed 2026-10-05). It is evidence, not the decision: the decision and its status live in the Sphinx API-reference rendering entry of context/docs-stack.md. ADR-NNN mentions below name the former records, readable at commit c06222e.
Context¶
The docs subsystem (ADR-025) auto-generates
four reference pages. What they share is one property: when include_docs is
set, each is derived from the live app object at build time, and no schema
or reference file is hand-maintained or committed. The mechanism differs:
- Subprocess dump (Typer CLI, Litestar OpenAPI, FastStream AsyncAPI):
docs/conf.pyruns the generator withcheck=True, writing into the gitignoreddocs/_generated/directory, and the page includes the artifact. - In-process dump (FastAPI OpenAPI):
docs/conf.pyimports the app, callsapp.openapi(), and writes the schema intodocs/_generated/(asopenapi.yamlandopenapi.json). - Direct autodoc (pydantic config):
configuration.rstusesautopydantic_settingson the live model; there is no_generated/artifact.
Either way a broken app breaks the build. Because ci.yml's docs-doctest job
runs tox -e docs-build under the required check gate, a generator failure blocks the merge, not just a
preview.
The four generators are not at the same rendering quality, which is the crux of this decision:
| Reference | Toggle | Mechanism | Output rendered as |
|---|---|---|---|
| CLI (Typer) | include_cli + cli_framework == typer |
python -m typer … utils docs → cli.md |
Structured Markdown (MyST {include}) |
| Config (pydantic) | include_pydantic_settings |
autodoc-pydantic .. autopydantic_settings:: on the live model |
Structured autodoc directive |
| OpenAPI (web) | include_web |
Litestar … schema openapi / FastAPI app.openapi() dump → openapi.yaml |
Raw YAML (.. literalinclude:: in web-interface.rst) |
| AsyncAPI (worker) | include_worker |
faststream docs gen --yaml → asyncapi.yaml |
Raw YAML (.. literalinclude:: in worker-interface.rst) |
The CLI and config references are genuinely rendered documentation. The
OpenAPI and AsyncAPI references are, today, a syntax-highlighted spec file pasted
into a page via literalinclude — a reader gets no navigable endpoint/channel
docs, only the raw schema. That gap is what prompted surveying the Sphinx
API-doc ecosystem.
Packages surveyed¶
| Package | Scope | Health (re-checked 2026-10) | Fit |
|---|---|---|---|
sphinxcontrib-typer |
.. typer:: renders a Typer app (html/svg/text) |
Healthy, MIT | Redundant — see below |
sphinxcontrib-openapi |
.. openapi:: renders a spec to native rST via httpdomain (2.0/3.0/3.1) |
Mature (~126★), active | Best fit for a future OpenAPI upgrade |
sphinxcontrib-redoc |
Embeds ReDoc JS for OpenAPI | Older, moderate | JS embed — offline concern |
SAP/swagger-plugin-for-sphinx |
Swagger-UI embed | Active | CDN by default — wrong posture |
Unidocs1/sphinx_openapi |
Downloads remote specs for ReDoc | 0★, niche | Irrelevant — specs are generated locally |
mortbauer/asyncapi-sphinx-ext |
asyncapi_channels/asyncapi_overview directives |
3★, "early stage", stale | Not adoptable |
git-pull/gp-sphinx |
Whole-conf.py bundle (themes + autodoc collection) |
2★, personal monorepo | Out of scope — a config framework, not a renderer |
Additional candidates surveyed (second pass)¶
CLI¶
| Package | Scope | Health | Fit |
|---|---|---|---|
sphinx-argparse-cli |
sphinx_argparse_cli directive, sub-command-friendly |
Active, tox-dev org | Best candidate to fill the argparse gap (see below) |
sphinx-argparse |
argparse directive |
Maintained (sphinx-doc org; the original alex-rudakov repo is inactive) |
argparse baseline; less clean for nested CLIs |
sphinxcontrib-autoprogram |
Expands an ArgumentParser into program/option |
Mature, low activity | argparse only |
sphinxcontrib-programoutput |
Embeds any command's --help stdout |
Mature, low activity | Framework-agnostic but unstructured |
OpenAPI¶
| Package | Scope | Health | Fit |
|---|---|---|---|
sphinx-rapidoc |
RapiDoc web-component renderer | Young (2025), low adoption | JS embed — same offline concern as ReDoc/Swagger |
sphinxcontrib-swaggerui |
swaggerui directive, vendors swagger-ui assets |
Stale (last release 0.1.0, 2023; Bitbucket-hosted) | Vendored (no CDN) but low-maintenance risk |
AsyncAPI¶
No Sphinx directive exists — these are the build path for a port.
| Package | Scope | Health | Fit |
|---|---|---|---|
@asyncapi/generator + @asyncapi/html-template |
Official spec → static HTML | Active, official org | Canonical asyncapi.yaml → browsable docs |
@asyncapi/generator + @asyncapi/markdown-template |
Official spec → Markdown | Active, official org | Markdown output could feed MyST {include} |
@asyncapi/react-component |
Embeddable renderer html-template uses |
Active, official org | The swagger-ui-dist analog to wrap in a Sphinx directive |
pydantic¶
| Package | Scope | Health | Fit |
|---|---|---|---|
pydantic-kitbash |
Canonical's model → config-reference directive | New/niche, active (Canonical) | Closest live alternative to autodoc-pydantic |
sphinx-jsonschema |
Renders any JSON Schema (model_json_schema()) as tables |
Mature, maintained | Low-dep fallback route |
sphinx-pydantic |
pydantic directive via sphinx-jsonschema |
Abandoned (no release since ~2020) | Do not adopt |
Embeddable API-UI bundles¶
Portable to a Sphinx directive, including the openapipages idea.
| Package | Scope | Health |
|---|---|---|
@scalar/api-reference |
Single-file, no-build OpenAPI UI | Very active |
@stoplight/elements |
<elements-api> web component (no Sphinx wrapper yet) |
Active |
redoc standalone / swagger-ui-dist / rapidoc |
Raw embeddable bundles behind the wrappers above | Active |
openapipages |
Framework-agnostic HTML pages for the API-UI bundles above | Active |
Decision¶
Keep all four current generators unchanged for now. Do not adopt any of the surveyed extensions in this iteration. Record the OpenAPI upgrade and the AsyncAPI ecosystem gap as tracked follow-ups rather than acting on them here.
Per reference:
- CLI (Typer): keep
typer … utils docs.sphinxcontrib-typerrenders more richly but was already weighed and passed over in ADR-006, which found it works but kepttyper … utils docsto stay consistent with the worker/web generators and to avoid an extra extension. This ADR adds a further reason: it covers only Typer, socli_framework == argparsewould still need separate handling. No change. Noted gap: the CLI reference page is produced only forcli_framework == typer; anargparseCLI currently gets no generated reference.sphinx-argparse-cli(tox-dev, sub-command-aware) is the cleanest candidate to close that gap if we decide argparse CLIs warrant a reference page — a separate follow-up, not resolved here. - Config (pydantic): keep
autodoc-pydantic. It is the best-in-class option and is already the mechanism. The only live alternatives —pydantic-kitbash(new/niche) andsphinx-jsonschema(JSON-Schema tables) — offer no advantage for our settings-reference use;sphinx-pydanticis abandoned. No change. - OpenAPI (web): keep the
literalincludedump for now;sphinxcontrib-openapiis the designated future upgrade. It renders to native rST (no JS, no CDN), which suits the template's self-contained/offline docs posture, unlike the ReDoc and Swagger-UI embedders. The Swagger plugin's CDN-by-default is an outright poor fit for that posture.Unidocs1/sphinx_openapiis irrelevant (download-only). The JS-embed alternatives —sphinx-rapidocand hand-wrapping a standalone bundle (Scalar, Stoplight Elements, RapiDoc, redoc/swagger-ui-dist, or anopenapipages-style port) — are ruled out because they replace native rST with a JS app shipped into the docs.sphinxcontrib-swaggeruivendors its assets (no CDN) but is the same JS embed and adds maintenance risk (no release since 2023). Open risk to verify before adopting:sphinxcontrib-openapi's OpenAPI 3.1 support has historically lagged, and both Litestar and FastAPI emit 3.1 — a pilot must confirm the generated specs render correctly. - AsyncAPI (worker): keep the
literalincludedump; there is no adoptable renderer.asyncapi-sphinx-extis self-described "early stage", stale, and wants channels authored in docstrings rather than consuming a generatedasyncapi.yaml— the opposite of our generate-from-live-app model. This is a genuine, unfilled ecosystem gap; a purpose-built directive that renders a generatedasyncapi.yaml/.json(e.g. anopenapipages-style static renderer) would be novel and is the strongest candidate for an upstream contribution, but is out of scope here. The realistic build path for such a port is the official AsyncAPI stack —@asyncapi/react-component(the embeddable renderer),@asyncapi/generator+@asyncapi/html-template(spec → static HTML), or@asyncapi/generator+@asyncapi/markdown-template(spec → Markdown, feedable into MyST).
Rationale¶
- The current pattern's value is the live-source property. Every reference
is derived from the live app at build time with nothing committed, using only
the generator each framework already ships (plus
autodoc-pydanticfor config). Each additional extension is one more docs dependency to pin and keep compatible, so the bar for adopting one is a rendering upgrade large enough to justify it. - That bar is only met for OpenAPI/AsyncAPI, where today's output is a raw spec dump — not for CLI/config, which already render properly.
- Posture rules out the JS/CDN OpenAPI options. Native-rST rendering is the only style consistent with the template's offline/self-contained defaults.
- The AsyncAPI gap is real, not an oversight — no production-grade Sphinx renderer for an AsyncAPI spec file exists, which is why the dump stands.
Consequences¶
- No template changes in this ADR — it records evaluation and direction.
- Follow-up (not committed here): pilot
sphinxcontrib-openapiagainst a generated web project for bothweb_frameworkvalues, checking OpenAPI 3.1 rendering; if it clears, a later ADR would swap the webliteralincludefor the directive and add the extension to thedocsgroup under theinclude_webguard. - Follow-up (not committed here): prototype an AsyncAPI-spec → Sphinx renderer as a potential upstream contribution; if it matures, a later ADR would wire it into the worker reference page.
- Follow-up (not committed here): decide whether an
argparseCLI warrants a generated reference page (currently none); if so,sphinx-argparse-cliis the designated candidate. - ADR-006's CLI-generator rationale is unchanged; this ADR cites it as the
precedent for keeping the Typer generator over
sphinxcontrib-typer.