Skip to content

Getting started

Name a Python package. The plugin generates an API reference page for each of its modules. The pages appear in the Starlight sidebar as a tree that mirrors the package layout, and the site’s own search indexes them like any other page.

Griffe extracts the API. It reads the package’s source files directly and never imports them. So the machine that builds your docs does not need a working install of the package it documents.

  1. Install the plugin.

    npm install starlight-pydocs
  2. Check that the build process can run Griffe.

    By default, the plugin runs Griffe at site build time to read your package. We recommend uv for this. The plugin uses uv automatically when it is on your PATH. Otherwise, it falls back to python -m griffe.

    You can also run Griffe separately and point the plugin at the JSON file it writes. See Without Python for more information.

    Terminal window
    # Check if uv is available
    uv --version
    # If not, on macOS and Linux:
    curl -LsSf https://astral.sh/uv/install.sh | sh
  3. Add the plugin to Starlight, name the package, and reserve a spot in the sidebar for the pages it will generate.

    astro.config.mjs
    import starlight from '@astrojs/starlight';
    import { defineConfig } from 'astro/config';
    import starlightPydocs, { pydocsSidebarGroup } from 'starlight-pydocs';
    export default defineConfig({
    integrations: [
    starlight({
    title: 'My project',
    // Name the package and point griffe at its source.
    plugins: [
    starlightPydocs({
    packages: [{ name: 'mypkg', search: ['../src'] }],
    }),
    ],
    // Reserve the sidebar spot for the generated pages.
    sidebar: [{ label: 'API reference', items: [pydocsSidebarGroup] }],
    }),
    ],
    });

    Starlight reads the sidebar from your config before the plugin parses the package. At that point, Starlight does not yet know what pages exist. pydocsSidebarGroup is an empty group that holds a place in the tree. Route middleware replaces it with the generated links when each page renders. Put it wherever you want the API reference to appear.

    search is the list of directories passed to griffe --search. It is relative to the Astro project root. Point it at the parent of the package directory: use ../src for ../src/mypkg.

  4. Start the dev server.

    npm run dev

    /api/mypkg/ now exists. This page documents the package’s top-level module, and the plugin creates one page per submodule below it. The API reference group in the sidebar is now filled in.

    Every class, function and attribute has an anchor named after its dotted path. For example, /api/mypkg/report/#mypkg.report.Report.generate links straight to one method.

Each package also gets three files, served next to its pages. symbols.json is the index behind symbol search. objects.inv is a Sphinx inventory: it lets other projects link into yours. And llms.txt is the whole surface as plain Markdown.

Set this now if your docstrings are not google style. Griffe parses one flavour per package and does not guess. The wrong style leaves parameters and return values as plain, undifferentiated prose.

astro.config.mjs
8 collapsed lines
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({
packages: [
{
name: 'mypkg',
search: ['../src'],
docstringStyle: 'numpy',
},
],
6 collapsed lines
}),
],
sidebar: [{ label: 'API reference', items: [pydocsSidebarGroup] }],
}),
],
});

The default is google. numpy and sphinx are the alternatives. auto lets griffe guess the style per docstring: use it for a package whose docstrings disagree with each other. Docstring styles covers which sections each style recognises.

Add this once the pages exist. Type annotations that name something outside your package, such as pathlib.Path or a pandas DataFrame, render as plain text. To link them, give the plugin a Sphinx inventory to resolve them against. An inventory is the objects.inv file that most Python documentation sites publish. It maps object paths to page URLs.

astro.config.mjs
8 collapsed lines
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({
packages: [{ name: 'mypkg', search: ['../src'] }],
inventories: [
'python',
{ url: 'https://pandas.pydata.org/docs/objects.inv' },
{ file: './vendor/internal.inv', base: 'https://docs.internal.example/' },
],
6 collapsed lines
}),
],
sidebar: [{ label: 'API reference', items: [pydocsSidebarGroup] }],
}),
],
});

inventories is a top-level option, shared by every package. 'python' is a preset for the CPython standard library. Any other project that publishes an objects.inv file works too, by URL or as a local file.

base is the URL that the inventory’s relative links resolve against. It defaults to the objects.inv URL with its last segment removed. Set it explicitly for a local file, because a local file has no URL to derive it from.

Give the plugin your forge. Then every documented object links to the lines where it is defined.

astro.config.mjs
8 collapsed lines
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({
packages: [
{
name: 'mypkg',
search: ['../src'],
sourceLink: {
host: 'github',
repo: 'you/mypkg',
ref: 'main',
root: '..',
},
},
],
6 collapsed lines
}),
],
sidebar: [{ label: 'API reference', items: [pydocsSidebarGroup] }],
}),
],
});

host is github, gitlab or bitbucket. root is the directory that the paths in the URL are relative to. Set it to your repository root when the Astro project is a subdirectory, as it is for a docs/ site. For forges without a preset, use a URL template instead. See Source links.

If the build machine has no Python, or you would rather not run extraction on every docs build, generate the dump where the package already lives. Run this in the Python project. It writes exactly the JSON that the plugin would otherwise produce itself:

Terminal window
griffe dump -f -d google --search src mypkg -o mypkg.json

Publish mypkg.json from the Python project’s CI. Then fetch it into your Astro project before the docs build. The example below uses api/mypkg.json, a path resolved against the Astro project root. The package entry then points at that copy instead of a search path:

packages: [
{ name: 'mypkg', search: ['../src'] },
{ name: 'mypkg', source: { file: './api/mypkg.json' } },
];

source: { url: '…' } fetches it instead. It revalidates the cached copy with its ETag, so it does not re-download on every build. Pre-generated dumps covers the CI side of this.