bpo-28850: Fix PrettyPrinter.format overrides ignored for contents of small containers (GH-22120)
This commit is contained in:
parent
dd844a2916
commit
ff420f0e08
148
Lib/pprint.py
148
Lib/pprint.py
|
@ -64,15 +64,15 @@ def pp(object, *args, sort_dicts=False, **kwargs):
|
||||||
|
|
||||||
def saferepr(object):
|
def saferepr(object):
|
||||||
"""Version of repr() which can handle recursive data structures."""
|
"""Version of repr() which can handle recursive data structures."""
|
||||||
return _safe_repr(object, {}, None, 0, True)[0]
|
return PrettyPrinter()._safe_repr(object, {}, None, 0)[0]
|
||||||
|
|
||||||
def isreadable(object):
|
def isreadable(object):
|
||||||
"""Determine if saferepr(object) is readable by eval()."""
|
"""Determine if saferepr(object) is readable by eval()."""
|
||||||
return _safe_repr(object, {}, None, 0, True)[1]
|
return PrettyPrinter()._safe_repr(object, {}, None, 0)[1]
|
||||||
|
|
||||||
def isrecursive(object):
|
def isrecursive(object):
|
||||||
"""Determine if object requires a recursive representation."""
|
"""Determine if object requires a recursive representation."""
|
||||||
return _safe_repr(object, {}, None, 0, True)[2]
|
return PrettyPrinter()._safe_repr(object, {}, None, 0)[2]
|
||||||
|
|
||||||
class _safe_key:
|
class _safe_key:
|
||||||
"""Helper function for key functions when sorting unorderable objects.
|
"""Helper function for key functions when sorting unorderable objects.
|
||||||
|
@ -435,7 +435,7 @@ class PrettyPrinter:
|
||||||
and flags indicating whether the representation is 'readable'
|
and flags indicating whether the representation is 'readable'
|
||||||
and whether the object represents a recursive construct.
|
and whether the object represents a recursive construct.
|
||||||
"""
|
"""
|
||||||
return _safe_repr(object, context, maxlevels, level, self._sort_dicts)
|
return self._safe_repr(object, context, maxlevels, level)
|
||||||
|
|
||||||
def _pprint_default_dict(self, object, stream, indent, allowance, context, level):
|
def _pprint_default_dict(self, object, stream, indent, allowance, context, level):
|
||||||
if not len(object):
|
if not len(object):
|
||||||
|
@ -518,77 +518,79 @@ class PrettyPrinter:
|
||||||
|
|
||||||
_dispatch[_collections.UserString.__repr__] = _pprint_user_string
|
_dispatch[_collections.UserString.__repr__] = _pprint_user_string
|
||||||
|
|
||||||
# Return triple (repr_string, isreadable, isrecursive).
|
def _safe_repr(self, object, context, maxlevels, level):
|
||||||
|
# Return triple (repr_string, isreadable, isrecursive).
|
||||||
|
typ = type(object)
|
||||||
|
if typ in _builtin_scalars:
|
||||||
|
return repr(object), True, False
|
||||||
|
|
||||||
def _safe_repr(object, context, maxlevels, level, sort_dicts):
|
r = getattr(typ, "__repr__", None)
|
||||||
typ = type(object)
|
if issubclass(typ, dict) and r is dict.__repr__:
|
||||||
if typ in _builtin_scalars:
|
|
||||||
return repr(object), True, False
|
|
||||||
|
|
||||||
r = getattr(typ, "__repr__", None)
|
|
||||||
if issubclass(typ, dict) and r is dict.__repr__:
|
|
||||||
if not object:
|
|
||||||
return "{}", True, False
|
|
||||||
objid = id(object)
|
|
||||||
if maxlevels and level >= maxlevels:
|
|
||||||
return "{...}", False, objid in context
|
|
||||||
if objid in context:
|
|
||||||
return _recursion(object), False, True
|
|
||||||
context[objid] = 1
|
|
||||||
readable = True
|
|
||||||
recursive = False
|
|
||||||
components = []
|
|
||||||
append = components.append
|
|
||||||
level += 1
|
|
||||||
if sort_dicts:
|
|
||||||
items = sorted(object.items(), key=_safe_tuple)
|
|
||||||
else:
|
|
||||||
items = object.items()
|
|
||||||
for k, v in items:
|
|
||||||
krepr, kreadable, krecur = _safe_repr(k, context, maxlevels, level, sort_dicts)
|
|
||||||
vrepr, vreadable, vrecur = _safe_repr(v, context, maxlevels, level, sort_dicts)
|
|
||||||
append("%s: %s" % (krepr, vrepr))
|
|
||||||
readable = readable and kreadable and vreadable
|
|
||||||
if krecur or vrecur:
|
|
||||||
recursive = True
|
|
||||||
del context[objid]
|
|
||||||
return "{%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):
|
|
||||||
if not object:
|
if not object:
|
||||||
return "[]", True, False
|
return "{}", True, False
|
||||||
format = "[%s]"
|
objid = id(object)
|
||||||
elif len(object) == 1:
|
if maxlevels and level >= maxlevels:
|
||||||
format = "(%s,)"
|
return "{...}", False, objid in context
|
||||||
else:
|
if objid in context:
|
||||||
if not object:
|
return _recursion(object), False, True
|
||||||
return "()", True, False
|
context[objid] = 1
|
||||||
format = "(%s)"
|
readable = True
|
||||||
objid = id(object)
|
recursive = False
|
||||||
if maxlevels and level >= maxlevels:
|
components = []
|
||||||
return format % "...", False, objid in context
|
append = components.append
|
||||||
if objid in context:
|
level += 1
|
||||||
return _recursion(object), False, True
|
if self._sort_dicts:
|
||||||
context[objid] = 1
|
items = sorted(object.items(), key=_safe_tuple)
|
||||||
readable = True
|
else:
|
||||||
recursive = False
|
items = object.items()
|
||||||
components = []
|
for k, v in items:
|
||||||
append = components.append
|
krepr, kreadable, krecur = self.format(
|
||||||
level += 1
|
k, context, maxlevels, level)
|
||||||
for o in object:
|
vrepr, vreadable, vrecur = self.format(
|
||||||
orepr, oreadable, orecur = _safe_repr(o, context, maxlevels, level, sort_dicts)
|
v, context, maxlevels, level)
|
||||||
append(orepr)
|
append("%s: %s" % (krepr, vrepr))
|
||||||
if not oreadable:
|
readable = readable and kreadable and vreadable
|
||||||
readable = False
|
if krecur or vrecur:
|
||||||
if orecur:
|
recursive = True
|
||||||
recursive = True
|
del context[objid]
|
||||||
del context[objid]
|
return "{%s}" % ", ".join(components), readable, recursive
|
||||||
return format % ", ".join(components), readable, recursive
|
|
||||||
|
|
||||||
rep = repr(object)
|
if (issubclass(typ, list) and r is list.__repr__) or \
|
||||||
return rep, (rep and not rep.startswith('<')), False
|
(issubclass(typ, tuple) and r is tuple.__repr__):
|
||||||
|
if issubclass(typ, list):
|
||||||
|
if not object:
|
||||||
|
return "[]", True, False
|
||||||
|
format = "[%s]"
|
||||||
|
elif len(object) == 1:
|
||||||
|
format = "(%s,)"
|
||||||
|
else:
|
||||||
|
if not object:
|
||||||
|
return "()", True, False
|
||||||
|
format = "(%s)"
|
||||||
|
objid = id(object)
|
||||||
|
if maxlevels and level >= maxlevels:
|
||||||
|
return format % "...", False, objid in context
|
||||||
|
if objid in context:
|
||||||
|
return _recursion(object), False, True
|
||||||
|
context[objid] = 1
|
||||||
|
readable = True
|
||||||
|
recursive = False
|
||||||
|
components = []
|
||||||
|
append = components.append
|
||||||
|
level += 1
|
||||||
|
for o in object:
|
||||||
|
orepr, oreadable, orecur = self.format(
|
||||||
|
o, context, maxlevels, level)
|
||||||
|
append(orepr)
|
||||||
|
if not oreadable:
|
||||||
|
readable = False
|
||||||
|
if orecur:
|
||||||
|
recursive = True
|
||||||
|
del context[objid]
|
||||||
|
return format % ", ".join(components), readable, recursive
|
||||||
|
|
||||||
|
rep = repr(object)
|
||||||
|
return rep, (rep and not rep.startswith('<')), False
|
||||||
|
|
||||||
_builtin_scalars = frozenset({str, bytes, bytearray, int, float, complex,
|
_builtin_scalars = frozenset({str, bytes, bytearray, int, float, complex,
|
||||||
bool, type(None)})
|
bool, type(None)})
|
||||||
|
@ -604,7 +606,7 @@ def _perfcheck(object=None):
|
||||||
object = [("string", (1, 2), [3, 4], {5: 6, 7: 8})] * 100000
|
object = [("string", (1, 2), [3, 4], {5: 6, 7: 8})] * 100000
|
||||||
p = PrettyPrinter()
|
p = PrettyPrinter()
|
||||||
t1 = time.perf_counter()
|
t1 = time.perf_counter()
|
||||||
_safe_repr(object, {}, None, 0, True)
|
p._safe_repr(object, {}, None, 0, True)
|
||||||
t2 = time.perf_counter()
|
t2 = time.perf_counter()
|
||||||
p.pformat(object)
|
p.pformat(object)
|
||||||
t3 = time.perf_counter()
|
t3 = time.perf_counter()
|
||||||
|
|
|
@ -453,12 +453,23 @@ AdvancedNamespace(the=0,
|
||||||
dog=8)""")
|
dog=8)""")
|
||||||
|
|
||||||
def test_subclassing(self):
|
def test_subclassing(self):
|
||||||
|
# length(repr(obj)) > width
|
||||||
o = {'names with spaces': 'should be presented using repr()',
|
o = {'names with spaces': 'should be presented using repr()',
|
||||||
'others.should.not.be': 'like.this'}
|
'others.should.not.be': 'like.this'}
|
||||||
exp = """\
|
exp = """\
|
||||||
{'names with spaces': 'should be presented using repr()',
|
{'names with spaces': 'should be presented using repr()',
|
||||||
others.should.not.be: like.this}"""
|
others.should.not.be: like.this}"""
|
||||||
self.assertEqual(DottedPrettyPrinter().pformat(o), exp)
|
|
||||||
|
dotted_printer = DottedPrettyPrinter()
|
||||||
|
self.assertEqual(dotted_printer.pformat(o), exp)
|
||||||
|
|
||||||
|
# length(repr(obj)) < width
|
||||||
|
o1 = ['with space']
|
||||||
|
exp1 = "['with space']"
|
||||||
|
self.assertEqual(dotted_printer.pformat(o1), exp1)
|
||||||
|
o2 = ['without.space']
|
||||||
|
exp2 = "[without.space]"
|
||||||
|
self.assertEqual(dotted_printer.pformat(o2), exp2)
|
||||||
|
|
||||||
def test_set_reprs(self):
|
def test_set_reprs(self):
|
||||||
self.assertEqual(pprint.pformat(set()), 'set()')
|
self.assertEqual(pprint.pformat(set()), 'set()')
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
Fix :meth:`pprint.PrettyPrinter.format` overrides being ignored for contents of small containers. The :func:`pprint._safe_repr` function was removed.
|
Loading…
Reference in New Issue