diff -r 6a2f74811240 Lib/pprint.py --- a/Lib/pprint.py Thu Oct 23 23:03:35 2014 +0200 +++ b/Lib/pprint.py Sat Oct 25 12:33:38 2014 +0300 @@ -85,14 +85,10 @@ class _safe_key: def __lt__(self, other): try: - rv = self.obj.__lt__(other.obj) + return self.obj < other.obj except TypeError: - rv = NotImplemented - - if rv is NotImplemented: - rv = (str(type(self.obj)), id(self.obj)) < \ - (str(type(other.obj)), id(other.obj)) - return rv + return ((str(type(self.obj)), id(self.obj)) < \ + (str(type(other.obj)), id(other.obj))) def _safe_tuple(t): "Helper function for comparing 2-tuples" diff -r 6a2f74811240 Lib/test/test_pprint.py --- a/Lib/test/test_pprint.py Thu Oct 23 23:03:35 2014 +0200 +++ b/Lib/test/test_pprint.py Sat Oct 25 12:33:38 2014 +0300 @@ -48,6 +48,25 @@ class Unorderable: def __repr__(self): return str(id(self)) +# Class Orderable is orderable with any type +class Orderable: + def __init__(self, hash): + self._hash = hash + def __lt__(self, other): + return False + def __gt__(self, other): + return self != other + def __le__(self, other): + return self == other + def __ge__(self, other): + return True + def __eq__(self, other): + return self is other + def __ne__(self, other): + return self is not other + def __hash__(self): + return self._hash + class QueryTestCase(unittest.TestCase): def setUp(self): @@ -532,6 +551,26 @@ frozenset2({0, self.assertEqual(pprint.pformat(dict.fromkeys(keys, 0)), '{%r: 0, %r: 0}' % tuple(sorted(keys, key=id))) + def test_sort_orderable_and_unorderable_values(self): + # Issue 22721: sorted pprints is not stable + a = Unorderable() + b = Orderable(hash(a)) # should have the same hash value + # self-test + self.assertLess(a, b) + self.assertLess(str(type(b)), str(type(a))) + self.assertEqual(sorted([b, a]), [a, b]) + self.assertEqual(sorted([a, b]), [a, b]) + # set + self.assertEqual(pprint.pformat(set([b, a]), width=1), + '{%r,\n %r}' % (a, b)) + self.assertEqual(pprint.pformat(set([a, b]), width=1), + '{%r,\n %r}' % (a, b)) + # dict + self.assertEqual(pprint.pformat(dict.fromkeys([b, a]), width=1), + '{%r: None,\n %r: None}' % (a, b)) + self.assertEqual(pprint.pformat(dict.fromkeys([a, b]), width=1), + '{%r: None,\n %r: None}' % (a, b)) + def test_str_wrap(self): # pprint tries to wrap strings intelligently fox = 'the quick brown fox jumped over a lazy dog'