Issue 3976: fix pprint for sets, frozensets, and dicts containing unorderable types.

This commit is contained in:
Raymond Hettinger 2009-11-19 01:07:05 +00:00
parent df961cfbfa
commit a7da1663ec
2 changed files with 49 additions and 11 deletions

View File

@ -70,6 +70,32 @@ def isrecursive(object):
"""Determine if object requires a recursive representation."""
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:
def __init__(self, indent=1, width=80, depth=None, stream=None):
"""Handle pretty printing operations onto a stream using a set of
@ -145,7 +171,7 @@ class PrettyPrinter:
if length:
context[objid] = 1
indent = indent + self._indent_per_level
items = sorted(object.items())
items = sorted(object.items(), key=_safe_tuple)
key, ent = items[0]
rep = self._repr(key, context, level)
write(rep)
@ -178,14 +204,14 @@ class PrettyPrinter:
return
write('{')
endchar = '}'
object = sorted(object)
object = sorted(object, key=_safe_key)
elif issubclass(typ, frozenset):
if not length:
write('frozenset()')
return
write('frozenset({')
endchar = '})'
object = sorted(object)
object = sorted(object, key=_safe_key)
indent += 10
else:
write('(')
@ -267,14 +293,7 @@ def _safe_repr(object, context, maxlevels, level):
append = components.append
level += 1
saferepr = _safe_repr
items = object.items()
try:
items = sorted(items)
except TypeError:
def sortkey(item):
key, value = item
return str(type(key)), key, value
items = sorted(items, key=sortkey)
items = sorted(object.items(), key=_safe_tuple)
for k, v in items:
krepr, kreadable, krecur = saferepr(k, context, maxlevels, level)
vrepr, vreadable, vrecur = saferepr(v, context, maxlevels, level)

View File

@ -2,6 +2,7 @@ import pprint
import test.support
import unittest
import test.test_set
import random
# list, tuple and dict subclasses that do or don't overwrite __repr__
class list2(list):
@ -25,6 +26,10 @@ class dict3(dict):
def __repr__(self):
return dict.__repr__(self)
class Unorderable:
def __repr__(self):
return str(id(self))
class QueryTestCase(unittest.TestCase):
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_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):