Skip to content

Commit 62aa7dc

Browse files
Issue #22721: An order of multiline pprint output of set or dict containing
orderable and non-orderable elements no longer depends on iteration order of set or dict.
1 parent 01362da commit 62aa7dc

3 files changed

Lines changed: 46 additions & 7 deletions

File tree

‎Lib/pprint.py‎

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -86,14 +86,10 @@ def __init__(self, obj):
8686

8787
def __lt__(self, other):
8888
try:
89-
rv = self.obj.__lt__(other.obj)
89+
return self.obj < other.obj
9090
except TypeError:
91-
rv = NotImplemented
92-
93-
if rv is NotImplemented:
94-
rv = (str(type(self.obj)), id(self.obj)) < \
95-
(str(type(other.obj)), id(other.obj))
96-
return rv
91+
return ((str(type(self.obj)), id(self.obj)) < \
92+
(str(type(other.obj)), id(other.obj)))
9793

9894
def _safe_tuple(t):
9995
"Helper function for comparing 2-tuples"

‎Lib/test/test_pprint.py‎

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,25 @@ class Unorderable:
5050
def __repr__(self):
5151
return str(id(self))
5252

53+
# Class Orderable is orderable with any type
54+
class Orderable:
55+
def __init__(self, hash):
56+
self._hash = hash
57+
def __lt__(self, other):
58+
return False
59+
def __gt__(self, other):
60+
return self != other
61+
def __le__(self, other):
62+
return self == other
63+
def __ge__(self, other):
64+
return True
65+
def __eq__(self, other):
66+
return self is other
67+
def __ne__(self, other):
68+
return self is not other
69+
def __hash__(self):
70+
return self._hash
71+
5372
class QueryTestCase(unittest.TestCase):
5473

5574
def setUp(self):
@@ -620,6 +639,26 @@ def test_sort_unorderable_values(self):
620639
self.assertEqual(pprint.pformat(dict.fromkeys(keys, 0)),
621640
'{%r: 0, %r: 0}' % tuple(sorted(keys, key=id)))
622641

642+
def test_sort_orderable_and_unorderable_values(self):
643+
# Issue 22721: sorted pprints is not stable
644+
a = Unorderable()
645+
b = Orderable(hash(a)) # should have the same hash value
646+
# self-test
647+
self.assertLess(a, b)
648+
self.assertLess(str(type(b)), str(type(a)))
649+
self.assertEqual(sorted([b, a]), [a, b])
650+
self.assertEqual(sorted([a, b]), [a, b])
651+
# set
652+
self.assertEqual(pprint.pformat(set([b, a]), width=1),
653+
'{%r,\n %r}' % (a, b))
654+
self.assertEqual(pprint.pformat(set([a, b]), width=1),
655+
'{%r,\n %r}' % (a, b))
656+
# dict
657+
self.assertEqual(pprint.pformat(dict.fromkeys([b, a]), width=1),
658+
'{%r: None,\n %r: None}' % (a, b))
659+
self.assertEqual(pprint.pformat(dict.fromkeys([a, b]), width=1),
660+
'{%r: None,\n %r: None}' % (a, b))
661+
623662
def test_str_wrap(self):
624663
# pprint tries to wrap strings intelligently
625664
fox = 'the quick brown fox jumped over a lazy dog'

‎Misc/NEWS‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@ Core and Builtins
1919
Library
2020
-------
2121

22+
- Issue #22721: An order of multiline pprint output of set or dict containing
23+
orderable and non-orderable elements no longer depends on iteration order of
24+
set or dict.
25+
2226
- Issue #15133: _tkinter.tkapp.getboolean() now supports Tcl_Obj and always
2327
returns bool. tkinter.BooleanVar now validates input values (accepted bool,
2428
int, str, and Tcl_Obj). tkinter.BooleanVar.get() now always returns bool.

0 commit comments

Comments
 (0)