Skip to main content

Get started with Python docstrings

pyDocusaurus is designed for VS Code developers and Markdown docstring style. Basically, this package follows the following principles:

  • No type hints in the docstring: Users can add optional type annotations in the docstring, but such annotations are only used as falling-back options. The type annotations are essentially based on Python's typing system.

  • VS Code compatibility: pyDocusaurus does not follow the PEP 287 docstring rules because it was designed for rst-style texts. Instead, we adopt the VS Code style. This tutorial will start by introducing the basics of writing such docstrings.

  • Half-and-half analysis: Currently, pyDocusaurus uses both the source code AST analysis and run-time inspections to extract type annotations and docstrings. Users need to ensure that the package to be converted is runnable with pyDocusaurus.

info

It means that pyDocusaurus requires that:

  1. Your package can work in Python>=3.14 run time.
  2. Your package has source codes and docstrings, not just compiled .pyc files.
  3. Your package is not written in C (such as the .pyd files).

Currently, pyDocusaurus has not been tested on .pyi files. It is recommended that the docstrings should be written together with the source codes.

1. Write docstrings for modules

pyDocusaurus reads the following package/module docstring:

example_module.py
# -*- coding: UTF-8 -*-
"""
Module Title
============

Author
------
My name
myemail@gmail.com

License
-------
MIT License

Description
-----------
Markdown description text.

Support **multi-line** and complicated formats.

```sh
example-command
```
"""

import a_module

...
tip

pyDocusaurus uses section titles, such as "Auhtor" and "License", to detect fields. The order of sections can be switched.

2. Write docstrings for functions

pyDocusaurus reads the following function/method docstring:

def example_func(arg1: str, arg2: int = 0) -> float:
"""An example function.

Arguments
---------
arg1: `str`
The first argument.

arg2: `int`
The second argument.

Returns
-------
#1: `float`
The returned value.
"""
raise NotImplementedError

Basically, a function's docstring contains three parts: a description, arguments, and return values. If any arguments appear in the signature but are not mentioned in the docstring, pyDocusaurus can sense such ignorance and report a warning. If a function has no arguments or no return values (the return value is None), the corresponding docstring section can be skipped.

tip

pyDocusaurus uses section titles, "Arguments" and "Returns", to divide the original docstring into different parts. The order of sections can be switched.

tip

The section titles can be named as:

  • Arguments, Argument, Parameters, Parameters

  • Returns, Return, Results, Result

2.1. Write docstrings of an iterator

If users intend to write an iterator function, it is important to let the function explicitly yield ..., for exmaple:

def example_iterator_func(
arg1: str, arg2: int
) -> Iterator[tuple[str, str, Iterator[tuple[str]]]]:
"""An example function iteratively return items.

Arguments
---------
arg1: `str`
The first argument.

arg2: `int`
The second argument.

Yields
------
#1: `str`
The first iterator item.

#2: `str`
The second iterator item.

#3: `Iterator[tuple[str]]`
The third iterator item a nested iterator.
"""
yield NotImplemented
raise NotImplementedError
tip

In this case, users need to maintain that

  1. yield is explicitly used.
  2. The returned value is annotated as Iterator, Generator, or their async versions.
  3. In the docstring, use Yields as the section title instead of the Returns section.

3. Write docstrings for classes

pyDocusaurus reads the following class docstring:

class ExampleClass:
"""An example class which is a mixture of methods, properties, and operators."""

def __init__(self, key: str, val: int = 10) -> None:
"""Initialization.

Arguments
---------
key: `str`
The first key to be configured.

val: `int`
The first value of the given key.
"""
raise NotImplementedError

@property
def prop(self) -> int:
"""A vanilla property with annotation."""
raise NotImplementedError

def method(self, arg: int, /, arg_any: Any, *, arg_key: str = "test") -> str:
"""A vanilla instance method.

Arguments
---------
arg: `int`
The input argument that is positional-only.

arg_any: `Any`
The input argument without specified type.

arg_key: `str`
The input argument that is keyword-only.

Returns
-------
#1: `str`
The returned value.
"""
raise NotImplementedError

Overall, the top-level class docstring will be interpretedd as a multi-line Markdown documentdescribingf the clasn. The other rules are:

  1. The function and operator docstrings follow the previously mentioned function style. For methods and class methods, the first argument self/cls is skipped in the docstring.

  2. The initialization method will be parsed as the class arguments.

  3. The docstring of a property or cached property is interpreted as a multi-line Markdown document. If a property implements its setter or deleter, only the docstring of the getter will be read.

4. Write docstrings for fields

In PEP 224, the author proposed a standard for the attribute docstring, but was rejected by the committee. However, this proposal has effectively become the solution for VS Code to supplement regular docstrings. pyDocusaurus will read such docstrings from the source codes directly. Here is an example of annotating a PyDantic model:

class ExampleModel(BaseModel):
"""An example PyDantic model that is used for testing the docstring extraction.

The initialization function are synthesized from the field definitions.
"""

val_1: int
"""The most plain field that is required."""

val_2: str = ""
"""An optional field with a default value."""

val_3: int | str = Field(default="")
"""A union field with the default value specified by the `Field` creator."""

val_4: list[str] = Field(default_factory=list)
"""A list field with a default value specified by the factory function."""

val_5: Annotated[list[str], Field(default_factory=list)]
"""A list field with the default value specified by the annotation."""

Although PyDantic provides the Field.description attribute for writing docstrings, we do not adopt this runtime solution because such docstrings cannot be detected by static type checkers. In comparison, the “attribute-docstring” style is preferred here because VS Code can detect it.

5. Write docstrings for types

PEP 224 also applies to the type aliases and variables. Currently, pyDocusaurus implements the docstring detection for the type aliase definitions.

warning

In other words, currently, the docstring of the global variables are not supported yet.

Suppose that we have such a module:

example_aliases.py
CustomType: TypeAlias = int | str
"""docstring for CustomType"""

type SpecifiedList = list[str]
"""docstring for SpecifiedList"""

Here, the example module contains two aliases defined in different ways. pyDocusaurus can detect both cases.