Skip to content

[pydocstyle] Skip section detection inside RST directive bodies (D214, D405, D413) - #23635

Merged
ntBre merged 5 commits into
astral-sh:mainfrom
bxff:investigate/sphinx-docstring-reformat
Jul 28, 2026
Merged

[pydocstyle] Skip section detection inside RST directive bodies (D214, D405, D413)#23635
ntBre merged 5 commits into
astral-sh:mainfrom
bxff:investigate/sphinx-docstring-reformat

Conversation

@bxff

@bxff bxff commented Feb 28, 2026

Copy link
Copy Markdown
Contributor

Summary

Fixes #23562.

Content inside reStructuredText directives (e.g., .. code-block:: yaml) was incorrectly identified as docstring section headers. For example, references: inside a code-block would trigger D405 (capitalization), D214 (over-indentation), and D413 (missing blank line).

The root cause is that the section parser in from_docstring (docstrings/sections.rs) iterates docstring lines and calls is_docstring_section with no awareness of RST directive blocks. The word references matches SectionKind::References, the suffix : passes the section name check, and the preceding blank line (required by RST after the directive declaration) satisfies the end-of-paragraph heuristic.

There is already existing RST awareness in blanks_and_section_underline (the is_sphinx check at lines 1593 and 1691), but that serves a different purpose — it preserves blank lines when a real section header like Example: has a .. code-block:: directive as its body content. This fix is complementary: it prevents content inside a directive body from being misidentified as section headers in the first place.

This adds RST directive body tracking to from_docstring. When a line starting with .. is detected (an RST directive), all subsequent lines indented deeper than the directive are skipped from section detection. Real sections after directives continue to be detected correctly.

Test Plan

Added sphinx_directive.py test fixture with cases for:

  • Module-level docstring with .. code-block:: yaml containing references: (the exact case from ruff incorrectly reformatting Sphinx docstrings #23562)
  • Function-level docstring with directive followed by real sections (Returns:)
  • Single-colon directive variant (.. code-block: yaml)
  • Nested directives (.. note:: containing .. code-block::)
  • Real section (Notes:) following a directive — verifies sections after directives are still detected

Registered test cases for D214, D405, and D413 against the new fixture. All 72 pydocstyle tests pass:

cargo test -p ruff_linter -- "pydocstyle::tests::rules"
test result: ok. 72 passed; 0 failed; 0 ignored

Also manually verified the original reproduction case no longer triggers D405/D214/D413 false positives.

…214`, `D405`, `D413`)

Closes astral-sh#23562

Content inside reStructuredText directives (e.g., `.. code-block:: yaml`) was incorrectly identified as docstring section headers. For example, `references:` inside a code-block would trigger D405 (capitalization), D214 (over-indentation), and D413 (missing blank line).

This adds RST directive body tracking to `from_docstring` in the section parser. When a line starting with `.. ` is detected, all subsequent lines indented deeper than the directive are skipped from section detection. Real sections after directives continue to be detected correctly.
@astral-sh-bot

astral-sh-bot Bot commented Feb 28, 2026

Copy link
Copy Markdown

ruff-ecosystem results

Linter (stable)

✅ ecosystem check detected no linter changes.

Linter (preview)

✅ ecosystem check detected no linter changes.

@amyreese amyreese added bug Something isn't working rule Implementing or modifying a lint rule labels Mar 3, 2026
@ntBre ntBre closed this May 7, 2026
@ntBre ntBre reopened this May 7, 2026
@astral-sh-bot
astral-sh-bot Bot requested a review from ntBre May 7, 2026 23:30

@ntBre ntBre left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for working on this and sorry for the delay. This looks good to me overall, I just had one question about nested section handling. CI is also failing, but I think it's just from a change to our lines of context in the snapshot tests and should be easy to regenerate.

@bxff

bxff commented Jun 11, 2026

Copy link
Copy Markdown
Contributor Author

Updated this, thanks. I changed the directive tracking from a single active indent to a stack, so exiting an inner directive keeps the outer directive body active.

I also added the nested note / code-block regression case you suggested and regenerated the affected snapshot context.

@bxff
bxff requested a review from ntBre June 11, 2026 16:14

@ntBre ntBre left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you! And sorry again for the delay. This looks good to me. I think I was too hasty with abandoning the Option in favor of a stack. I pushed a small suggestion from Codex to simplify back to an Option and merged the snapshot updates from main, but this otherwise looked great to me.

@ntBre
ntBre merged commit 810b772 into astral-sh:main Jul 28, 2026
47 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working rule Implementing or modifying a lint rule

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ruff incorrectly reformatting Sphinx docstrings

3 participants