# 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](https://mkdocstrings.github.io/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.

## Setup

1. Install the plugin.

   ```sh frame="none"
     npm install starlight-pydocs
     ```

     ```sh frame="none"
     pnpm add starlight-pydocs
     ```

     ```sh frame="none"
     yarn add 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](https://docs.astral.sh/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](#without-python) for more information.

   ```sh
   # 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.

   ```js title="astro.config.mjs" {3, 10-15, 17-18}
   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.

   ```sh frame="none"
   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](/starlight-pydocs/guides/search/). `objects.inv` is a
Sphinx inventory: it lets [other projects link into
yours](/starlight-pydocs/guides/cross-references/). And
[`llms.txt`](/starlight-pydocs/guides/llms-txt/) is the whole surface as plain
Markdown.

## Docstring style

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.

```js title="astro.config.mjs" collapse={1-8, 18-23} "docstringStyle: 'numpy'"
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',
            },
          ],
        }),
      ],
      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](/starlight-pydocs/guides/docstring-styles/) covers
which sections each style recognises.

## Linking to other projects

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.

```js title="astro.config.mjs" collapse={1-8, 17-22} {12-16}
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/' },
          ],
        }),
      ],
      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.

## Source links

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

```js title="astro.config.mjs" collapse={1-8, 23-28} {15-20}
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: '..',
              },
            },
          ],
        }),
      ],
      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](/starlight-pydocs/guides/source-links/).

## Without Python

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:

```sh
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:

```diff lang="js"
 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](/starlight-pydocs/guides/pregenerated-dumps/) covers the CI side of this.

[Configuration](/starlight-pydocs/guides/configuration/)
  [Autodoc](/starlight-pydocs/guides/autodoc/)
  [Multiple packages](/starlight-pydocs/guides/multiple-packages/)
  [Vanilla Astro](/starlight-pydocs/guides/vanilla-astro/)