DEV Community

Alberto Nieto
Alberto Nieto

Posted on

How docvet learned to read Sphinx and NumPy docstrings

The problem: one inspector, one language

docvet checks whether your Python docstrings are present, complete, accurate, and renderable. Since v1.0, it's caught missing Raises: sections, stale docstrings after code changes, broken mkdocs rendering, and more — 22 rules across five check modules.

But it only understood Google-style.

That's a problem, because a huge portion of the Python ecosystem uses something else:

  • Sphinx/RST style (:param name:, :returns:, :raises:) — Django, Flask, SQLAlchemy, requests, boto3, CPython stdlib
  • NumPy style (underlined section headers) — NumPy, SciPy, pandas, scikit-learn, matplotlib

If you maintain a Django app or a scientific Python library, docvet's enrichment checks couldn't parse your docstrings. As of v1.13.0, that's fixed.

How style support works

It's a project-level setting, not auto-detection

docvet doesn't guess your style per-file. You tell it once in pyproject.toml:

[tool.docvet]
docstring-style = "sphinx"
Enter fullscreen mode Exit fullscreen mode

Two valid options: "google" (default) and "sphinx". The setting applies project-wide.

The NumPy twist: NumPy-style underlined headers are recognized automatically in the default Google mode. If your project uses NumPy-style docstrings, you don't need to change anything — it already works.

Sphinx/RST parsing

When you set docstring-style = "sphinx", docvet maps field-list directives to the same internal section model used for Google-style:

Sphinx directive Maps to
:param:, :type: Args
:returns:, :return:, :rtype: Returns
:raises: Raises
:ivar:, :cvar: Attributes
.. seealso:: See Also
.. code-block::, >>> Examples

This means all existing enrichment rules apply — missing-raises, missing-examples, missing-attributes, and the rest.

def connect(host: str, port: int = 5432) -> Connection:
    """Open a database connection.

    :param host: Hostname or IP address.
    :param port: Port number.
    :returns: An active connection object.
    :raises ConnectionError: If the host is unreachable.
    """
Enter fullscreen mode Exit fullscreen mode

docvet checks this the same way it checks a Google-style docstring — are all raised exceptions documented? Are there parameters in the signature not covered by :param: directives?

Auto-disabled rules: Five enrichment rules that have no Sphinx/RST equivalent are automatically disabled in Sphinx mode: require_yields, require_receives, require_warns, require_other_parameters, and prefer_fenced_code_blocks. You can override any of them:

[tool.docvet]
docstring-style = "sphinx"

[tool.docvet.enrichment]
require_yields = true  # re-enable if your project uses a yields convention
Enter fullscreen mode Exit fullscreen mode

Griffe compatibility: The griffe check is auto-skipped in Sphinx mode, since griffe's parser targets Google-style docstrings.

NumPy section recognition

NumPy-style uses section headers with matching-length underlines:

def transform(data, axis=0):
    """Apply transformation along an axis.

    Parameters
    ----------
    data : array_like
        Input data.
    axis : int, optional
        Axis along which to operate.

    Returns
    -------
    result : ndarray
        Transformed data.

    Raises
    ------
    ValueError
        If axis is out of bounds.
    """
Enter fullscreen mode Exit fullscreen mode

In the default Google mode, docvet already recognizes these headers alongside Google colon-format headers. The section parser looks for 3+ consecutive dashes or equals signs on the line following a known header name. No config change needed.

NumPy-specific sections like Notes, References, Warnings, Extended Summary, and Methods are recognized for section boundary detection but don't have their own enforcement rules — they won't trigger findings.

New rules

missing-returns

Functions that return a value should document what they return:

# docvet flags this — return value is undocumented
def calculate_total(items: list[Item]) -> float:
    """Sum all item prices."""
    return sum(item.price for item in items)
Enter fullscreen mode Exit fullscreen mode

The rule skips cases where a return section doesn't make sense:

  • Stubs (... or pass body) — interface definitions, not implementations
  • __init__ methods — return None by convention
  • Properties — the getter docstring describes the attribute, not a return value
  • Re-raise-only functions — they don't meaningfully "return"

Works in both Google and Sphinx modes.

overload-has-docstring

@typing.overload signatures describe distinct call patterns. They deserve docstrings explaining when to use each variant:

@overload
def parse(data: str) -> dict: ...

@overload
def parse(data: bytes) -> dict: ...

def parse(data):
    """Parse input data into a dictionary."""
Enter fullscreen mode Exit fullscreen mode

docvet flags the overload signatures missing docstrings. The existing missing-docstring rule skips overloads to avoid double-reporting — each rule owns its scope cleanly.

The bigger picture

This release is about reach. docvet's quality model — six layers from presence to rendering — applies regardless of docstring style. The rules don't change; the parser learned new dialects.

Style Configuration Notes
Google Default, no config needed Original support
NumPy Default, no config needed Recognized automatically in Google mode
Sphinx/RST docstring-style = "sphinx" One line in pyproject.toml

If you're on a Django team, a scientific Python project, or any codebase using Sphinx-style docs, docvet is ready.

pip install --upgrade docvet
docvet check --all
Enter fullscreen mode Exit fullscreen mode

22 rules across 5 check modules. Zero runtime dependencies beyond typer.

Docs | PyPI

Top comments (0)