Skip to content

Fix RST field list absorbing trailing blocks into last field (#86) - #113

Merged
rr- merged 1 commit into
rr-:masterfrom
apoorvdarshan:fix/issue-86-rst-field-list-boundary
Oct 27, 2026
Merged

rr- merged 1 commit into
rr-:masterfrom
apoorvdarshan:fix/issue-86-rst-field-list-boundary

Conversation

@apoorvdarshan

Copy link
Copy Markdown
Contributor

Fixes #86

Root cause

In the ReST parser (docstring_parser/rest.py), the field list is split into individual fields with:

for match in re.finditer(r"(^:.*?)(?=^:|\Z)", meta_chunk, flags=re.S | re.M):

A field only ends at the next line starting with : or at the end of input. Any trailing block that follows the field list but has no leading : (e.g. an Example section after :rtype: dict) therefore gets pulled into the value of the last field. The last :rtype:/:param: type then swallows everything after it.

Reproduction

from docstring_parser import parse

doc = '''
Creates a user with the given username, age, and activity status.

:param username: The username of the user.
:type username: str
:return: A dictionary representing the created user.
:rtype: dict

Example:

>>> create_user("Alice", 25)
{'username': 'Alice'}
'''

t = parse(doc)
print(repr(t.many_returns[-1].type_name))

Wrong output (before the fix):

'dict\nExample:\n\n>>> create_user("Alice", 25)\n{\'username\': \'Alice\'}'

Corrected output (after the fix):

'dict'

Fix

Terminate a field not only at the next :field: or end of input, but also at a blank line followed by unindented, non-field content — which is where an RST field list ends:

r"(^:.*?)(?=^:|\n[ \t]*\n(?=[^ \t\n:])|\Z)"

The new alternative \n[ \t]*\n(?=[^ \t\n:]) matches a blank line followed by a line that starts at the base indentation and is not another field, so the trailing block is no longer absorbed. This is deliberately narrow:

  • Indented continuation lines (with or without a preceding blank line) still stay in the field body.
  • Unindented continuation lines that follow without a blank separator (already supported behavior) are unaffected.

Tests

Added test_returns_does_not_absorb_trailing_block in docstring_parser/tests/test_rest.py, which asserts that a :rtype: dict followed by a trailing Example block yields type_name == "dict". The test fails on the unmodified code (the type name includes the whole Example block) and passes with the fix.

The full existing test suite passes (255 passed). black, isort, and pylint (repo config) are clean on the changed files.

Disclosure: prepared with AI assistance; reviewed and verified locally.

A field ran until the next field or the end of the docstring, so a
block after the field list, such as an example, became part of the
last field's value. A blank line followed by unindented text that is
not a field now ends the field, also with CRLF line endings, and that
text goes to the long description instead of being dropped.

Fixes rr-#86.
@rr-
rr- force-pushed the fix/issue-86-rst-field-list-boundary branch from f81ee06 to 8e4d5ee Compare October 27, 2026 22:16
@rr-

rr- commented Oct 27, 2026

Copy link
Copy Markdown
Owner

Thanks @apoorvdarshan! I force-pushed to your branch to rebase it onto master and reword the commit to the repo's new commit-message format. I also made two fixes:

  • Text after the field list (like the Example: block) was dropped from the parse result. It now goes to long_description.
  • Files with CRLF line endings still had the original bug.

I added tests for both. You're still the commit author and credited in the changelog. CI is green, so I'm merging this now.

@rr-
rr- merged commit f9be8e3 into rr-:master Oct 27, 2026
24 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Parsing of last RST params definition (incorrectly) includes rest-of-docs.

2 participants