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":
- Remove
"griffe" from warn_on and fail_on (unless user explicitly set it)
- Set
prefer_fenced_code_blocks = False (unless user explicitly set it)
- 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
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
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:
...triggers these false-positive docvet findings:
missing-raises— docvet doesn't seeRaises:(but:raises:may exist)missing-examples— docvet doesn't seeExamples:(RST uses::blocks inline)missing-cross-references— docvet doesn't seeSee 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 commentsgriffe-missing-type— griffe's Google parser can't parse:type name: strsyntaxThe only options today are disabling most enrichment rules, which throws away the genuinely useful checks.
Real-world validation: boto3
Running
docvet check --allagainst boto3 (AWS SDK for Python, ~34 source files, ~355 functions)::type,:param,:rtype:,:return:) across 34 filesArgs:,Returns:,Raises:) in the original codeLazyLoadedDocstringclasses generate RST programmaticallyThis 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-styleValid 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
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:Receives:Warns:Attributes:#:comments or:ivar name:^\s*#:or^\s*:ivar\s+\w+Examples:::indented block,>>>doctestSee Also:.. seealso::or inline:py:class:Implementation sketch:
2. Auto-disable inapplicable rules
3.
prefer-fenced-code-blocks→ auto-disabled in sphinx mode::and>>>are correct RST code block syntax.4.
missing-typed-attributes→ recognize#:and:ivarPragmatic v1: skip typed-attributes check entirely in sphinx mode; revisit with
#:detection in a follow-up.5.
missing-cross-references→ accept Sphinx roles in bodyIn 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":"griffe"fromwarn_onandfail_on(unless user explicitly set it)prefer_fenced_code_blocks = False(unless user explicitly set it)require_typed_attributes = Falsefor v1 (unless user explicitly set it)What this enables
sphinxstyleAcceptance Criteria
docstring-style = "sphinx"config key accepted and validatedprefer-fenced-code-blocksauto-disabled in sphinx modeTechnical 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:
"numpy", see Feature: Implement enrichment check (missing docstring sections) #8)Example
pyproject.tomlReferences
LazyLoadedDocstringgenerates RST programmatically at runtimeBMAD Workflow
When ready to implement, run in a fresh context:
/bmad-bmm-create-prd-> architecture -> stories (recommended — multi-module touch)/bmad-bmm-quick-spec->/bmad-bmm-quick-dev