[Python-checkins] r76389 - in python/branches/release31-maint: Lib/pprint.py Lib/test/test_pprint.py Misc/NEWS

raymond.hettinger python-checkins at python.org
Thu Nov 19 02:02:57 CET 2009


Author: raymond.hettinger
Date: Thu Nov 19 02:02:56 2009
New Revision: 76389

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

Modified:
   python/branches/release31-maint/Lib/pprint.py
   python/branches/release31-maint/Lib/test/test_pprint.py
   python/branches/release31-maint/Misc/NEWS

Modified: python/branches/release31-maint/Lib/pprint.py
==============================================================================
--- python/branches/release31-maint/Lib/pprint.py	(original)
+++ python/branches/release31-maint/Lib/pprint.py	Thu Nov 19 02:02:56 2009
@@ -70,6 +70,32 @@
     """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 @@
                 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 @@
                         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 @@
         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)

Modified: python/branches/release31-maint/Lib/test/test_pprint.py
==============================================================================
--- python/branches/release31-maint/Lib/test/test_pprint.py	(original)
+++ python/branches/release31-maint/Lib/test/test_pprint.py	Thu Nov 19 02:02:56 2009
@@ -2,6 +2,7 @@
 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 @@
     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 @@
         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):
 

Modified: python/branches/release31-maint/Misc/NEWS
==============================================================================
--- python/branches/release31-maint/Misc/NEWS	(original)
+++ python/branches/release31-maint/Misc/NEWS	Thu Nov 19 02:02:56 2009
@@ -46,6 +46,9 @@
 Library
 -------
 
+- Issue #3976: pprint for sets, frozensets, and dicts now succeed when
+  they contain unorderable types.
+
 - Issue #7341: Close the internal file object in the TarFile constructor in
   case of an error.
 


More information about the Python-checkins mailing list