Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 30 additions & 4 deletions Lib/pprint.py
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,20 @@ def _pprint_dict(self, object, stream, indent, allowance, context, level):

_dispatch[dict.__repr__] = _pprint_dict

def _pprint_frozendict(self, object, stream, indent, allowance, context, level):
write = stream.write
cls = object.__class__
stream.write(cls.__name__ + '(')
length = len(object)
if length:
self._pprint_dict(object, stream,
indent + len(cls.__name__) + 1,
allowance + 1,
context, level)
write(')')

_dispatch[frozendict.__repr__] = _pprint_frozendict

def _pprint_ordered_dict(self, object, stream, indent, allowance, context, level):
if not len(object):
stream.write(repr(object))
Expand Down Expand Up @@ -623,12 +637,21 @@ def _safe_repr(self, object, context, maxlevels, level):
else:
return repr(object), True, False

if issubclass(typ, dict) and r is dict.__repr__:
if ((issubclass(typ, dict) and r is dict.__repr__)
or (issubclass(typ, frozendict) and r is frozendict.__repr__)):
is_frozendict = issubclass(typ, frozendict)
if not object:
return "{}", True, False
if is_frozendict:
rep = f"{object.__class__.__name__}()"
else:
rep = "{}"
return rep, True, False
objid = id(object)
if maxlevels and level >= maxlevels:
return "{...}", False, objid in context
rep = "{...}"
if is_frozendict:
rep = f"{object.__class__.__name__}({rep})"
return rep, False, objid in context
if objid in context:
return _recursion(object), False, True
context[objid] = 1
Expand All @@ -651,7 +674,10 @@ def _safe_repr(self, object, context, maxlevels, level):
if krecur or vrecur:
recursive = True
del context[objid]
return "{%s}" % ", ".join(components), readable, recursive
rep = "{%s}" % ", ".join(components)
if is_frozendict:
rep = f"{object.__class__.__name__}({rep})"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it covered by tests?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line is coverted by test_basic_line_wrap() and test_same_as_repr() tests. If I modify the code to raise an exception, both tests fail.

return rep, readable, recursive

if (issubclass(typ, list) and r is list.__repr__) or \
(issubclass(typ, tuple) and r is tuple.__repr__):
Expand Down
40 changes: 38 additions & 2 deletions Lib/test/test_pprint.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,13 @@ class dict3(dict):
def __repr__(self):
return dict.__repr__(self)

class frozendict2(frozendict):
pass

class frozendict3(frozendict):
def __repr__(self):
return frozendict.__repr__(self)

class dict_custom_repr(dict):
def __repr__(self):
return '*'*len(dict.__repr__(self))
Expand Down Expand Up @@ -254,18 +261,22 @@ def test_same_as_repr(self):
set(), set2(), set3(),
frozenset(), frozenset2(), frozenset3(),
{}, dict2(), dict3(),
frozendict(), frozendict2(), frozendict3(),
{}.keys(), {}.values(), {}.items(),
MappingView({}), KeysView({}), ItemsView({}), ValuesView({}),
self.assertTrue, pprint,
-6, -6, -6-6j, -1.5, "x", b"x", bytearray(b"x"),
(3,), [3], {3: 6},
(1,2), [3,4], {5: 6},
(1,2), [3,4],
tuple2((1,2)), tuple3((1,2)), tuple3(range(100)),
[3,4], list2([3,4]), list3([3,4]), list3(range(100)),
set({7}), set2({7}), set3({7}),
frozenset({8}), frozenset2({8}), frozenset3({8}),
dict2({5: 6}), dict3({5: 6}),
{5: 6}, dict2({5: 6}), dict3({5: 6}),
frozendict({5: 6}), frozendict2({5: 6}), frozendict3({5: 6}),
{5: 6}.keys(), {5: 6}.values(), {5: 6}.items(),
frozendict({5: 6}).keys(), frozendict({5: 6}).values(),
frozendict({5: 6}).items(),
MappingView({5: 6}), KeysView({5: 6}),
ItemsView({5: 6}), ValuesView({5: 6}),
range(10, -11, -1),
Expand Down Expand Up @@ -330,20 +341,45 @@ def test_basic_line_wrap(self):
for type in [dict, dict2]:
self.assertEqual(pprint.pformat(type(o)), exp)

exp = """\
frozendict({'RPM_cal': 0,
'RPM_cal2': 48059,
'Speed_cal': 0,
'controldesk_runtime_us': 0,
'main_code_runtime_us': 0,
'read_io_runtime_us': 0,
'write_io_runtime_us': 43690})"""
self.assertEqual(pprint.pformat(frozendict(o)), exp)
exp = """\
frozendict2({'RPM_cal': 0,
'RPM_cal2': 48059,
'Speed_cal': 0,
'controldesk_runtime_us': 0,
'main_code_runtime_us': 0,
'read_io_runtime_us': 0,
'write_io_runtime_us': 43690})"""
self.assertEqual(pprint.pformat(frozendict2(o)), exp)

o = range(100)
exp = 'dict_keys([%s])' % ',\n '.join(map(str, o))
keys = dict.fromkeys(o).keys()
self.assertEqual(pprint.pformat(keys), exp)
keys = frozendict.fromkeys(o).keys()
self.assertEqual(pprint.pformat(keys), exp)

o = range(100)
exp = 'dict_values([%s])' % ',\n '.join(map(str, o))
values = {v: v for v in o}.values()
self.assertEqual(pprint.pformat(values), exp)
values = frozendict({v: v for v in o}).values()
self.assertEqual(pprint.pformat(values), exp)

o = range(100)
exp = 'dict_items([%s])' % ',\n '.join("(%s, %s)" % (i, i) for i in o)
items = {v: v for v in o}.items()
self.assertEqual(pprint.pformat(items), exp)
items = frozendict({v: v for v in o}).items()
self.assertEqual(pprint.pformat(items), exp)

o = range(100)
exp = 'odict_keys([%s])' % ',\n '.join(map(str, o))
Expand Down
Loading