summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRaymond Hettinger <python@rcn.com>2009-11-19 01:07:05 (GMT)
committerRaymond Hettinger <python@rcn.com>2009-11-19 01:07:05 (GMT)
commita7da1663ecf9a881970b096a6dd0494584d0f795 (patch)
treeb3239a5f8dfc4f4de103b6dfddcdd057b6040b8a
parentdf961cfbfa1d52c6fdd138444e621ed7ffcd76bb (diff)
downloadcpython-a7da1663ecf9a881970b096a6dd0494584d0f795.zip
cpython-a7da1663ecf9a881970b096a6dd0494584d0f795.tar.gz
cpython-a7da1663ecf9a881970b096a6dd0494584d0f795.tar.bz2
Issue 3976: fix pprint for sets, frozensets, and dicts containing unorderable types.
-rw-r--r--Lib/pprint.py41
-rw-r--r--Lib/test/test_pprint.py19
2 files changed, 49 insertions, 11 deletions
diff --git a/Lib/pprint.py b/Lib/pprint.py
index 8bbc845..b3a6446 100644
--- a/Lib/pprint.py
+++ b/Lib/pprint.py
@@ -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)
diff --git a/Lib/test/test_pprint.py b/Lib/test/test_pprint.py
index 3d13b60..4370935 100644
--- a/Lib/test/test_pprint.py
+++ b/Lib/test/test_pprint.py
@@ -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):