Repository navigation
Conversation
Add a new parsing and composing implementation in docstring_parser/_core, enabled with legacy=False on parse, parse_from_object, combine_docstrings and the style specific parsers. All styles share the same rules for descriptions, types, optional markers, defaults, canonical item keys, attributes, deprecations and examples. No text is silently dropped, malformed docstrings do not raise, and composing round-trips. The rules and differences from legacy are documented in BEHAVIOR.md. This is meant to become the default in the next major version. The new implementation requires Python 3.10 or newer and raises RuntimeError on older versions, which keep the legacy behavior. The default legacy=True keeps the previous behavior, now pinned by golden tests over a corpus of 3,180 real-world docstrings. Also add Docstring.attributes, DocstringParam.is_attribute and Docstring.legacy, property-based tests, and scripts to build the corpus and review golden differences. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
As mentioned in #119, the point of this is to illustrate ideas. Common rules, common code, corpus of real data to understand impact of changes, opt-in for new parsers, etc. |
|
Sorry, I started to separately work on this, and landed corpus data along with a couple bug fixes. Will review the approach. |
|
Thanks for this. I would like to land this in some form, but not in its current shape. Before reviewing it line by line, I think we should agree on the larger design questions.
This is the first thing I would change. A flag named after the old behavior leaves us maintaining two complete parsers until the next major version, with every function signature carrying I would rather have one parser. Where its output differs from today’s behavior, we should either describe that as a fix or make it an explicit breaking change in 1.0, which I'd release sooner than later. If a transition period is necessary, it should use a name that describes the behavior being selected, rather than one that describes what is being avoided. Strict parsing Never raising is useful, but it should not be the only mode. Silently accepting malformed docstrings hides exactly the problems people use a parser to detect. I would like strict parsing to be the default, with Python 3.10+ I do not see a reason to require Python 3.10. Python 3.8 is past end of life, but the package supports it today, and nothing in Custom sections Looking back at what people have asked for, extensibility has mostly been about sections, not about overriding parser internals:
This PR covers part of that: custom section lists are carried over, attributes are separate, and the napoleon sections are recognized. What I think the new approach should allow:
Speed Using the PR’s own corpus on Python 3.12,
The target I would like to hold is no more than roughly 10% slower than the current implementation, measured in CI against the base commit. Numpydoc and Epydoc are already within that range, but Google is not yet. Relation to #137 #137, now merged, adds a snapshot corpus of 1,100 docstrings, together with line-ending and round-trip checks for every case. It also fixes the legacy bugs those checks found. I went with one diff-able Since it touches the same modules, this PR needs a rebase onto it, and the legacy golden files need to be regenerated. Two corpora are unnecessary, so we should either choose one or merge them rather than keep both. (Sorry!) The review also found three smaller bugs, all in
My proposed next step is to agree on the overall shape on each of the above sections first. Once that is settled, the core of this PR can be reworked toward it, but ideally in smaller pieces rather than as one 20,000-line change. Happy to work on it myself too. |
A
Sounds good.
Fine by me want to continue supporting 3.8 and 3.9. It should be very simple to make the new code run on those versions.
No comment.
Good having that target. The speed is already good, given that the new parser has the added guarantees, which naturally requires more processing.
Certainly, no need to have two corpora. What is important is having a corpus, to notice on real data what happens as the code changes.
No problem from my side. This pull request was just to illustrate ideas. You can do with this as you see fit. If it is already helpful for you, then great. I think it would be good if you look in detail at BEHAVIOR.md (excluding the legacy boolean part). Better to clearly document what is the expected behavior of the parser: guarantees, common rules, etc. That way it can be better understood whether some behavior in the code is a bug or intended. |
|
Thanks, this shaped a lot of what landed since. Instead of a second parser behind The goals of #136 on
Differences from #136: no Since the first draft of this reply, Here is where each item of BEHAVIOR.md stands. Rows 1 to 24 follow its "Differences from legacy, at a glance" table, with the
Still open besides these: a behaviour doc like the BEHAVIOR.md in #136, and a review of it would be very welcome. I'd close this PR, if that's OK. |
|
Sounds great, thanks! I am on vacation for a couple of weeks, so I can't really look at the details now. Fine by me if you close it. No need for it to be open for me to comment later. |
Adds a new parsing and composing implementation in
docstring_parser/_core, enabled withlegacy=Falseonparse,parse_from_object,combine_docstringsand the style-specific parsers. The default is unchanged. The rules and the differences from legacy are inBEHAVIOR.md.Why a new parser
Args:and aNote:orSee Also:afterArgs:disappear without warning. A summary wrapped over two lines is cut in half.:param Dict[str, int] x:and a bareValueErrorunderRaises:raiseParseError.argssometimes holds the type (["param", "x (int)"]), attributes are mixed intoparams, and a missing description can be""orNone.legacy=FalseParseErrorWhy not fix the legacy parsers
main, mostly to compose, changed legacy results for 1,340 of the 3,180 corpus docstrings. Fixing everything above would change far more, in a minor release, with no way to opt out.legacy=Falsewhen ready, and it can become the default in the next major version.What
legacy=Falseguaranteesparse(compose(d))gives backdin every rendering style, for docstrings parsed in that style. The one known exception is inBEHAVIOR.md.Docstringclasses stay the same, withDocstring.attributes,DocstringParam.is_attributeandDocstring.legacyadded.Compatibility
legacy=Truestays the default, and its output is identical tomainon the whole corpus.legacy=Falserequires Python 3.10+ and raisesRuntimeErroron older versions. The package still supports 3.8+, because_coreis only imported whenlegacy=Falseis used.Testing
docstring_parser/tests/corpus/).scripts/build_corpus.pyrebuilds the corpus andscripts/golden_diff.pyreviews changes.hypothesistest dependency) check that onlyParseErroris raised and that composing round-trips.Reviewing
The diff is large (about 21,000 lines), but most of it is corpus data. The code to review is
_core(about 2,000 lines), plus smalllegacyhooks in the existing modules.🤖 Generated with Claude Code