Skip to content

Feature: Sphinx/RST docstring style support via configuration #320

@Alberto-Codes

Description

@Alberto-Codes

Problem Statement

docvet's enrichment and griffe checks assume Google-style docstring conventions. Projects using Sphinx/RST-style docstrings — the dominant style in the scientific Python and AWS SDK ecosystems — get flooded with false positives or must disable most enrichment rules entirely.

This is the #1 barrier to adoption outside the Google-style world.

Current Behavior

A boto3-style Sphinx/RST docstring like:

def load_from_definition(self, resource_name, single_resource_json_definition, service_context):
    """
    Loads a resource from a model, creating a new
    :py:class:`~boto3.resources.base.ServiceResource` subclass.

    :type resource_name: string
    :param resource_name: Name of the resource to look up.

    :type service_context: :py:class:`~boto3.utils.ServiceContext`
    :param service_context: Context about the AWS service

    :rtype: Subclass of :py:class:`~boto3.resources.base.ServiceResource`
    :return: The service or resource class.
    """

...triggers these false-positive docvet findings:

  • missing-raises — docvet doesn't see Raises: (but :raises: may exist)
  • missing-examples — docvet doesn't see Examples: (RST uses :: blocks inline)
  • missing-cross-references — docvet doesn't see See Also: (but :py:class: cross-refs are everywhere)
  • prefer-fenced-code-blocks — flags :: and >>> as problems (they're correct RST)
  • missing-typed-attributes — doesn't recognize #: Sphinx autodoc comments
  • griffe-missing-type — griffe's Google parser can't parse :type name: str syntax

The only options today are disabling most enrichment rules, which throws away the genuinely useful checks.

Real-world validation: boto3

Running docvet check --all against boto3 (AWS SDK for Python, ~34 source files, ~355 functions):

  • 678 occurrences of Sphinx/RST markers (:type, :param, :rtype:, :return:) across 34 files
  • 0 occurrences of Google-style markers (Args:, Returns:, Raises:) in the original code
  • botocore (boto3's upstream dependency) uses identical Sphinx/RST style exclusively
  • The style is architecturally mandated — boto3's LazyLoadedDocstring classes generate RST programmatically

This isn't an outlier. Sphinx/RST is the standard for: Django, Flask, SQLAlchemy, requests, numpy (partial), scipy, boto3/botocore, CPython stdlib, Celery, and many others.

Proposed Solution

New config key: docstring-style

[tool.docvet]
docstring-style = "sphinx"  # default: "google"

Valid values: "google" (current default, no behavior change) and "sphinx".

Behavior changes when docstring-style = "sphinx"

Scoped to pattern recognition in existing rules — no new dependencies, no RST parser, no new check modules.

1. Section detection — parallel RST-aware patterns

Google section Sphinx/RST equivalent Detection pattern
Raises: :raises SomeError: ^\s*:raises?\s+\w+
Returns: :returns: or :rtype: ^\s*:(returns?|rtype):
Args: :param name: or :type name: ^\s*:(param|type)\s+\w+
Yields: (no RST equivalent) skip rule
Receives: (no RST equivalent) skip rule
Warns: (no RST equivalent) skip rule
Attributes: #: comments or :ivar name: ^\s*#: or ^\s*:ivar\s+\w+
Examples: :: indented block, >>> doctest presence of any code pattern
See Also: .. seealso:: or inline :py:class: directive or Sphinx roles in body

Implementation sketch:

_SPHINX_SECTION_MAP: dict[str, re.Pattern[str]] = {
    "Args": re.compile(r"^\s*:(param|type)\s+\w+", re.MULTILINE),
    "Returns": re.compile(r"^\s*:(returns?|rtype):", re.MULTILINE),
    "Raises": re.compile(r"^\s*:raises?\s+\w+", re.MULTILINE),
    "Attributes": re.compile(r"^\s*:(ivar|cvar)\s+\w+", re.MULTILINE),
    "See Also": re.compile(r"(^\s*\.\.\s+seealso::|:py:\w+:`[^`]+`)", re.MULTILINE),
    "Examples": re.compile(r"(^\s*>>>\s|::\s*$|^\s*\.\.\s+code-block::)", re.MULTILINE),
}

2. Auto-disable inapplicable rules

_SPHINX_SKIP_RULES = frozenset({
    "require_yields",      # no RST equivalent
    "require_receives",    # no RST equivalent
    "require_warns",       # no RST equivalent
    "require_other_parameters",  # RST uses more :param: lines
})

3. prefer-fenced-code-blocks → auto-disabled in sphinx mode

:: and >>> are correct RST code block syntax.

4. missing-typed-attributes → recognize #: and :ivar

Pragmatic v1: skip typed-attributes check entirely in sphinx mode; revisit with #: detection in a follow-up.

5. missing-cross-references → accept Sphinx roles in body

In sphinx mode, :py:class:, :py:meth: etc. anywhere in the body count as cross-references (RST weaves them inline).

6. griffe check → auto-skip in sphinx mode

griffe's parser is hardcoded to Google style. Sphinx docstrings always produce parse failures.

Config integration

Auto-disable logic when docstring_style == "sphinx":

  1. Remove "griffe" from warn_on and fail_on (unless user explicitly set it)
  2. Set prefer_fenced_code_blocks = False (unless user explicitly set it)
  3. Set require_typed_attributes = False for v1 (unless user explicitly set it)

What this enables

Check Status with sphinx style
presence Works as-is
coverage Works as-is
freshness Works as-is
enrichment: missing-raises Works with RST detection
enrichment: missing-examples Works with RST detection
enrichment: missing-cross-references Works with Sphinx role detection
griffe Auto-disabled

Acceptance Criteria

  • docstring-style = "sphinx" config key accepted and validated
  • RST section patterns detected for Args, Returns, Raises, Attributes, Examples, See Also
  • Inapplicable rules auto-disabled in sphinx mode
  • griffe check auto-skipped in sphinx mode
  • prefer-fenced-code-blocks auto-disabled in sphinx mode
  • User overrides respected (explicit config beats auto-disable)
  • Tests with RST fixture files mirroring boto3 patterns

Technical Notes

Estimated changes: ~250 lines across config.py (~20), enrichment.py (~50), cli.py (~10), griffe_compat.py (~5), tests (~150). No new files, no new dependencies.

Ordering: This issue should land before other enrichment issues (#2-#10) — each has a "Sphinx/RST interaction" section that depends on the style-dispatch infrastructure from this issue.

Scope/non-goals:

Example pyproject.toml

[tool.docvet]
docstring-style = "sphinx"
fail-on = ["presence", "enrichment"]
warn-on = ["freshness", "coverage"]

[tool.docvet.enrichment]
require-raises = true
require-examples = ["class", "dataclass"]
require-cross-references = true

References


BMAD Workflow

When ready to implement, run in a fresh context:

  • Full approach: /bmad-bmm-create-prd -> architecture -> stories (recommended — multi-module touch)
  • Quick approach: /bmad-bmm-quick-spec -> /bmad-bmm-quick-dev

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions