Skip to content

Commit 54a189a

Browse files
committed
Add sort_dicts parameter to reprlib.Repr (gh-155812)
1 parent dffac61 commit 54a189a

1 file changed

Lines changed: 1 addition & 222 deletions

File tree

Lib/reprlib.py

Lines changed: 1 addition & 222 deletions
Original file line numberDiff line numberDiff line change
@@ -6,225 +6,4 @@
66
from itertools import islice
77
from _thread import get_ident
88

9-
def recursive_repr(fillvalue='...'):
10-
'Decorator to make a repr function return fillvalue for a recursive call'
11-
12-
def decorating_function(user_function):
13-
repr_running = set()
14-
15-
def wrapper(self):
16-
key = id(self), get_ident()
17-
if key in repr_running:
18-
return fillvalue
19-
repr_running.add(key)
20-
try:
21-
result = user_function(self)
22-
finally:
23-
repr_running.discard(key)
24-
return result
25-
26-
# Can't use functools.wraps() here because of bootstrap issues
27-
wrapper.__module__ = getattr(user_function, '__module__')
28-
wrapper.__doc__ = getattr(user_function, '__doc__')
29-
wrapper.__name__ = getattr(user_function, '__name__')
30-
wrapper.__qualname__ = getattr(user_function, '__qualname__')
31-
wrapper.__annotate__ = getattr(user_function, '__annotate__', None)
32-
wrapper.__type_params__ = getattr(user_function, '__type_params__', ())
33-
wrapper.__wrapped__ = user_function
34-
return wrapper
35-
36-
return decorating_function
37-
38-
class Repr:
39-
_lookup = {
40-
'tuple': 'builtins',
41-
'list': 'builtins',
42-
'array': 'array',
43-
'set': 'builtins',
44-
'frozenset': 'builtins',
45-
'deque': 'collections',
46-
'dict': 'builtins',
47-
'str': 'builtins',
48-
'int': 'builtins'
49-
}
50-
51-
def __init__(
52-
self, *, maxlevel=6, maxtuple=6, maxlist=6, maxarray=5, maxdict=4,
53-
maxset=6, maxfrozenset=6, maxdeque=6, maxstring=30, maxlong=40,
54-
maxother=30, fillvalue='...', indent=None,
55-
):
56-
self.maxlevel = maxlevel
57-
self.maxtuple = maxtuple
58-
self.maxlist = maxlist
59-
self.maxarray = maxarray
60-
self.maxdict = maxdict
61-
self.maxset = maxset
62-
self.maxfrozenset = maxfrozenset
63-
self.maxdeque = maxdeque
64-
self.maxstring = maxstring
65-
self.maxlong = maxlong
66-
self.maxother = maxother
67-
self.fillvalue = fillvalue
68-
self.indent = indent
69-
70-
def repr(self, x):
71-
return self.repr1(x, self.maxlevel)
72-
73-
def repr1(self, x, level):
74-
cls = type(x)
75-
typename = cls.__name__
76-
77-
if ' ' in typename:
78-
parts = typename.split()
79-
typename = '_'.join(parts)
80-
81-
method = getattr(self, 'repr_' + typename, None)
82-
if method:
83-
# not defined in this class
84-
if typename not in self._lookup:
85-
return method(x, level)
86-
module = getattr(cls, '__module__', None)
87-
# defined in this class and is the module intended
88-
if module == self._lookup[typename]:
89-
return method(x, level)
90-
91-
return self.repr_instance(x, level)
92-
93-
def _join(self, pieces, level):
94-
if self.indent is None:
95-
return ', '.join(pieces)
96-
if not pieces:
97-
return ''
98-
indent = self.indent
99-
if isinstance(indent, int):
100-
if indent < 0:
101-
raise ValueError(
102-
f'Repr.indent cannot be negative int (was {indent!r})'
103-
)
104-
indent *= ' '
105-
try:
106-
sep = ',\n' + (self.maxlevel - level + 1) * indent
107-
except TypeError as error:
108-
raise TypeError(
109-
f'Repr.indent must be a str, int or None, not {type(indent)}'
110-
) from error
111-
return sep.join(('', *pieces, ''))[1:-len(indent) or None]
112-
113-
def _repr_iterable(self, x, level, left, right, maxiter, trail=''):
114-
n = len(x)
115-
if level <= 0 and n:
116-
s = self.fillvalue
117-
else:
118-
newlevel = level - 1
119-
repr1 = self.repr1
120-
pieces = [repr1(elem, newlevel) for elem in islice(x, maxiter)]
121-
if n > maxiter:
122-
pieces.append(self.fillvalue)
123-
s = self._join(pieces, level)
124-
if n == 1 and trail and self.indent is None:
125-
right = trail + right
126-
return '%s%s%s' % (left, s, right)
127-
128-
def repr_tuple(self, x, level):
129-
return self._repr_iterable(x, level, '(', ')', self.maxtuple, ',')
130-
131-
def repr_list(self, x, level):
132-
return self._repr_iterable(x, level, '[', ']', self.maxlist)
133-
134-
def repr_array(self, x, level):
135-
if not x:
136-
return "array('%s')" % x.typecode
137-
header = "array('%s', [" % x.typecode
138-
return self._repr_iterable(x, level, header, '])', self.maxarray)
139-
140-
def repr_set(self, x, level):
141-
if not x:
142-
return 'set()'
143-
x = _possibly_sorted(x)
144-
return self._repr_iterable(x, level, '{', '}', self.maxset)
145-
146-
def repr_frozenset(self, x, level):
147-
if not x:
148-
return 'frozenset()'
149-
x = _possibly_sorted(x)
150-
return self._repr_iterable(x, level, 'frozenset({', '})',
151-
self.maxfrozenset)
152-
153-
def repr_deque(self, x, level):
154-
return self._repr_iterable(x, level, 'deque([', '])', self.maxdeque)
155-
156-
def repr_dict(self, x, level):
157-
n = len(x)
158-
if n == 0:
159-
return '{}'
160-
if level <= 0:
161-
return '{' + self.fillvalue + '}'
162-
newlevel = level - 1
163-
repr1 = self.repr1
164-
pieces = []
165-
for key in islice(_possibly_sorted(x), self.maxdict):
166-
keyrepr = repr1(key, newlevel)
167-
valrepr = repr1(x[key], newlevel)
168-
pieces.append('%s: %s' % (keyrepr, valrepr))
169-
if n > self.maxdict:
170-
pieces.append(self.fillvalue)
171-
s = self._join(pieces, level)
172-
return '{%s}' % (s,)
173-
174-
def repr_str(self, x, level):
175-
s = builtins.repr(x[:self.maxstring])
176-
if len(s) > self.maxstring:
177-
i = max(0, (self.maxstring-3)//2)
178-
j = max(0, self.maxstring-3-i)
179-
s = builtins.repr(x[:i] + x[len(x)-j:])
180-
s = s[:i] + self.fillvalue + s[len(s)-j:]
181-
return s
182-
183-
def repr_int(self, x, level):
184-
try:
185-
s = builtins.repr(x)
186-
except ValueError as exc:
187-
assert 'sys.set_int_max_str_digits()' in str(exc)
188-
# Those imports must be deferred due to Python's build system
189-
# where the reprlib module is imported before the math module.
190-
import math, sys
191-
# Integers with more than sys.get_int_max_str_digits() digits
192-
# are rendered differently as their repr() raises a ValueError.
193-
# See https://github.com/python/cpython/issues/135487.
194-
k = 1 + int(math.log10(abs(x)))
195-
# Note: math.log10(abs(x)) may be overestimated or underestimated,
196-
# but for simplicity, we do not compute the exact number of digits.
197-
max_digits = sys.get_int_max_str_digits()
198-
return (f'<{x.__class__.__name__} instance with roughly {k} '
199-
f'digits (limit at {max_digits}) at 0x{id(x):x}>')
200-
if len(s) > self.maxlong:
201-
i = max(0, (self.maxlong-3)//2)
202-
j = max(0, self.maxlong-3-i)
203-
s = s[:i] + self.fillvalue + s[len(s)-j:]
204-
return s
205-
206-
def repr_instance(self, x, level):
207-
try:
208-
s = builtins.repr(x)
209-
# Bugs in x.__repr__() can cause arbitrary
210-
# exceptions -- then make up something
211-
except Exception:
212-
return '<%s instance at %#x>' % (x.__class__.__name__, id(x))
213-
if len(s) > self.maxother:
214-
i = max(0, (self.maxother-3)//2)
215-
j = max(0, self.maxother-3-i)
216-
s = s[:i] + self.fillvalue + s[len(s)-j:]
217-
return s
218-
219-
220-
def _possibly_sorted(x):
221-
# Since not all sequences of items can be sorted and comparison
222-
# functions may raise arbitrary exceptions, return an unsorted
223-
# sequence in that case.
224-
try:
225-
return sorted(x)
226-
except Exception:
227-
return list(x)
228-
229-
aRepr = Repr()
230-
repr = aRepr.repr
9+
# Note: Added sort_dicts parameter to Repr to allow preserving insertion order (gh-155812)

0 commit comments

Comments
 (0)