Skip to content

Add try_parse, which never raises - #152

Merged
rr- merged 1 commit into
mainfrom
rework/16-try-parse
Nov 6, 2026
Merged

rr- merged 1 commit into
mainfrom
rework/16-try-parse

Conversation

@rr-

@rr- rr- commented Nov 6, 2026

Copy link
Copy Markdown
Owner

parse() stops at the first part of a docstring it cannot read. Users who want whatever can be read, such as documentation tools or a linter, have no way to get it. This adds try_parse(), which keeps going and reports what went wrong:

>>> result = try_parse("Short.\n\nArgs:\n    x: X.\n    no colon here\n", "google")
>>> result.docstring.params[0].arg_name
'x'
>>> result.docstring.long_description
'no colon here'
>>> result.problems
[Problem(line=5, message="Expected a colon in 'no colon here'.")]

The text of a field or item that cannot be read goes to the long description, so nothing is dropped. The no-drop invariant test now runs through try_parse() on every case, including the ones parse() rejects. Lines count from 1 in the docstring as written, including blank lines before the text.

parse() is now try_parse() plus a raise. It raises one ParseError for the whole docstring. ParseError.problems lists every problem, and ParseError.result holds what was parsed anyway. The message is still the first problem's, with " (and 2 more)" added when there are others.

There is no strict= flag, and each function has one return type: parse() returns a Docstring, try_parse() a ParseResult(docstring, problems). Every style module and every Parser gets try_parse() too.

In auto mode, try_parse() prefers a style that reads the docstring with no problems, then the one with most meta. So whenever parse() returns, try_parse() returns the same docstring with no problems. A test checks that for every style on every case. The downside: a Google docstring with one bad item comes back as a plain ReST description in auto mode, as parse() does today. Passing the style gives the Google reading with its problem.

Parity against main: 749 of 29,074 docstrings differ, all in parse(). Each one is a ParseError message that gained " (and N more)", and nothing else changed. They are 734 generated docstrings, 13 from the stdlib and 2 case inputs (Google and Epydoc account for nearly all of them, one is ReST). One case file changes for the same reason.

Bench on 3.8: parse is 1.02–1.08x of main, the worst being ReST at 1.08. Compose is unchanged.

try_parse() parses what it can of a docstring and never raises. It
returns a ParseResult of the docstring and a list of problems, each
with its line in the docstring as written. The text of a field or item
that cannot be read goes to the long description.

parse() is try_parse() plus a raise: one ParseError for the whole
docstring, carrying every problem and the result. Its message is the
first problem's, with how many more there are.

In auto mode try_parse() keeps a style that reads the docstring with
no problems, so it returns what parse() returns whenever parse() does.
@rr- rr- self-assigned this Nov 6, 2026
@rr-
rr- merged commit 6c6a19f into main Nov 6, 2026
51 of 52 checks passed
@rr-
rr- deleted the rework/16-try-parse 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