This is the full developer documentation for Starlight Pydocs # Starlight Pydocs > Python API reference documentation for Astro and Starlight. At build time, Griffe reads your package by static analysis. ## Three lines of configuration [Section titled “Three lines of configuration”](#three-lines-of-configuration) astro.config.mjs ```js starlightPydocs({ packages: [{ name: 'mypkg', search: ['../src'] }], }); ``` This generates one page per module at `/api/mypkg/`, a sidebar tree that mirrors the package layout, and `symbols.json`, `objects.inv` and `llms.txt`. ## What you get [Section titled “What you get”](#what-you-get) Generated pages This injects one page per module into the site’s routes. Each page gets a sidebar tree and prev/next links that mirror the package layout. Autodoc component `` renders a single class or function into a hand-written MDX page. Symbol search Search the API surface by object path. This works alongside the site’s existing prose search. Docstring sections Google, numpy or sphinx style: parameters, returns, raises, examples, admonitions and deprecations. Your site’s own Markdown pipeline renders them. No Python at build time Point the plugin at a dump your CI published and the site builds without an interpreter. Linked signatures Names in an annotation link to their definition. The link points to your own pages, or, through a Sphinx inventory, to another project’s documentation. Inherited members Merged from resolvable base classes and labelled with the class they came from. Inventory and llms.txt `objects.inv` and `llms.txt` per package, so other documentation sites and language models can consume yours. ## Coming from mkdocstrings [Section titled “Coming from mkdocstrings”](#coming-from-mkdocstrings) `starlight-pydocs` is the Starlight counterpart of [mkdocstrings-python](https://mkdocstrings.github.io/python/). It keeps mkdocstrings’ conventions where they make sense. Heading anchors are dotted object paths. `__all__` selects the documented surface. `::: name` becomes ``. [Read the migration guide](/starlight-pydocs/guides/migration/) ## Reading this site with a language model [Section titled “Reading this site with a language model”](#reading-this-site-with-a-language-model) These docs are published as plain Markdown alongside the HTML. [`/llms.txt`](/starlight-pydocs/llms.txt) indexes the site. [`/llms-full.txt`](/starlight-pydocs/llms-full.txt) contains every guide in one file. Each documented package publishes its own API surface the same way, for example [`/api/demopkg/llms.txt`](/starlight-pydocs/api/demopkg/llms.txt). Point an agent at these files instead of asking it to crawl the rendered pages. ## Where to go next [Section titled “Where to go next”](#where-to-go-next) [Configuration](/starlight-pydocs/guides/configuration/)Every option, with its type and default. [Vanilla Astro](/starlight-pydocs/guides/vanilla-astro/)The same pages, without Starlight. [Autodoc](/starlight-pydocs/guides/autodoc/)Drop one object into a hand-written page. [The demo reference](/starlight-pydocs/api/demopkg/)demopkg (google style, pydantic, deprecations), numpkg (numpy style) and sphpkg (sphinx style, from a pre-generated dump). # Changelog > Release notes for every published version of starlight-pydocs. All notable changes to `starlight-pydocs` are recorded here. New work is added under **Unreleased** and rolled into a dated version section when a release is cut. ## v0.2.1 (2026-08-14) [Section titled “v0.2.1 (2026-08-14)”](#v021-2026-08-14) Metadata-only release: no code changes. * 🛠️ Reworked the npm keywords. `astro-integration` is gone: the default export is a Starlight plugin, so `astro add starlight-pydocs` would have wired up the wrong thing. * 🛠️ Published with npm provenance. ## v0.2.0 (2026-08-14) [Section titled “v0.2.0 (2026-08-14)”](#v020-2026-08-14) * ✨ Syntax-highlighted signatures, in your `markdown.shikiConfig` themes. Cross-references stay links. * ✨ Long attribute values laid out across lines, folding behind a “Show more” toggle. * ✨ Annotation links to another site open in a new tab; links to your own objects show the target’s summary on hover. * ✨ Version labels linking to the matching GitHub, GitLab or Bitbucket release. * ✨ Every generated page served as Markdown at `.md` too, for “Copy Markdown” buttons and agents. `pageMarkdown: false` turns it off. * ✨ `listPydocsPages()` from `starlight-pydocs/pages`, for share cards or an index of the generated pages. * 🛠️ Generated pages carry their own description, from the module’s docstring. * 🛠️ “Added in” is plain text beside the source link, not a badge. * 🐛 Fixed spacing throughout the generated pages on Starlight sites, and object names that looked like inline code. ## v0.1.0 (2026-08-14) [Section titled “v0.1.0 (2026-08-14)”](#v010-2026-08-14) First release. Python API reference documentation for Astro and Starlight sites, extracted with [Griffe](https://mkdocstrings.github.io/griffe/). * One reference page per Python module, on injected routes, with a sidebar tree, prev/next links, a table of contents and anchors that match mkdocstrings. * No Python needed on the docs host: griffe runs through `uvx`, or a pre-generated dump is read from a file or a URL. * Every docstring section griffe parses, in google, numpy, sphinx or auto style, rendered through the host site’s own markdown processor. * Type annotations linked to this site’s pages, to Python’s own documentation, or to any configured Sphinx inventory. `[title][dotted.path]` cross-references in prose resolve the same way. * Member selection that follows mkdocstrings: `__all__`, then griffe’s visibility flags, refined by include/exclude globs and filters. Inherited members are merged and badged with their origin. * Symbol search, plus `symbols.json`, `objects.inv` and `llms.txt` published per package. * `` to document a single object inside a hand-written page, and a Content Layer loader for the whole surface. * Source links to GitHub, GitLab, Bitbucket or a URL template. * “Added in” badges, generated by extracting a list of git refs and comparing them with the current source. * Usable as a Starlight plugin or as a plain Astro integration, with component overrides, `--pyd-*` theme tokens and translations for thirteen languages. # Examples > What each demo package on this site shows, and which one matches your setup. Every Python package documented on this site is a test fixture from the plugin’s own repository, in [`fixtures/`](https://github.com/ewels/starlight-pydocs/tree/main/fixtures). Each fixture tests a different path through the plugin. One [`astro.config.ts`](https://github.com/ewels/starlight-pydocs/blob/main/docs/astro.config.ts) file sets up all four. Start with the one that matches your setup. [demopkg](/starlight-pydocs/api/demopkg/)Google-style docstrings, extracted from source at build time. Inheritance, re-exports, pydantic models and deprecations. [numpkg](/starlight-pydocs/api/numpkg/)Numpy-style docstrings, parsed alongside a google-style package in the same build. [sphpkg](/starlight-pydocs/api/sphpkg/)Sphinx-style docstrings from a checked-in dump. No Python runs at build time. [demopkg 1.x](/starlight-pydocs/1x/api/demopkg/)The same package documented a second time at its own base, read from a dump rather than extracted. ## demopkg [Section titled “demopkg”](#demopkg) demopkg uses Google-style docstrings. The build extracts them from source with `uvx --from griffe`. It is the fixture that covers the most: docstring sections, inheritance with provenance, `__all__`, re-exports, pydantic models labelled through the `griffe_pydantic` extension, deprecations, and source links back to GitHub. Its pages are in the **API reference** section of the sidebar. ## numpkg [Section titled “numpkg”](#numpkg) numpkg also uses `uvx --from griffe` to extract docstrings from source, the same way as `demopkg`, but its docstrings are Numpy-style. [Docstring style](/starlight-pydocs/guides/docstring-styles/) is a per-package setting. Griffe parses each package with a different parser, but both sit in the same **API reference** section of one build. ## sphpkg [Section titled “sphpkg”](#sphpkg) sphpkg uses Sphinx-style docstrings. The plugin renders them from a checked-in `griffe dump` instead of extracting them, so no Python runs at build time. See [Pre-generated dumps](/starlight-pydocs/guides/pregenerated-dumps/): the dump comes from the Python project’s CI, and the docs site only reads it. Its sidebar section is **Sphinx demo**. ## demopkg 1.x [Section titled “demopkg 1.x”](#demopkg-1x) demopkg 1.x has the same import name as `demopkg`. The docs site documents it a second time, at its own base (`/1x/api/demopkg/`), under the **v1.x** sidebar section, and reads it from a dump instead of extracting it from source. This is the shape that [versioned docs](/starlight-pydocs/guides/versioned-docs/) take: one `packages` entry per release, each with its own base, sidebar section, and endpoints. Both entries here read the same fixture, so the two trees document identical code. This example shows the wiring between a package and its base, not a real difference between versions. On a real site, you would point each entry at the dump that its own release published. ## Endpoints [Section titled “Endpoints”](#endpoints) Each package serves machine-readable versions of its pages next to the pages themselves. These are the live ones for `demopkg`: * [`symbols.json`](/starlight-pydocs/api/demopkg/symbols.json): the index behind the [symbol search](/starlight-pydocs/guides/search/) box. * [`objects.inv`](/starlight-pydocs/api/demopkg/objects.inv): the Sphinx inventory other projects link against ([Cross-references](/starlight-pydocs/guides/cross-references/)). * [`llms.txt`](/starlight-pydocs/api/demopkg/llms.txt): the whole API as plain Markdown ([llms.txt](/starlight-pydocs/guides/llms-txt/)). ## Vanilla Astro [Section titled “Vanilla Astro”](#vanilla-astro) `examples/vanilla` in the repository documents `demopkg` again, in a plain Astro site with no Starlight. It uses the built-in layout and its own markdown pipeline. This example is not deployed here. See [the guide](/starlight-pydocs/guides/vanilla-astro/) or [the source](https://github.com/ewels/starlight-pydocs/tree/main/examples/vanilla). # Autodoc > Render one Python class, function or module inside a page you wrote by hand. Generated pages document a whole package. This is the default way to show your API reference. `` covers a different case: a page you write by hand that needs one class or function in the middle of the text, rendered exactly as the generated pages render it. It is the component form of mkdocstrings’ `::: demopkg.generate_report` directive. [Migrating from mkdocstrings](/starlight-pydocs/guides/migration/) maps the rest of the directive’s options onto props. ## Put a block in a page [Section titled “Put a block in a page”](#put-a-block-in-a-page) The page must be `.mdx`. A `.md` page cannot import a component. This is usually the first problem you hit. Starlight includes the MDX integration, so renaming the file is enough to fix it. A plain Astro project needs to install `@astrojs/mdx`. Alternatively, write the page as `.astro` and put the import in the frontmatter fence. src/content/docs/reporting.mdx ```mdx --- title: Reporting --- {/* Import the component once, below the frontmatter. */} import { Autodoc } from 'starlight-pydocs/components'; The helper that writes one for you: {/* Name the object where you want it rendered. */} Pass `fmt` when you want something other than Markdown. ``` `name` takes either the path an object is documented at, or the canonical path Griffe found the definition at. Both paths reach the same object. `package` says which configured entry to read it from. That same line sits on this page. Everything in the box below is what it renders. The heading, the signature, the parameter table and the return value all come from the function’s own docstring. None of it comes from anything written here. Rendered output ## `generate_report`function[#](#demopkg.generate_report) Re-exported from [`demopkg.report`](/starlight-pydocs/api/demopkg/report/)[View source](https://github.com/ewels/starlight-pydocs/blob/main/fixtures/demopkg/src/demopkg/report.py#L163-L187 "fixtures/demopkg/src/demopkg/report.py:163-187") ``` def generate_report(source, /, name: str, *, fmt: str = 'md') -> Report ``` Build a report from a source object. Exercises a positional-only parameter (`source`, before the `/`) and a keyword-only one (`fmt`, after the `*`). Returns a [Report](/starlight-pydocs/api/demopkg/report/#demopkg.report.Report) whose [generate](/starlight-pydocs/api/demopkg/report/#demopkg.report.Report.generate) method writes a [pathlib.Path](https://docs.python.org/3/library/pathlib.html#pathlib.Path). A reference nothing resolves, such as \[nosuchpkg.Thing]\[], is left exactly as it was written. Parameters | Name | Type | Default | Description | | -------- | ----- | -------- | ---------------------------------------------------------- | | `source` | | required | Anything with a `read()` method; deliberately unannotated. | | `name` | `str` | required | Name for the resulting report. | | `fmt` | `str` | `'md'` | Output format for the report. | Returns * `Report` A populated report. Raises * `ReportError` If `source` cannot be read. ## Props [Section titled “Props”](#props) | Prop | Type | Default | What it does | | -------------- | ----------------- | -------------------- | ----------------------------------------------- | | `name` | `string` | required | Dotted path of the object to document | | `package` | `string` | inferred from `name` | Which configured entry to document it from | | `headingLevel` | `number` | `2` | Heading depth for the object; members go deeper | | `labels` | `StringOverrides` | site translations | UI label overrides for this block alone | A class or function brings its members with it. Naming a module renders the whole module, the same way a generated module page does, including the member summary and submodule list. A module has no heading of its own here, so for a module, `headingLevel` sets the depth of its members. `labels` lets a page in a plain Astro site translate a block. It also lets a Starlight site change one string in one place. See [Internationalisation](/starlight-pydocs/guides/i18n/#without-starlight). ## Choosing the package [Section titled “Choosing the package”](#choosing-the-package) The dotted name usually says which package an object belongs to, so `package` is optional. It becomes required when one package is documented at more than one base, because then the import name identifies two entries. Give it the base of the one you mean: ```mdx ``` This site documents `demopkg` twice (the current source and the archived 1.x dump), which is why every block on this page names its base. Leave `package` off and the build stops with the candidates listed: ```text starlight-pydocs: is ambiguous: 'demopkg' is documented at 2 bases. Set the package prop to one of these bases: 'api/demopkg', '1x/api/demopkg'. ``` `package` also accepts a plain import name where that name is unambiguous. A base always wins over a name when both would match. A name that resolves to a package, but not to an object in it, fails the same way. This is usually a typo, or a `members`/`filters` setting that excludes the object from that entry: ```text starlight-pydocs: is not documented at '/api/demopkg'. Check the spelling, or the members/filters options that may be excluding it. ``` ## Heading levels [Section titled “Heading levels”](#heading-levels) `headingLevel` sets the depth of the object’s own heading. Its members go one level deeper. Use it to place a block under a section of your own, without breaking the document outline: src/content/docs/reporting.mdx ```mdx ## Building a report The helper that wires one together for you: ``` Below is the same function as the block further up this page, rendered again at `headingLevel={5}`. Its heading is an `h5` instead of an `h2`, and its parameters sit at `h6`. Everything else about the two blocks is identical. Rendered output ##### `generate_report`function[#](#demopkg.generate_report) Re-exported from [`demopkg.report`](/starlight-pydocs/api/demopkg/report/)[View source](https://github.com/ewels/starlight-pydocs/blob/main/fixtures/demopkg/src/demopkg/report.py#L163-L187 "fixtures/demopkg/src/demopkg/report.py:163-187") ``` def generate_report(source, /, name: str, *, fmt: str = 'md') -> Report ``` Build a report from a source object. Exercises a positional-only parameter (`source`, before the `/`) and a keyword-only one (`fmt`, after the `*`). Returns a [Report](/starlight-pydocs/api/demopkg/report/#demopkg.report.Report) whose [generate](/starlight-pydocs/api/demopkg/report/#demopkg.report.Report.generate) method writes a [pathlib.Path](https://docs.python.org/3/library/pathlib.html#pathlib.Path). A reference nothing resolves, such as \[nosuchpkg.Thing]\[], is left exactly as it was written. Parameters | Name | Type | Default | Description | | -------- | ----- | -------- | ---------------------------------------------------------- | | `source` | | required | Anything with a `read()` method; deliberately unannotated. | | `name` | `str` | required | Name for the resulting report. | | `fmt` | `str` | `'md'` | Output format for the report. | Returns * `Report` A populated report. Raises * `ReportError` If `source` cannot be read. ## Anchors and the table of contents [Section titled “Anchors and the table of contents”](#anchors-and-the-table-of-contents) Every heading a block emits has the dotted object path as its `id`. This is the same anchor scheme the generated pages and mkdocstrings use, so `#demopkg.generate_report` above is a working permalink. Those headings do not reach Starlight’s **On this page** panel. The panel is built from the Markdown headings of the page itself, and a component’s output is not one of them. The panel beside this guide proves it: it lists `Heading levels`, not `demopkg.generate_report`. Add your own `##` heading above a block when you want a table of contents entry for it. The generated pages stay an object’s canonical home either way. [Cross-references](/starlight-pydocs/guides/cross-references/) and the published `objects.inv` are built from the page plan. So `[Report][demopkg.report.Report]` in a docstring resolves to the generated `demopkg.report` page, never to a hand-placed copy of the same object. ## Symbol search [Section titled “Symbol search”](#symbol-search) `` searches one package’s API surface by name. It runs client-side, using the `symbols.json` index the plugin publishes. It is already on the generated package pages; this is the same component placed by hand. It takes the same `package` prop, a base or an unambiguous import name, and defaults to the first configured entry. [Search](/starlight-pydocs/guides/search/) covers the index, the ranking and the rest of its props. The box below searches `demopkg`. Type `generate`: Search symbolsSearch classes, functions, attributes… Type part of a name or a dotted path [Component overrides](/starlight-pydocs/guides/component-overrides/)Replace a rendering component and every Autodoc block picks it up. [Search](/starlight-pydocs/guides/search/)The symbols.json index, the matching rules and the search element in full. [Vanilla Astro](/starlight-pydocs/guides/vanilla-astro/)The same components in .astro pages, with no Starlight involved. # Component overrides > Replace any of the nine Astro components that render the API pages with your own version, everywhere it is used. The generated pages are Astro components. Each one renders a normalised model of your package, and you can replace nine of them with your own components. Do this when you need different markup: an extra badge in every signature, or a warning above every abstract class. If you only want different colours or spacing, use [Theming](/starlight-pydocs/guides/theming/) instead. Theming needs CSS and no components. An override applies everywhere its component is used. This includes the generated pages, the [``](/starlight-pydocs/guides/autodoc/) blocks you embed in hand-written pages, and other overrides. ## Registering an override [Section titled “Registering an override”](#registering-an-override) Name the component and point at a file. A value starting with `.`, or an absolute path, resolves against the Astro project root. Any other value is passed through as a package specifier. This lets an override ship from your own npm package. astro.config.mjs ```js starlightPydocs({ packages: [{ name: 'mypkg', search: ['../src'] }], components: { SourceLink: './src/components/pydocs/SourceLink.astro', Signature: '@internal/docs-theme/PySignature.astro', }, }); ``` The components themselves can live anywhere in your project. This page keeps them together: * astro.config.mjs * src/ * components/ * pydocs/ * **ClassDoc.astro** wraps the built-in renderer * **SourceLink.astro** replaces it outright * package.json An unknown component name causes a configuration error, and the error message lists the nine valid names. This way, a typo fails the build instead of being ignored. ## Wrapping the built-in component [Section titled “Wrapping the built-in component”](#wrapping-the-built-in-component) The built-in components are exported from `starlight-pydocs/components`, so an override can add to one rather than reimplement it. Importing the original there is safe. It bypasses the override mechanism, so there is no loop. A useful override often needs only three things: an import, the markup you add, and one line that hands the rest back. Here, the override adds a warning above every abstract class: src/components/pydocs/ClassDoc.astro ```astro --- import { ClassDoc } from 'starlight-pydocs/components'; import type { RenderScope } from 'starlight-pydocs/render'; // `DocObject` is the shape of every documented object, reachable through the scope type. type DocObject = RenderScope['model']['root']; interface Props { doc: DocObject; scope: RenderScope; labels?: Record | undefined; } const { doc, scope, labels } = Astro.props; const isAbstract = doc.labels.includes('abstract'); --- { isAbstract ? ( ) : null } ``` The last line hands everything else back to the built-in renderer, including `labels`. This keeps the class bodies rendering exactly as they did before. Overrides are compiled by your own project, so `astro check` covers them. Declare the props exactly as listed below. Then the check is meaningful: an override with a missing prop fails at build time instead of rendering a blank block. ## The nine components [Section titled “The nine components”](#the-nine-components) | Name | Renders | Props | | ------------------- | ----------------------------------------------------------------------- | ------------------------------------------------ | | `ModuleDoc` | A whole module page: docstring, member summary, submodule list, members | `doc`, `scope`, `headingLevel?`, `labels?` | | `ClassDoc` | A class body: signature, bases, docstring sections | `doc`, `scope`, `labels?` | | `FunctionDoc` | A function or method body: signature and docstring sections | `doc`, `scope`, `labels?` | | `AttributeDoc` | An attribute or property body | `doc`, `scope`, `labels?` | | `Signature` | The signature block, including `@overload` variants | `doc`, `scope`, `labels?`, `class?` | | `DocstringSections` | Every parsed docstring section, in docstring order | `doc`, `scope`, `labels?` | | `MemberSummary` | The table of members at the top of a page or class | `doc`, `scope`, `labels?` | | `SourceLink` | The `View source` link | `doc`, `labels?` | | `Heading` | An anchor heading with its permalink | `level`, `id`, `class?`, `labels?`, default slot | Everything else is fixed. The object heading, badges, provenance line and deprecation notice come from `ObjectDoc`, which also handles the recursion into members. `` and `` are entry points, not pieces you can replace. Style those with CSS instead. ## The props [Section titled “The props”](#the-props) `doc` is one documented object and `scope` is everything needed to render it. The `doc` fields you are most likely to use: | `doc` field | Meaning | | --------------------------------- | ----------------------------------------------------------------------- | | `path`, `canonicalPath`, `name` | Documented path (also the heading id), definition path, short name | | `kind` | `module`, `class`, `function`, `attribute` or `alias` | | `labels` | Griffe’s labels: `property`, `classmethod`, `pydantic-model`, … | | `summary`, `docstring` | First docstring line as plain text; the raw text plus parsed sections | | `members`, `groups` | Filtered members, and the same members bucketed for rendering | | `bases`, `mro`, `unresolvedBases` | Declared bases with resolution, the linearisation, what did not resolve | | `inheritedFrom`, `reexportedFrom` | Provenance, when the object is not defined where it is documented | | `deprecated` | `{ version, description }` when anything marks it deprecated | | `source` | `{ file, startLine, endLine, href }` | | `pageSlug` | The page the object is rendered on | | `scope` field | Meaning | | ------------- | ------------------------------------------------------------------------------ | | `context` | Site configuration: `siteBase`, `trailingSlash`, `packages`, the feature flags | | `pkg` | The package being rendered | | `model` | The whole normalised package: pages, `objectsByPath`, `symbolsByPath`, scopes | | `resolver` | Annotation name resolver, wired to the site’s inventories | | `rendered` | Pre-rendered docstring HTML, keyed by canonical path and section index | `labels` is a `StringOverrides` object. You can pass it to any component to override individual strings. Pass it down so overrides keep working through your component. `starlight-pydocs/render` exports the helpers the built-in components use, so an override rarely has to compute anything itself: `hrefForPath`, `hrefForTarget`, `pageHref`, `packageAssetHref`, `objectBadges`, `splitInherited`, `admonitionKind`, `admonitionTitle` and `createRenderScope`. ## Replacing a component outright [Section titled “Replacing a component outright”](#replacing-a-component-outright) When the built-in markup is not the starting point you want, declare the same props and render whatever you like. For example, make the `View source` link carry a forge icon and open in a new tab: src/components/pydocs/SourceLink.astro ```astro --- import type { RenderScope } from 'starlight-pydocs/render'; // `DocObject` is the shape of every documented object, reachable through the scope type. type DocObject = RenderScope['model']['root']; interface Props { doc: DocObject; labels?: Record | undefined; } const { doc } = Astro.props; const source = doc.source; --- { source?.href === undefined ? null : ( {' '} Source ) } ``` Register it under its component name: astro.config.mjs ```js starlightPydocs({ packages: [{ name: 'mypkg', search: ['../src'] }], components: { SourceLink: './src/components/pydocs/SourceLink.astro' }, }); ``` Every object on every generated page, and every `` block, now uses it. [Theming](/starlight-pydocs/guides/theming/)The --pyd-\* custom properties and the class hooks, when CSS is all you need. [Internationalisation](/starlight-pydocs/guides/i18n/)Where the labels your override passes down actually come from. # Configuration > Every option the starlight-pydocs plugin and the Astro integration accept, with its type and default. Options work at two levels. Site-wide options go directly to the plugin. Per-package options go in the `packages` array, with one entry per Python package. The Starlight plugin (`starlight-pydocs`) and the Astro integration (`starlight-pydocs/astro`) accept the same options. The integration adds one extra option, `layout`. **A package entry is identified by its `base`, not by its import name.** The `base` is the URL path where the package’s pages are generated. Bases must be unique and must not contain one another. Names carry no such rule: the same import name can appear in `packages` several times, each with its own base. This is how you document several releases of one package in a single build, and why `` and `` take a base when a name is documented more than once. Astro validates every option when it loads the config. If a value is wrong, the error names it directly: ```plaintext packages[1].base: must not be empty or the site root ``` ## Where the options go [Section titled “Where the options go”](#where-the-options-go) astro.config.mjs ```js import starlight from '@astrojs/starlight'; import { defineConfig } from 'astro/config'; import starlightPydocs, { pydocsSidebarGroup } from 'starlight-pydocs'; export default defineConfig({ integrations: [ starlight({ title: 'My project', plugins: [ starlightPydocs({ // Per-package options, one entry per package. packages: [ { name: 'mypkg', base: 'api/mypkg', search: ['../src'], docstringStyle: 'google', docstringOptions: { warn_unknown_params: false }, extensions: ['griffe_pydantic'], extraRequirements: ['griffe-pydantic'], members: { exclude: ['mypkg._*'] }, sourceLink: { host: 'github', repo: 'you/mypkg', ref: 'main', root: '..' }, sidebar: { label: 'mypkg API', collapsed: true }, }, ], // Top-level options, shared by every package. inventories: ['python'], cacheDir: '.cache', }), ], sidebar: [{ label: 'API reference', items: [pydocsSidebarGroup] }], }), ], }); ``` Extraction runs [Griffe](https://mkdocstrings.github.io/griffe/) as a separate process. Griffe reads a package’s source without importing it. The options `runner`, `docstringStyle`, `docstringOptions`, `extensions` and `source` control this step. The other options control what gets documented and how the pages look. ## Top-level options [Section titled “Top-level options”](#top-level-options) | Option | Type | Default | What it does | | ------------------ | -------------------------------------- | --------------------- | --------------------------------------------------------------------------------------------------- | | `packages` | `PydocsPackageInput[]` | required | The packages to document. At least one. | | `runner` | `{ command?, python? }` | `{}` | Overrides for how griffe is invoked. See [How extraction is resolved](#how-extraction-is-resolved). | | `inventories` | `(PydocsInventoryInput \| 'python')[]` | `[]` | Sphinx inventories used to link type annotations outwards. See [Inventories](#inventories). | | `publishInventory` | `boolean` | `true` | Serve `objects.inv` for each package. | | `symbolSearch` | `boolean` | `true` | Serve `symbols.json` and render the search box. | | `llmsTxt` | `boolean` | `true` | Serve `llms.txt` for each package. | | `pageMarkdown` | `boolean` | `true` | Serve each generated page as Markdown at `.md` and `.md.txt`. | | `components` | `Record` | `{}` | Component overrides. See [Component overrides](/starlight-pydocs/guides/component-overrides/). | | `injectStyles` | `boolean` | `true` | Append the package stylesheet to Starlight’s `customCss`. | | `cacheDir` | `string` | `node_modules/.astro` | Directory for cached dumps, inventories and rendered prose. | | `layout` | `string` | built-in layout | Integration only: the component that wraps generated pages. | A plain Astro project has no Starlight `customCss` to append to, so the integration ignores `injectStyles`. Import `starlight-pydocs/styles` from your own layout instead. `layout` names that layout, as an import specifier or a path relative to the project root. The component receives `title`, `headings` and `description` props, and renders the page body in its default slot (see [Vanilla Astro](/starlight-pydocs/guides/vanilla-astro/)). Turning `symbolSearch` off removes the `symbols.json` endpoint and every rendered search box, including a hand-placed ``. Rather than show a search box that cannot search, it renders nothing. ### Inventories [Section titled “Inventories”](#inventories) A Sphinx inventory is the `objects.inv` file that a documentation site publishes. It maps each object to the URL that documents it. Each entry of `inventories` is either the `'python'` preset (CPython’s inventory, based at `https://docs.python.org/3/`) or an object: | Field | Type | Default | What it does | | ------- | ------------------------------------- | -------------------------------- | ---------------------------------------------------------------------------------- | | `url` | `string` | none | URL of an `objects.inv`. Mutually exclusive with `file`. | | `file` | `string` | none | Path to a local `objects.inv`, relative to the project root. | | `base` | `string` | the URL without its last segment | Base URL the inventory’s relative URIs resolve against. Required alongside `file`. | | `cache` | `'revalidate' \| 'force' \| 'bypass'` | `'revalidate'` | Download cache policy. See [Cache behaviour](#cache-behaviour). | astro.config.mjs ```js inventories: [ 'python', { url: 'https://pandas.pydata.org/docs/objects.inv' }, { file: './vendor/internal.inv', base: 'https://docs.internal.example/' }, ]; ``` [Cross-references](/starlight-pydocs/guides/cross-references/) covers how annotations and docstring references resolve against these. ## Per-package options [Section titled “Per-package options”](#per-package-options) Each entry of `packages` describes one Python package. | Option | Type | Default | What it does | | ------------------- | ------------------------------------------------------------ | ----------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- | | `name` | `string` | required | Python import name, e.g. `mypkg`. Must be a valid dotted import name. | | `base` | `string` | `api/` | URL base for the generated pages, relative to the site root. Identifies the entry. | | `label` | `string` | `name` | Display name for this entry: the sidebar group, the `llms.txt` heading. | | `search` | `string[]` | project root | Directories passed to `griffe --search`, relative to the project root. | | `docstringStyle` | `'google' \| 'numpy' \| 'sphinx' \| 'auto'` | `'google'` | Docstring flavour, passed to `griffe -d`. See [Docstring styles](/starlight-pydocs/guides/docstring-styles/). | | `docstringOptions` | `Record` | `{}` | Parser options, serialised into `griffe -D`. | | `extensions` | `(string \| { name, options })[]` | `[]` | Griffe extensions, passed to `griffe -e`. | | `extraRequirements` | `string[]` | `[]` | Python requirements the extensions need (`uvx --with`). | | `forceInspection` | `boolean` | `false` | Pass `griffe -x`, allowing griffe to import the package. | | `source` | `{ file } \| { url, cache? }` | none | Use a pre-generated dump instead of running griffe. See [Pre-generated dumps](/starlight-pydocs/guides/pregenerated-dumps/). | | `members` | `{ include?, exclude? }` | `{}` | Glob patterns on dotted paths, applied after `filters`. | | `filters` | `{ special?, private?, imported?, inherited? }` | see [Member selection](#member-selection) | Coarse member switches. | | `sourceLink` | `{ template, ref?, root? }` or `{ host, repo, ref?, root? }` | none | Link objects to their source lines. See [Source links](/starlight-pydocs/guides/source-links/). | | `sidebar` | `{ label?, collapsed?, group? }` | `{}` | Sidebar presentation, Starlight only. See [Multiple packages](/starlight-pydocs/guides/multiple-packages/). | | `versions` | `{ refs: { ref, label }[] }` | none | Badge objects with the release they appeared in. See [Version annotations](/starlight-pydocs/guides/version-annotations/). | `versions` extracts the package at past git refs, so it needs a checkout with history. You cannot use it together with `source`, because a pinned dump describes one release and has no history to compare against. Document that release as its own `packages` entry instead. ### Search paths [Section titled “Search paths”](#search-paths) `search` points at the *parent* of the package directory: use `../src` for `../src/mypkg`. Astro resolves these paths against the project root, and they may point outside it. This is what a `docs/` subdirectory needs. Leave the option unset only when the package sits in the project root. astro.config.mjs ```js packages: [ { name: 'mypkg', search: ['../src', '../generated'] }, ]; ``` Each path becomes its own `-s` argument, so split sources need no other configuration. Building the cache key scans everything under a search path, so a path pointing at unrelated code can trigger unneeded rebuilds. ### Bases [Section titled “Bases”](#bases) `base` cannot be empty, cannot be the site root, and cannot overlap another package’s base. For example, `api/mypkg` and `api` together are rejected, because one would swallow the other’s pages. `base` also cannot contain query strings, fragments or whitespace. Names need not be unique: the same import name can be documented at several bases, one per release. [Versioned docs](/starlight-pydocs/guides/versioned-docs/) describes this. ### Member selection [Section titled “Member selection”](#member-selection) `filters` defaults to `{ special: false, private: false, imported: false, inherited: true }`, and runs before `members`: * `special` documents dunder members such as `__init__` as separate entries (their parameters are merged into the class signature either way). * `private` documents underscore-prefixed members. * `imported` documents members that are imports rather than definitions. Submodules are an exception: they act as navigation, so they get pages whether or not they are exported. * `inherited` merges the public members of resolvable base classes into a class, badged with the class they came from. A module that declares `__all__` overrides all of this for its own members, exactly as mkdocstrings does: the list defines the documented surface. `members.include` and `members.exclude` then apply on top. They match dotted paths with a small glob dialect: `*` matches inside one segment, and `**` matches across segments. astro.config.mjs ```js packages: [ { name: 'mypkg', search: ['../src'], filters: { private: true }, members: { exclude: ['mypkg.internal.**', 'mypkg.*.legacy_*'] }, }, ]; ``` When `include` is non-empty, it acts as an allow-list. Only matching objects are documented, plus the containers on the way to them. ## How extraction is resolved [Section titled “How extraction is resolved”](#how-extraction-is-resolved) Extraction picks the first strategy that applies, per package: 1. `runner.command`: a full argv array. starlight-pydocs appends the dump arguments and `-o `, so pass only the executable and its own arguments, for example `['micromamba', 'run', '-n', 'docs', 'griffe']`. 2. `source: { file }`: a dump already on disk. Nothing runs. 3. `source: { url }`: a dump downloaded and cached. 4. `uvx --from griffe griffe`, when `uv --version` succeeds. `extraRequirements` become `--with` arguments. 5. ` -m griffe`, for the first of `python3`, `python` (or `runner.python`) that can `import griffe`. If nothing works, the error message lists every probe it tried and why each one failed. It also lists three ways to fix this: install `uv`, install griffe into the interpreter that runs the build, or point at a pre-generated dump. Whatever strategy runs, starlight-pydocs always invokes griffe as `griffe dump -f -d