Fumadocs

Python

Generate API references from Python packages.

Experimental

Support for Python docgen is still experimental, please use it in caution.

Introduction

fumadocs-python documents a Python package from its source code and docstrings. A Python command collects the package into a JSON file, which you can serve directly as a runtime content source, or convert into MDX files.

Every module and class becomes a page, with functions, attributes and parameters rendered by the included components.

Setup

Install the package:

npm install fumadocs-python shiki

Generate JSON

Install the Python command, it collects docs from your Python package:

pip install ./node_modules/fumadocs-python

Generate the JSON file of a package:

fumapy-generate httpx

It writes httpx.json into the current directory, pass --dir to change it.

Docstring sections like Parameters: and Returns: are parsed as Google style, pass --docstring-style numpy or --docstring-style sphinx for other styles. Docstrings outside these styles are rendered as Markdown.

Styles

The components are styled with Tailwind CSS, add the preset:

Tailwind CSS
@import 'tailwindcss';
@import 'fumadocs-ui/css/neutral.css';
@import 'fumadocs-ui/css/preset.css';
@import 'fumadocs-python/preset.css';

Configure Source

Create a source from the JSON file and pass it to loader():

lib/source.ts
import { loader } from 'fumadocs-core/source';
import { createPython } from 'fumadocs-python';

const python = createPython({
  file: './httpx.json',
});

export const source = loader(await python.staticSource(), {
  baseUrl: '/docs',
  // adds a `module` or `class` badge to generated pages
  plugins: [python.loaderPlugin()],
});

Pages are placed under the package name, for example httpx._client.Client is served at /docs/httpx/_client/Client.

Then load and render a page:

app/docs/[[...slug]]/page.tsx
import { source } from '@/lib/source';
import { notFound } from 'next/navigation';
import { DocsBody, DocsPage, DocsTitle } from 'fumadocs-ui/layouts/docs/page';

export default async function Page({ params }: PageProps<'/docs/[[...slug]]'>) {
  const page = source.getPage((await params).slug);
  if (!page) notFound();

  const data = await page.data.load();
  const { body, toc } = await data.render();

  return (
    <DocsPage toc={toc}>
      <DocsTitle>{page.data.title}</DocsTitle>
      <DocsBody>{body}</DocsBody>
    </DocsPage>
  );
}

Dynamic Source

staticSource() reads the JSON file once. To pick up a regenerated file without restarting, use dynamicSource() with dynamicLoader() instead:

lib/source.ts
import { dynamicLoader } from 'fumadocs-core/source';
import { createPython } from 'fumadocs-python';

const python = createPython({
  file: './httpx.json',
});

const pythonLoader = dynamicLoader(python.dynamicSource(), {
  baseUrl: '/docs',
});

export function getSource() {
  return pythonLoader.get();
}

The file is read again after pythonLoader.invalidate(), and getSource() returns the same content loader instance until then.

Compiler

Source code and docstring examples are highlighted with Shiki, configure it with rehypeCodeOptions. You can also pass remark/rehype plugins:

lib/source.ts
import { createPython } from 'fumadocs-python';
import remarkMath from 'remark-math';
import rehypeKatex from 'rehype-katex';

const python = createPython({
  file: './httpx.json',
  rehypeCodeOptions: {
    themes: {
      light: 'github-light',
      dark: 'github-dark',
    },
  },
  remarkPlugins: [remarkMath],
  rehypePlugins: [rehypeKatex],
});

Generate MDX Files

You can convert the JSON into MDX files instead, and keep them next to your other content. Links between modules and classes are written into the files, so baseUrl must match where the content is served:

import { rimraf } from 'rimraf';
import * as Python from 'fumadocs-python';
import * as fs from 'node:fs/promises';

const content = JSON.parse(await fs.readFile('./httpx.json', 'utf-8'));
// clean previous output
await rimraf('content/docs/httpx');

await Python.write(Python.convert(content, { baseUrl: '/docs' }), 'content/docs');

How is this guide?

Last updated on

On this page