# Migrating from mkdocstrings

Both plugins render the same model. [Griffe](https://mkdocstrings.github.io/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

This handler configuration:

```yaml title="mkdocs.yml" {5-6,8-9,10-11,13-15}
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.inv
```

becomes this, with the same four options at the matching numbers:

```js title="astro.config.mjs" {14-15,16-17,18-19,22-23}
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

- 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_mapping` entries 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

### 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 <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](/starlight-pydocs/guides/autodoc/).

### 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](/starlight-pydocs/guides/pregenerated-dumps/). This is the route for CI
without Python.

### 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](/starlight-pydocs/guides/component-overrides/) swap any of those nine for
  your own, everywhere they render.
- [Theme tokens](/starlight-pydocs/guides/theming/) restyle the output without replacing anything.
- The [Content Layer loader](/starlight-pydocs/guides/vanilla-astro/#the-content-layer-loader) is a
  typed way of building custom pages from the same model.

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