gh-90117: handle dict and mapping views in pprint (#30135)

* Teach pprint about dict views with PrettyPrinter._pprint_dict_view and ._pprint_dict_items_view.
* Use _private names for _dict_*_view attributes of PrettyPrinter.
* Use explicit 'items' keyword when calling _pprint_dict_view from _pprint_dict_items_view.
* 📜🤖 Added by blurb_it.
* Improve tests
* Add tests for collections.abc.[Keys|Items|Mapping|Values]View support in pprint.
* Add support for collections.abc.[Keys|Items|Mapping|Values]View in pprint.
* Split _pprint_dict_view into _pprint_abc_view, so pretty-printing normal dict views and ABC views is handled in two simple methods.
* Simplify redundant code.
* Add collections.abc views to some existing pprint tests.
* Test that views from collection.UserDict are correctly formatted by pprint.
* Handle recursive dict and ABC views.
* Test that subclasses of ABC views work in pprint.
* Test dict views coming from collections.Counter.
* Test ABC views coming from collections.ChainMap.
* Test odict views coming from collections.OrderedDict.
* Rename _pprint_abc_view to _pprint_mapping_abc_view.
* Add pprint test for mapping ABC views where ._mapping has a custom __repr__ and fix ChainMap test.
* When a mapping ABC view has a ._mapping that defines a custom __repr__, dispatch pretty-printing it by that __repr__.
* Add tests for ABC mapping views subclasses that don't replace __repr__, also handling those that delete ._mapping on instances.
* Simplify the pretty printing of ABC mapping views.
* Add a test for depth handling when pretty printing dict views.
* Fix checking whether the view type is a subclass of an items view, add a test.
* Move construction of the views __repr__ set out of _safe_repr.

---------

Co-authored-by: blurb-it[bot] <43283697+blurb-it[bot]@users.noreply.github.com>
Co-authored-by: Éric <merwok@netwok.org>
Co-authored-by: Oleg Iarygin <oleg@arhadthedev.net>
Co-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com>
Co-authored-by: Gregory P. Smith <greg@krypto.org>
This commit is contained in:
devdanzin 2025-05-20 16:30:00 -03:00 committed by GitHub
parent 91e6a58e2d
commit c7f8e706e1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 424 additions and 2 deletions

View file

@ -248,6 +248,49 @@ class PrettyPrinter:
_dispatch[_collections.OrderedDict.__repr__] = _pprint_ordered_dict
def _pprint_dict_view(self, object, stream, indent, allowance, context, level):
"""Pretty print dict views (keys, values, items)."""
if isinstance(object, self._dict_items_view):
key = _safe_tuple
else:
key = _safe_key
write = stream.write
write(object.__class__.__name__ + '([')
if self._indent_per_level > 1:
write((self._indent_per_level - 1) * ' ')
length = len(object)
if length:
if self._sort_dicts:
entries = sorted(object, key=key)
else:
entries = object
self._format_items(entries, stream, indent, allowance + 1,
context, level)
write('])')
def _pprint_mapping_abc_view(self, object, stream, indent, allowance, context, level):
"""Pretty print mapping views from collections.abc."""
write = stream.write
write(object.__class__.__name__ + '(')
# Dispatch formatting to the view's _mapping
self._format(object._mapping, stream, indent, allowance, context, level)
write(')')
_dict_keys_view = type({}.keys())
_dispatch[_dict_keys_view.__repr__] = _pprint_dict_view
_dict_values_view = type({}.values())
_dispatch[_dict_values_view.__repr__] = _pprint_dict_view
_dict_items_view = type({}.items())
_dispatch[_dict_items_view.__repr__] = _pprint_dict_view
_dispatch[_collections.abc.MappingView.__repr__] = _pprint_mapping_abc_view
_view_reprs = {cls.__repr__ for cls in
(_dict_keys_view, _dict_values_view, _dict_items_view,
_collections.abc.MappingView)}
def _pprint_list(self, object, stream, indent, allowance, context, level):
stream.write('[')
self._format_items(object, stream, indent, allowance + 1,
@ -610,6 +653,42 @@ class PrettyPrinter:
del context[objid]
return "{%s}" % ", ".join(components), readable, recursive
if issubclass(typ, _collections.abc.MappingView) and r in self._view_reprs:
objid = id(object)
if maxlevels and level >= maxlevels:
return "{...}", False, objid in context
if objid in context:
return _recursion(object), False, True
key = _safe_key
if issubclass(typ, (self._dict_items_view, _collections.abc.ItemsView)):
key = _safe_tuple
if hasattr(object, "_mapping"):
# Dispatch formatting to the view's _mapping
mapping_repr, readable, recursive = self.format(
object._mapping, context, maxlevels, level)
return (typ.__name__ + '(%s)' % mapping_repr), readable, recursive
elif hasattr(typ, "_mapping"):
# We have a view that somehow has lost its type's _mapping, raise
# an error by calling repr() instead of failing cryptically later
return repr(object), True, False
if self._sort_dicts:
object = sorted(object, key=key)
context[objid] = 1
readable = True
recursive = False
components = []
append = components.append
level += 1
for val in object:
vrepr, vreadable, vrecur = self.format(
val, context, maxlevels, level)
append(vrepr)
readable = readable and vreadable
if vrecur:
recursive = True
del context[objid]
return typ.__name__ + '([%s])' % ", ".join(components), readable, recursive
if (issubclass(typ, list) and r is list.__repr__) or \
(issubclass(typ, tuple) and r is tuple.__repr__):
if issubclass(typ, list):