Repository navigation
gh-145153: Implement PEP 813 pprint protocol - #161647
Draft
ericvsmith wants to merge 22 commits into
Draft
ericvsmith wants to merge 22 commits into
ericvsmith wants to merge 22 commits into
Conversation
collections replaces its pure-Python OrderedDict with the C version from _collections, so the __pprint__() added to the Python class never applied. The C class defines its own __repr__ and only inherited dict.__pprint__, so describe() fell back to repr() and test__pprint failed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The built-in containers now have __pprint__() methods, so the example uses
plain dicts, lists and frozensets instead of subclasses that add one. It
prints with f'{obj!p:{Printer(**options)}}' and imports Printer and Expand
from _pprint, dropping its private copy of the vocabulary, describe() and
the printer. That copy couldn't read the _pprint objects the built-ins
describe themselves with.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
PEP 813: use built-in __pprint__ in the example, fix OrderedDict
…eaking functionality available for user types.
* Restore the public PyObject_Pretty() and have it join the lines.
PEP 813 specifies PyObject_Pretty() in the limited C API. It calls the
printer (the default one when printer is NULL), joins the lines it returns
with '\n', and returns the str, mirroring PyObject_Str() and friends. It
raises TypeError if the printer returns a str, whose items are characters
rather than lines, or if any line is not a str.
FORMAT_PRETTY and str.format() now call it, so the join and its checks live
in one place.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Don't generate a dataclass __pprint__() when the class defines __repr__().
Both methods would be defined on the same class, so the MRO rule would use
the generated __pprint__(), which describes every field, instead of the
hand-written __repr__(), which may hide some of them.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Bring _pprint in line with the PEP 813 draft.
* Re-export the protocol's names from pprint, lazily, so that importing
pprint stays cheap.
* Add pprint.LEAF, which decompose() returns for a leaf, and
pprint.DescriptionType.
* Add pprint.FormattableNumber. int and float delegate to it, and the
Printer formats it with its new number_format option, a format spec.
If format() raises, the number is printed with repr().
* Add a Layout.hug attribute. When exactly one item is a container, the
other items stay on the opening and closing lines, as PrettyPrinter does
with expand=True. defaultdict, mappingproxy, the MappingViews and deque
use it.
* Split strings and bytes when rendering, so that the split depends on the
column, as in PrettyPrinter. Only a literal that is the whole object is
parenthesized. The continuation lines of a nested literal are aligned
under its first piece.
* Match PrettyPrinter for bytearray and deque, print OrderedDict as
OrderedDict({...}), use '(...)' for tuple recursion, and describe an
Interpolation positionally, to agree with repr().
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Regenerate Programs/test_frozenmain.h for the new FORMAT_PRETTY opcode.
This fixes test_embed's test_frozenmain.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Render the default markers through a dispatch table in the Printer. The Printer now looks up each marker in a table that maps a marker class to the function that renders it, with BreakableString, BreakableBytes and FormattableNumber as the default entries. This is the design of PEP 813's deferred idea of a user-supplied marker table, without the public option. A rendering function is called as render(marker, *, width, last_width, top_level) and returns an iterable of lines. width is the room for each line from the column where the marker starts, and last_width is the room for the last line, which leaves space for any text that follows. The printer places the first line at the current position and the rest at the marker's starting column. With unlimited width a function returns the marker's one-line form, which is what the printer measures when it decides whether a container fits. A marker that isn't in the table is printed with repr(). The markers are now plain leaves in the structural document, as decompose() reports. Since a dataclass that generates __repr__() now also generates __pprint__(), the marker classes define __repr__() themselves so that they stay leaves. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Have string.templatelib.convert() pretty print the way !p does. convert(obj, 'p', printer) joined the printer's lines itself, so it skipped the checks that PyObject_Pretty() makes. A printer that returned a str gave one line per character instead of raising TypeError. convert() now evaluates the same f-string that the template would, so both share one path. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Fix PEP 813 printer regressions after the C migration * Use hanging indentation for leaf marker name lookup
… module (#5) * PEP 813: sort keyword items last, and treat a None printer as the default Sorting a sortable layout that contains keyword items failed with TypeError. Keyword items are now sorted by name after the other items, so the output stays in call order, as in Example('y', 'z', a=10, b=0). PyObject_Pretty() now treats a printer of None like NULL and uses the default printer. So f'{obj!p:{None}}' matches f'{obj!p}' and string.templatelib.convert(obj, 'p', None), where None already meant that no printer was given. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Move the PEP 813 protocol into _pprint_protocol and break the import cycles The protocol code lived inside _load_protocol(), which a module __getattr__ ran on first use and then copied into the module globals. It now lives at module level in a new private module, _pprint_protocol, and pprint re-exports its names with lazy imports. Importing pprint alone still does not import dataclasses or the protocol module. The __pprint__() methods in collections, _collections_abc and dataclasses imported helpers from pprint inside the function to avoid import cycles. The helpers are now inlined into those methods, and the names they need come from the C _pprint module with lazy imports, so there is no cycle even when lazy imports are forced eager. pprint's own function-local imports of dataclasses and re are now lazy imports at the top of the module. string.templatelib.convert() no longer special-cases a printer of None, since PyObject_Pretty() handles it. Regenerate stdlib_module_names.h, which also adds the missing _pprint entry. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…very mode (#6) A hugged document was always wrapped in a group, so with Expand.ALWAYS a container nested in a hugging object such as a deque could stay on one line. The hugged document now drops its group below the top level, as other containers do with ALWAYS. compact was used only with Expand.NEVER, and was silently ignored with FIT and ALWAYS. It now packs positional items in every mode, using PrettyPrinter's rule: an item whose one-line form fits shares lines with its packable neighbors, and any other item gets lines of its own. With ALWAYS, a non-empty container is never packed. With NEVER, this also fixes a broken container starting partway along a line, so the output now matches PrettyPrinter's, except that a packed line can end at the last column of the width. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
ericvsmith
requested review from
AA-Turner,
ZeroIntensity,
ericsnowcurrently,
iritkatriel,
lysnikolaou,
markshannon,
pablogsal and
rhettinger
as code owners
November 9, 2026 01:52
ericvsmith
requested review from
a team,
corona10,
emmatyping,
encukou,
erlend-aasland,
itamaro,
methane and
willingc
as code owners
November 9, 2026 01:52
ericvsmith
marked this pull request as draft
November 9, 2026 01:53
Member
Author
|
I marked this as draft because the last thing I did at the sprint was have Codex convert the builtin type's |
Member
Author
|
This code was originally largely written by Codex, but I spent a lot of time reviewing it, redesigning parts of it, and fixing various errors. As mentioned, I still need to make a pass at the dunder pprint methods that are included. |
rhettinger
removed their request for review
November 9, 2026 06:02
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.