| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent df961cf commit a7da166
2 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -70,6 +70,32 @@ def isrecursive(object): | |||
| 70 | 70 | """Determine if object requires a recursive representation.""" | |
| 71 | 71 | return _safe_repr(object, {}, None, 0)[2] | |
| 72 | 72 | ||
| 73 | + class _safe_key: | ||
| 74 | + """Helper function for key functions when sorting unorderable objects. | ||
| 75 | + | ||
| 76 | + The wrapped-object will fallback to an Py2.x style comparison for | ||
| 77 | + unorderable types (sorting first comparing the type name and then by | ||
| 78 | + the obj ids). Does not work recursively, so dict.items() must have | ||
| 79 | + _safe_key applied to both the key and the value. | ||
| 80 | + | ||
| 81 | + """ | ||
| 82 | + | ||
| 83 | + __slots__ = ['obj'] | ||
| 84 | + | ||
| 85 | + def __init__(self, obj): | ||
| 86 | + self.obj = obj | ||
| 87 | + | ||
| 88 | + def __lt__(self, other): | ||
| 89 | + rv = self.obj.__lt__(other.obj) | ||
| 90 | + if rv is NotImplemented: | ||
| 91 | + rv = (str(type(self.obj)), id(self.obj)) < \ | ||
| 92 | + (str(type(other.obj)), id(other.obj)) | ||
| 93 | + return rv | ||
| 94 | + | ||
| 95 | + def _safe_tuple(t): | ||
| 96 | + "Helper function for comparing 2-tuples" | ||
| 97 | + return _safe_key(t[0]), _safe_key(t[1]) | ||
| 98 | + | ||
| 73 | 99 | class PrettyPrinter: | |
| 74 | 100 | def __init__(self, indent=1, width=80, depth=None, stream=None): | |
| 75 | 101 | """Handle pretty printing operations onto a stream using a set of | |
@@ -145,7 +171,7 @@ def _format(self, object, stream, indent, allowance, context, level): | |||
| 145 | 171 | if length: | |
| 146 | 172 | context[objid] = 1 | |
| 147 | 173 | indent = indent + self._indent_per_level | |
| 148 | - items = sorted(object.items()) | ||
| 174 | + items = sorted(object.items(), key=_safe_tuple) | ||
| 149 | 175 | key, ent = items[0] | |
| 150 | 176 | rep = self._repr(key, context, level) | |
| 151 | 177 | write(rep) | |
@@ -178,14 +204,14 @@ def _format(self, object, stream, indent, allowance, context, level): | |||
| 178 | 204 | return | |
| 179 | 205 | write('{') | |
| 180 | 206 | endchar = '}' | |
| 181 | - object = sorted(object) | ||
| 207 | + object = sorted(object, key=_safe_key) | ||
| 182 | 208 | elif issubclass(typ, frozenset): | |
| 183 | 209 | if not length: | |
| 184 | 210 | write('frozenset()') | |
| 185 | 211 | return | |
| 186 | 212 | write('frozenset({') | |
| 187 | 213 | endchar = '})' | |
| 188 | - object = sorted(object) | ||
| 214 | + object = sorted(object, key=_safe_key) | ||
| 189 | 215 | indent += 10 | |
| 190 | 216 | else: | |
| 191 | 217 | write('(') | |
@@ -267,14 +293,7 @@ def _safe_repr(object, context, maxlevels, level): | |||
| 267 | 293 | append = components.append | |
| 268 | 294 | level += 1 | |
| 269 | 295 | saferepr = _safe_repr | |
| 270 | - items = object.items() | ||
| 271 | - try: | ||
| 272 | - items = sorted(items) | ||
| 273 | - except TypeError: | ||
| 274 | - def sortkey(item): | ||
| 275 | - key, value = item | ||
| 276 | - return str(type(key)), key, value | ||
| 277 | - items = sorted(items, key=sortkey) | ||
| 296 | + items = sorted(object.items(), key=_safe_tuple) | ||
| 278 | 297 | for k, v in items: | |
| 279 | 298 | krepr, kreadable, krecur = saferepr(k, context, maxlevels, level) | |
| 280 | 299 | vrepr, vreadable, vrecur = saferepr(v, context, maxlevels, level) | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -2,6 +2,7 @@ | |||
| 2 | 2 | import test.support | |
| 3 | 3 | import unittest | |
| 4 | 4 | import test.test_set | |
| 5 | + import random | ||
| 5 | 6 | ||
| 6 | 7 | # list, tuple and dict subclasses that do or don't overwrite __repr__ | |
| 7 | 8 | class list2(list): | |
@@ -25,6 +26,10 @@ class dict3(dict): | |||
| 25 | 26 | def __repr__(self): | |
| 26 | 27 | return dict.__repr__(self) | |
| 27 | 28 | ||
| 29 | + class Unorderable: | ||
| 30 | + def __repr__(self): | ||
| 31 | + return str(id(self)) | ||
| 32 | + | ||
| 28 | 33 | class QueryTestCase(unittest.TestCase): | |
| 29 | 34 | ||
| 30 | 35 | def setUp(self): | |
@@ -407,6 +412,20 @@ def test_depth(self): | |||
| 407 | 412 | self.assertEqual(pprint.pformat(nested_dict, depth=1), lv1_dict) | |
| 408 | 413 | self.assertEqual(pprint.pformat(nested_list, depth=1), lv1_list) | |
| 409 | 414 | ||
| 415 | + def test_sort_unorderable_values(self): | ||
| 416 | + # Issue 3976: sorted pprints fail for unorderable values. | ||
| 417 | + n = 20 | ||
| 418 | + keys = [Unorderable() for i in range(n)] | ||
| 419 | + random.shuffle(keys) | ||
| 420 | + skeys = sorted(keys, key=id) | ||
| 421 | + clean = lambda s: s.replace(' ', '').replace('\n','') | ||
| 422 | + | ||
| 423 | + self.assertEqual(clean(pprint.pformat(set(keys))), | ||
| 424 | + '{' + ','.join(map(repr, skeys)) + '}') | ||
| 425 | + self.assertEqual(clean(pprint.pformat(frozenset(keys))), | ||
| 426 | + 'frozenset({' + ','.join(map(repr, skeys)) + '})') | ||
| 427 | + self.assertEqual(clean(pprint.pformat(dict.fromkeys(keys))), | ||
| 428 | + '{' + ','.join('%r:None' % k for k in skeys) + '}') | ||
| 410 | 429 | ||
| 411 | 430 | class DottedPrettyPrinter(pprint.PrettyPrinter): | |
| 412 | 431 | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments