Docusaurus plugin that generates an SDK reference from Python docstrings.
The Python counterpart to
@vantagecompute/docusaurus-plugin-godoc,
with the same option shape and the same page furniture, so a reader moving between a Go
spoke and a Python spoke on docs.vantagecompute.ai sees the same thing in the same
places.
Most Python autodoc tooling imports the module it documents. This does not, and that is the main design decision in the package:
- Importing runs the module. A docs build that imports the project inherits every import-time side effect that project has, and a project that reads configuration at import time cannot be documented at all without supplying that configuration.
- Importing needs the dependency tree installed and importable in whatever
interpreter the docs build uses. Parsing needs only the source, so the plugin runs
under any modern
python3rather than the project's own virtualenv. - An import failure is hard to attribute. A parse failure is local to one file and names that file.
The cost is that anything only knowable at runtime is invisible: attributes attached dynamically, values computed by decorators, members inherited from a base class in another module. For documenting hand-written docstrings, that trade is worth taking.
yarn add -D @vantagecompute/docusaurus-plugin-pydocPublished to npm from a tag by publish.yml, with
provenance: every release is
signed with the workflow and commit it was built from, so a consumer can check that the
tarball came from this repository rather than from someone with a token.
A git dependency on a tag still works and needs no build step, since there is nothing to
compile and no prepare script:
yarn add -D "@vantagecompute/docusaurus-plugin-pydoc@git+https://github.com/vantagecompute/docusaurus-plugin-pydoc.git#v0.1.1"Reach for that when you need an unreleased commit. Prefer npm otherwise: it is the version with provenance attached, and a git dependency over SSH needs a key on every CI runner that installs it.
The introspector runs under python3 from PATH. It uses only the standard library.
// docusaurus.config.ts
import * as path from 'path';
plugins: [
[
'@vantagecompute/docusaurus-plugin-pydoc',
{
projectRoot: path.join(__dirname, '..'),
modules: [
{module: 'myproject.app', label: 'app'},
{module: 'myproject.auth', label: 'auth'},
],
outputDir: './docs/sdk-reference',
label: 'myproject',
position: 1,
},
],
],| Option | Default | Meaning |
|---|---|---|
projectRoot |
required | Project root, relative to the Docusaurus site directory. Both a src/ layout and a package at the root are found without configuration. |
modules |
[] |
Dotted module names to document. A bare string works; {module, label} sets the display name. |
outputDir |
./docs/sdk-reference |
Where pages are written. One page per module, plus index.md. |
label |
SDK Reference |
Title of the generated index page, and so the sidebar label of the category built from outputDir. Set it per instance when a site runs more than one. |
position |
none | sidebar_position for that category. Absent, sibling categories are ordered alphabetically by directory name. |
python |
python3 |
Interpreter used to run the introspector. It never imports your project, so this need not be your virtualenv. |
strict |
true |
Fail the build on an introspection failure. See below. |
Docusaurus builds an autogenerated sidebar from the files on disk, and it reads the sidebar label and the ordering out of each page's front matter. The generator therefore emits both:
sidebar_labelis the bare module label.titlekeeps itsModule:prefix, because a browser tab and a search result want the longer form; a sidebar does not. Falling back totitleputs a column of identical prefixes down the nav and pushes the part that distinguishes one entry from the next past the edge of a narrow sidebar.sidebar_positionis the module's index in the configuredmoduleslist. That list is usually ordered to read as an introduction -- settings, then auth, then the clients that use both. Without a position, Docusaurus orders alphabetically by filename and the reader getsaggregate, attachments, authinstead.
The index page carries label and position for the same reason, and it carries them for
the category, not only for itself: Docusaurus consumes index.md as the category index
for its directory and defaults the category's label and position to that document's. A site
running one instance of this plugin per package needs that, or every one of those
categories is labelled SDK Reference and the reader gets identically named siblings with
no way to tell which package each holds.
No _category_.json is written. One generated page carrying its own front matter is a
smaller contract with the consuming site than a second generated file it has to know about.
strict: true means an introspection failure fails the build, and a run in which every
requested module documented zero classes and zero functions also fails the build. That
second check exists because the usual cause is a wrong projectRoot or a src/ layout
mismatch, not genuinely empty modules.
This is a deliberate difference from the Go sibling, which warns and continues on a per-package failure. That behaviour means a missing toolchain or a bad credential produces a green build that publishes a reference with nothing in it, and every consuming repository has to add its own verification step to notice. Failing the build is the behaviour that needs no workaround downstream.
Set strict: false if you want warn-and-continue anyway.
Per module: the module docstring as an Overview, public upper-case module constants,
public classes with their bases, decorators, annotated attributes and public methods, and
public module-level functions. Signatures are rendered from source, so annotations,
defaults, positional-only and keyword-only markers, *args and **kwargs all survive.
A name starting with an underscore is private and skipped, except for the dunders worth
documenting when they carry a docstring: __init__, __call__, and the sync and async
context-manager pairs.
Docstrings are emitted verbatim. They are prose written by the same people who wrote the code, and reflowing them here would mangle the indented blocks, tables and reStructuredText roles many of them contain.
Without a Docusaurus build:
node node_modules/@vantagecompute/docusaurus-plugin-pydoc/src/generate.js \
--project-root .. \
--output-dir ./docs/sdk-reference \
--modules myproject.app,myproject.auth \
--label myproject --position 1Or the introspector alone, which prints JSON and is the thing to reach for when the rendered output looks wrong and you want to see what was actually extracted:
python3 node_modules/@vantagecompute/docusaurus-plugin-pydoc/src/introspect.py \
--root .. --module myproject.appjust release 0.1.1 bumps, tags, pushes and creates the GitHub release. Publishing that
release to npm is publish.yml, which runs on release: published and can also be fired
by hand against a tag.
It publishes with --provenance, which needs id-token: write on the job and a release
built by Actions rather than from a laptop. That is the reason to keep publishing here
rather than running npm publish locally: a local publish is unattested, and once one
version in a package is unattested the guarantee is only as good as the weakest release.
MIT