mirror of
https://github.com/python/cpython.git
synced 2025-10-09 16:34:44 +00:00
Issue 3976: fix pprint for sets, frozensets, and dicts containing unorderable types.
This commit is contained in:
parent
df961cfbfa
commit
a7da1663ec
2 changed files with 49 additions and 11 deletions
|
@ -70,6 +70,32 @@ def isrecursive(object):
|
||||||
"""Determine if object requires a recursive representation."""
|
"""Determine if object requires a recursive representation."""
|
||||||
return _safe_repr(object, {}, None, 0)[2]
|
return _safe_repr(object, {}, None, 0)[2]
|
||||||
|
|
||||||
|
class _safe_key:
|
||||||
|
"""Helper function for key functions when sorting unorderable objects.
|
||||||
|
|
||||||
|
The wrapped-object will fallback to an Py2.x style comparison for
|
||||||
|
unorderable types (sorting first comparing the type name and then by
|
||||||
|
the obj ids). Does not work recursively, so dict.items() must have
|
||||||
|
_safe_key applied to both the key and the value.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
__slots__ = ['obj']
|
||||||
|
|
||||||
|
def __init__(self, obj):
|
||||||
|
self.obj = obj
|
||||||
|
|
||||||
|
def __lt__(self, other):
|
||||||
|
rv = self.obj.__lt__(other.obj)
|
||||||
|
if rv is NotImplemented:
|
||||||
|
rv = (str(type(self.obj)), id(self.obj)) < \
|
||||||
|
(str(type(other.obj)), id(other.obj))
|
||||||
|
return rv
|
||||||
|
|
||||||
|
def _safe_tuple(t):
|
||||||
|
"Helper function for comparing 2-tuples"
|
||||||
|
return _safe_key(t[0]), _safe_key(t[1])
|
||||||
|
|
||||||
class PrettyPrinter:
|
class PrettyPrinter:
|
||||||
def __init__(self, indent=1, width=80, depth=None, stream=None):
|
def __init__(self, indent=1, width=80, depth=None, stream=None):
|
||||||
"""Handle pretty printing operations onto a stream using a set of
|
"""Handle pretty printing operations onto a stream using a set of
|
||||||
|
@ -145,7 +171,7 @@ class PrettyPrinter:
|
||||||
if length:
|
if length:
|
||||||
context[objid] = 1
|
context[objid] = 1
|
||||||
indent = indent + self._indent_per_level
|
indent = indent + self._indent_per_level
|
||||||
items = sorted(object.items())
|
items = sorted(object.items(), key=_safe_tuple)
|
||||||
key, ent = items[0]
|
key, ent = items[0]
|
||||||
rep = self._repr(key, context, level)
|
rep = self._repr(key, context, level)
|
||||||
write(rep)
|
write(rep)
|
||||||
|
@ -178,14 +204,14 @@ class PrettyPrinter:
|
||||||
return
|
return
|
||||||
write('{')
|
write('{')
|
||||||
endchar = '}'
|
endchar = '}'
|
||||||
object = sorted(object)
|
object = sorted(object, key=_safe_key)
|
||||||
elif issubclass(typ, frozenset):
|
elif issubclass(typ, frozenset):
|
||||||
if not length:
|
if not length:
|
||||||
write('frozenset()')
|
write('frozenset()')
|
||||||
return
|
return
|
||||||
write('frozenset({')
|
write('frozenset({')
|
||||||
endchar = '})'
|
endchar = '})'
|
||||||
object = sorted(object)
|
object = sorted(object, key=_safe_key)
|
||||||
indent += 10
|
indent += 10
|
||||||
else:
|
else:
|
||||||
write('(')
|
write('(')
|
||||||
|
@ -267,14 +293,7 @@ def _safe_repr(object, context, maxlevels, level):
|
||||||
append = components.append
|
append = components.append
|
||||||
level += 1
|
level += 1
|
||||||
saferepr = _safe_repr
|
saferepr = _safe_repr
|
||||||
items = object.items()
|
items = sorted(object.items(), key=_safe_tuple)
|
||||||
try:
|
|
||||||
items = sorted(items)
|
|
||||||
except TypeError:
|
|
||||||
def sortkey(item):
|
|
||||||
key, value = item
|
|
||||||
return str(type(key)), key, value
|
|
||||||
items = sorted(items, key=sortkey)
|
|
||||||
for k, v in items:
|
for k, v in items:
|
||||||
krepr, kreadable, krecur = saferepr(k, context, maxlevels, level)
|
krepr, kreadable, krecur = saferepr(k, context, maxlevels, level)
|
||||||
vrepr, vreadable, vrecur = saferepr(v, context, maxlevels, level)
|
vrepr, vreadable, vrecur = saferepr(v, context, maxlevels, level)
|
||||||
|
|
|
@ -2,6 +2,7 @@ import pprint
|
||||||
import test.support
|
import test.support
|
||||||
import unittest
|
import unittest
|
||||||
import test.test_set
|
import test.test_set
|
||||||
|
import random
|
||||||
|
|
||||||
# list, tuple and dict subclasses that do or don't overwrite __repr__
|
# list, tuple and dict subclasses that do or don't overwrite __repr__
|
||||||
class list2(list):
|
class list2(list):
|
||||||
|
@ -25,6 +26,10 @@ class dict3(dict):
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return dict.__repr__(self)
|
return dict.__repr__(self)
|
||||||
|
|
||||||
|
class Unorderable:
|
||||||
|
def __repr__(self):
|
||||||
|
return str(id(self))
|
||||||
|
|
||||||
class QueryTestCase(unittest.TestCase):
|
class QueryTestCase(unittest.TestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
@ -407,6 +412,20 @@ class QueryTestCase(unittest.TestCase):
|
||||||
self.assertEqual(pprint.pformat(nested_dict, depth=1), lv1_dict)
|
self.assertEqual(pprint.pformat(nested_dict, depth=1), lv1_dict)
|
||||||
self.assertEqual(pprint.pformat(nested_list, depth=1), lv1_list)
|
self.assertEqual(pprint.pformat(nested_list, depth=1), lv1_list)
|
||||||
|
|
||||||
|
def test_sort_unorderable_values(self):
|
||||||
|
# Issue 3976: sorted pprints fail for unorderable values.
|
||||||
|
n = 20
|
||||||
|
keys = [Unorderable() for i in range(n)]
|
||||||
|
random.shuffle(keys)
|
||||||
|
skeys = sorted(keys, key=id)
|
||||||
|
clean = lambda s: s.replace(' ', '').replace('\n','')
|
||||||
|
|
||||||
|
self.assertEqual(clean(pprint.pformat(set(keys))),
|
||||||
|
'{' + ','.join(map(repr, skeys)) + '}')
|
||||||
|
self.assertEqual(clean(pprint.pformat(frozenset(keys))),
|
||||||
|
'frozenset({' + ','.join(map(repr, skeys)) + '})')
|
||||||
|
self.assertEqual(clean(pprint.pformat(dict.fromkeys(keys))),
|
||||||
|
'{' + ','.join('%r:None' % k for k in skeys) + '}')
|
||||||
|
|
||||||
class DottedPrettyPrinter(pprint.PrettyPrinter):
|
class DottedPrettyPrinter(pprint.PrettyPrinter):
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue