Skip to content

Give each parse problem a code - #160

Merged
rr- merged 4 commits into
mainfrom
rework/25-problem-codes
Nov 6, 2026
Merged

rr- merged 4 commits into
mainfrom
rework/25-problem-codes

Conversation

@rr-

@rr- rr- commented Nov 6, 2026

Copy link
Copy Markdown
Owner

Each Problem from try_parse() now has a code, a short name that says what is wrong. Tools built on top, such as a linter, can match on it or let users silence one by name. Messages may change between versions. Codes don't.

>>> try_parse("Short.\n\nArgs:\n    x: X.\n    no colon here\n", "google").problems
[Problem(line=5, code='missing-colon', message="Expected a colon in 'no colon here'.")]

The codes, documented on the Problem class:

Code Meaning
bad-field A ReST or Epydoc field that cannot be read, like :param x with no closing colon
wrong-arguments A field head with too many or too few words, like :param a b c d:
mixed-returns-and-yields An Epydoc docstring with both @return and @yield
missing-colon A Google item with no colon, in a section of several items
missing-name A Google params section holding text and no named item
no-items A Google section whose items cannot be told apart by indent

ParseError is unchanged. The code of what it reports is on error.problems[0].code.

This also checks that parsing fails only with problems or a ParseError. A new test mutates every case input in every style and fails on any other exception, on a code the docs don't list, or on a line outside the docstring. Fuzzing turned up no crashes in the parsers themselves: about 190k stdlib, generated and whitespace-variant docstrings plus 180k mutated ones. It did find two in parse_from_object(), fixed in their own commit:

  • parse_from_object(int) and parse_from_object(sys) raised TypeError, because built-ins have no source to read attribute docstrings from. They now parse without them.
  • An object whose __doc__ is not a string, such as collections._tuplegetter, whose __doc__ is a descriptor, raised TypeError from inside the parser. It now parses as having no docstring.

Exceptions raised by a user's parse_item hook still propagate from both parse() and try_parse(). That's the user's code, not docstring text.

Parity against main: 0 of 29,074 docstrings differ. Codes aren't part of what the tool compares, and no message changed. Bench on 3.8: every workload is 0.98–1.03x of main.

@rr- rr- self-assigned this Nov 6, 2026
@rr-
rr- force-pushed the rework/25-problem-codes branch from de3e9c0 to 97e9851 Compare November 6, 2026 16:40
rr- added 4 commits November 6, 2026 17:40
parse_from_object() raised TypeError for a built-in class or module,
which inspect.getsource() has no source for, and for an object whose
__doc__ is not a string, such as a descriptor. Both now parse: the
first without attribute docstrings, the second as no docstring.
A Problem now has a code, a short name such as missing-colon, which
says what is wrong and stays the same between versions while the
message may change. The Problem class documents every code.

A test mutates every case input in every style and checks that
try_parse() only reports problems with a documented code and a line in
the docstring, and that parse() raises nothing but ParseError.
try_parse() never raises for a docstring's text. An exception from a
user's parse_item hook is the hook's own and propagates, so say so
instead of promising it never raises at all.
Python 3.13 dedents docstrings when compiling them, so the codes in
Problem's docstring lost the indent the test looked for. Dedent the
docstring first, as every version then agrees.
@rr-
rr- force-pushed the rework/25-problem-codes branch from 97e9851 to 4d026e6 Compare November 6, 2026 16:41
@rr-
rr- merged commit f2d2b63 into main Nov 6, 2026
24 of 25 checks passed
@rr-
rr- deleted the rework/25-problem-codes branch November 6, 2026 23:51
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.

1 participant