Python, mkdocs and mkdocstrings

Written by Dominik Pantůček on 2024-09-12

pythoncrypt4gh

When creating a library with some rather uncommon features, it is vital to ensure its users will be able to correctly use it by providing a useful documentation. Turns out this is possible even for Python projects.


One of the recent projects we are working on right now is a stream processing library for Crypt4GH encrypted containers. There is a lot of work to be done before it is actually useful for some real-world usage, however even during these early stages of development we found out we need both prose-like documentation and some generated API reference as well.

For creating both types of documentation, the MkDocs documentation tool can be used. Although it primarily deals with converting Markdown project documentation to a static HTML website, it can also be extended with plugins. One such plugin is mkdocstring with its mkdocstrings-python handler for Python Docstrings which can generate documentation from annotated Python source files.

Configuring this basic infrastructure in pyproject.toml file requires only listing required packages as optional development dependencies. In this following configuration snippet the mkdocs-material package is listed too as the material theme is commonly used and the users will find the resulting documentation site familiar.

[project.optional-dependencies]
dev = [
    "mkdocstrings[python]>=0.18",
    "mkdocs-material",
]

The mkdocs package requires its own configuration which configures at least the site name, but for our use-case it more importantly configures the mkdocstrings plugin. The skeleton of our configuration in mkdocs.yml file becomes:

site_name: Crypt4GH Processing
plugins:
  - mkdocstrings:
      handlers:
        python:
          paths:
            - oarepo_c4gh
          options:
            allow_inspection: false
            show_source: true
            filters:
              - "!^__"
              - "__init__"
              - "__bytes__"
            show_signature_annotations: true
            show_root_heading: true
            ignore_init_summary: false

As we can see, mkdocstrings plugin allows for quite some flexibility with respect to filtering collected identifiers, showing type hints (as signature annotations) and even directly showing appropriate source file section which the generated documentation is refering to.

Now that the generated content is configured, the visual style should be configured as well. However with the mkdocs-material package installed, it is just a matter of setting the theme name to a particular value (again, in mkdocs.yml):

theme:
  name: "material"

The only thing missing is the actual list of documentation pages. In the same configuration file a simple nav key with a list of "title" : file.md pairs has to be written:

nav:
  - "Public API": index.md
  - "Exceptions": exceptions.md

In index.md we have decided to write a prose-like guidelines on how to use the library without delving into implementation details. Then in the exceptions.md a generated list of exceptions with more information should be produced. Using mkdocstring syntax allows the author to combine prose with extracted documentation starting at given module and/or specific identifier. The exceptions.md file may look like this:

Exceptions
==========

All the exceptions that might be raised by the libary come from the
`exceptions` module.

::: oarepo_c4gh.exceptions

If you are interested in the documentation of a development version of this library, you can see the result in the official published documentation at Crypt4GH Processing.

To our pleasant surprise, it is apparently possible to semi-automatically maintain decent documentation and API reference even in Python. See ya next time for more surprises!