]> granicus.if.org Git - python/commitdiff
pprint functions used to sort a dict (by key) if and only if
authorTim Peters <tim.peters@gmail.com>
Fri, 2 Jun 2006 23:22:51 +0000 (23:22 +0000)
committerTim Peters <tim.peters@gmail.com>
Fri, 2 Jun 2006 23:22:51 +0000 (23:22 +0000)
the output required more than one line.  "Small" dicts got
displayed in seemingly random order (the hash-induced order
produced by dict.__repr__).  None of this was documented.
Now pprint functions always sort dicts by key, and the docs
promise it.

This was proposed and agreed to during the PyCon 2006 core
sprint -- I just didn't have time for it before now.

Doc/lib/libpprint.tex
Lib/pprint.py
Lib/test/test_pprint.py
Misc/NEWS

index 45d9c873ac0a8e7ea3c679488739ebc807da201e..fd030389b3213b962be55764e422d0a2edd9fecd 100644 (file)
@@ -20,6 +20,10 @@ and breaks them onto multiple lines if they don't fit within the
 allowed width.  Construct \class{PrettyPrinter} objects explicitly if
 you need to adjust the width constraint.
 
+\versionchanged[Dictionaries are sorted by key before the display is
+computed; before 2.5, a dictionary was sorted only if its display
+required more than one line, although that wasn't documented]{2.5}
+
 The \module{pprint} module defines one class:
 
 
index f77a0e224832fb012a37c2011533e0eee61f77f1..19a3dc249cee8038562071cc118db7327a10dab0 100644 (file)
@@ -246,7 +246,7 @@ def _safe_repr(object, context, maxlevels, level):
         append = components.append
         level += 1
         saferepr = _safe_repr
-        for k, v in object.iteritems():
+        for k, v in sorted(object.items()):
             krepr, kreadable, krecur = saferepr(k, context, maxlevels, level)
             vrepr, vreadable, vrecur = saferepr(v, context, maxlevels, level)
             append("%s: %s" % (krepr, vrepr))
index 27d6b5208470c13cbae54434badeea55f1168695..09ba268f71431c6662d1ac2e611cb9f06701228e 100644 (file)
@@ -11,16 +11,21 @@ except NameError:
 # list, tuple and dict subclasses that do or don't overwrite __repr__
 class list2(list):
     pass
+
 class list3(list):
     def __repr__(self):
         return list.__repr__(self)
+
 class tuple2(tuple):
     pass
+
 class tuple3(tuple):
     def __repr__(self):
         return tuple.__repr__(self)
+
 class dict2(dict):
     pass
+
 class dict3(dict):
     def __repr__(self):
         return dict.__repr__(self)
@@ -101,7 +106,13 @@ class QueryTestCase(unittest.TestCase):
 
     def test_same_as_repr(self):
         # Simple objects, small containers and classes that overwrite __repr__
-        # For those the result should be the same as repr()
+        # For those the result should be the same as repr().
+        # Ahem.  The docs don't say anything about that -- this appears to
+        # be testing an implementation quirk.  Starting in Python 2.5, it's
+        # not true for dicts:  pprint always sorts dicts by key now; before,
+        # it sorted a dict display if and only if the display required
+        # multiple lines.  For that reason, dicts with more than one element
+        # aren't tested here.
         verify = self.assert_
         for simple in (0, 0L, 0+0j, 0.0, "", uni(""),
                        (), tuple2(), tuple3(),
@@ -112,9 +123,7 @@ class QueryTestCase(unittest.TestCase):
                        (1,2), [3,4], {5: 6, 7: 8},
                        tuple2((1,2)), tuple3((1,2)), tuple3(range(100)),
                        [3,4], list2([3,4]), list3([3,4]), list3(range(100)),
-                       {5: 6, 7: 8}, dict2({5: 6, 7: 8}), dict3({5: 6, 7: 8}),
-                       dict3([(x,x) for x in range(100)]),
-                       {"xy\tab\n": (3,), 5: [[]], (): {}},
+                       {5: 6, 7: 8}, dict2({5: 6}), dict3({5: 6}),
                        range(10, -11, -1)
                       ):
             native = repr(simple)
@@ -160,6 +169,24 @@ class QueryTestCase(unittest.TestCase):
         for type in [list, list2]:
             self.assertEqual(pprint.pformat(type(o), indent=4), exp)
 
+    def test_sorted_dict(self):
+        # Starting in Python 2.5, pprint sorts dict displays by key regardless
+        # of how small the dictionary may be.
+        # Before the change, on 32-bit Windows pformat() gave order
+        # 'a', 'c', 'b' here, so this test failed.
+        d = {'a': 1, 'b': 1, 'c': 1}
+        self.assertEqual(pprint.pformat(d), "{'a': 1, 'b': 1, 'c': 1}")
+        self.assertEqual(pprint.pformat([d, d]),
+            "[{'a': 1, 'b': 1, 'c': 1}, {'a': 1, 'b': 1, 'c': 1}]")
+
+        # The next one is kind of goofy.  The sorted order depends on the
+        # alphabetic order of type names:  "int" < "str" < "tuple".  Before
+        # Python 2.5, this was in the test_same_as_repr() test.  It's worth
+        # keeping around for now because it's one of few tests of pprint
+        # against a crazy mix of types.
+        self.assertEqual(pprint.pformat({"xy\tab\n": (3,), 5: [[]], (): {}}),
+            r"{5: [[]], 'xy\tab\n': (3,), (): {}}")
+
     def test_subclassing(self):
         o = {'names with spaces': 'should be presented using repr()',
              'others.should.not.be': 'like.this'}
index fdfde1e0dfbfc28357d1f5eaef9e0c923cb60f8e..d54928e3b7857b9a106816143b202aeaf5b218ae 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -101,6 +101,12 @@ Extension Modules
 Library
 -------
 
+- The functions in the ``pprint`` module now sort dictionaries by key
+  before computing the display.  Before 2.5, ``pprint`` sorted a dictionary
+  if and only if its display required more than one line, although that
+  wasn't documented.  The new behavior increases predictability; e.g.,
+  using ``pprint.pprint(a_dict)`` in a doctest is now reliable.
+
 - Patch #1497027: try HTTP digest auth before basic auth in urllib2
   (thanks for J. J. Lee).