Migrating from mkdocstrings
Both plugins render the same model. Griffe produces this
model. mkdocstrings-python already runs Griffe as its static analyser, and starlight-pydocs runs
Griffe the same way. Your docstrings, anchors and published objects.inv file carry over
unchanged. Deep links into your documentation, and links from other projects into it, keep
working.
Configuration
Section titled “Configuration”This handler configuration:
plugins: - mkdocstrings: handlers: python: # 1. paths -> search paths: [src] options: # 2. docstring_style -> docstringStyle docstring_style: google # 3. show_source -> sourceLink show_source: true filters: ["!^_"] # 4. inventories -> inventories inventories: - url: https://docs.python.org/3/objects.invbecomes this, with the same four options at the matching numbers:
import starlight from '@astrojs/starlight';import { defineConfig } from 'astro/config';import starlightPydocs, { pydocsSidebarGroup } from 'starlight-pydocs';
export default defineConfig({ integrations: [ starlight({ title: 'mypkg', plugins: [ starlightPydocs({ packages: [ { name: 'mypkg', // 1. paths -> search search: ['../src'], // 2. docstring_style -> docstringStyle docstringStyle: 'google', // 3. show_source -> sourceLink sourceLink: { host: 'github', repo: 'you/mypkg', ref: 'main', root: '..' }, }, ], // 4. inventories -> inventories inventories: ['python'], }), ], sidebar: [{ label: 'API reference', items: [pydocsSidebarGroup] }], }), ],});starlight-pydocs resolves search against the Astro project root, so a docs site in docs/
reaches its Python sources with ../src.
starlight-pydocs hides underscore-prefixed members by default, so filters: ["!^_"] needs no
counterpart. Use filters: { private: true } for the opposite.
Each remaining handler option and its equivalent:
mkdocs.yml (plugins.mkdocstrings.handlers.python) |
starlight-pydocs |
|---|---|
paths: [src] |
packages[].search: ['../src'] (relative to the Astro project) |
docstring_style: numpy |
packages[].docstringStyle: 'numpy' |
docstring_options: {…} |
packages[].docstringOptions: {…} |
members: [A, B] / members: false |
packages[].members.include (globs on dotted paths) |
filters: ["!^_"] |
packages[].filters plus packages[].members.exclude |
inherited_members: true |
packages[].filters.inherited (already true) |
show_source: true |
packages[].sourceLink (a forge preset or a template) |
inventories: [{url: …}] |
top-level inventories: [{ url, base? }] or 'python' |
extensions: [griffe_pydantic] |
packages[].extensions, plus extraRequirements |
::: mypkg.Report in a page |
<Autodoc name="mypkg.Report" /> in an .mdx page |
heading_level: 3 |
<Autodoc … headingLevel={3} /> |
paths + several handlers |
several entries in packages |
mkdocstrings installed in the docs environment |
nothing: uv or a pre-generated dump |
Shared features
Section titled “Shared features”- Docstrings: google, numpy and sphinx, parsed by the same Griffe parsers with the same options.
- Anchors:
#mypkg.report.Report.generate, the dotted object path, as mkdocstrings emits it. __all__: a module that declares__all__publishes that list. Without it, visibility heuristics apply, and filters and glob selection then refine the result. If an object is missing after the move, check__all__or a filter first; the cause is rarely extraction.objects.inv, in both directions: same names, roles and anchors. Other projects’intersphinx_mappingentries keep resolving, and you can consume any inventory you consume today.- Cross-references in prose:
[title][mypkg.thing]and[mypkg.thing][], resolved against your own package first, then the inventories. __init__merged into the class, inherited members with provenance, positional-only and keyword-only markers, property and classmethod badges.
Differences
Section titled “Differences”Page layout
Section titled “Page layout”starlight-pydocs generates pages; you do not place them by hand. mypkg is documented at
/api/mypkg/, and each submodule at /api/mypkg/<module>/, nested to mirror the package. base
moves the whole tree.
Pages are injected routes, not .md files written into src/content/docs. Markdown is a lossy
format for an API reference: dict[str, float] | None needs escaping everywhere before you can
link its parts, heading IDs go through a slugger instead of the dotted object path, and there is
nowhere to attach symbol-level search metadata. Nothing is written to your content directory, and
astro dev reflects a source change without rewriting files.
Members are documented on their module’s page, so mypkg.report.Report.generate is a heading on
/api/mypkg/report/. This matches an mkdocstrings site with one ::: per module, the common
layout.
There is no page-per-symbol mode. If your mkdocstrings site has one page per class, those URLs change. The anchors within the module page do not change.
starlight-pydocs identifies a package entry by its URL base. mkdocstrings has no such notion. The same import name can be documented at several bases, one per release. This is how versioned docs work.
The ::: directive becomes <Autodoc>
Section titled “The ::: directive becomes <Autodoc>”Generated pages cover whole modules, so you only need ::: when you want one object inside your
own prose. <Autodoc> does this: it renders the object exactly as the generated pages do, at
whatever heading level you choose:
import { Autodoc } from 'starlight-pydocs/components';
<Autodoc name="mypkg.Report" headingLevel={3} />When the same import name is documented at more than one base, <Autodoc package="…"> takes the
base. See Autodoc.
Extraction runs as a subprocess
Section titled “Extraction runs as a subprocess”Griffe runs as griffe dump. starlight-pydocs finds it through uv, or through an interpreter
that already has it, so you do not need to install anything into a docs environment, unlike
mkdocstrings. Griffe performs static analysis, so it never imports your package. You can also
skip the subprocess entirely and commit a
pre-generated dump. This is the route for CI
without Python.
Extension points
Section titled “Extension points”There are no mkdocs hooks and no plugin ecosystem: nothing maps to hooks.py,
mkdocs-gen-files or mkdocs-literate-nav. Templates are Astro components, not Jinja. There are
nine of them, with typed props.
What replaces them:
- Component overrides swap any of those nine for your own, everywhere they render.
- Theme tokens restyle the output without replacing anything.
- The Content Layer loader is a typed way of building custom pages from the same model.
Not supported
Section titled “Not supported”Most show_* options do not exist. Signatures, source links, badges, member summaries and
inherited members always render. Use CSS and component overrides to change them.
starlight-pydocs does not show @typing.overload signatures yet. Griffe’s Function.as_dict does
not serialise overloads, so the dump only contains the implementation. The renderer already stacks
overload signatures when a dump carries them, so a future Griffe release will fix this.