1"""Redo the builtin repr() (representation) but with limits on most sizes."""
2
3__all__ = ["Repr", "repr", "recursive_repr"]
4
5import builtins
6from itertools import islice
7from _thread import get_ident
8
9def 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.__annotations__ = getattr(user_function, '__annotations__', {})
32        return wrapper
33
34    return decorating_function
35
36class Repr:
37
38    def __init__(self):
39        self.fillvalue = '...'
40        self.maxlevel = 6
41        self.maxtuple = 6
42        self.maxlist = 6
43        self.maxarray = 5
44        self.maxdict = 4
45        self.maxset = 6
46        self.maxfrozenset = 6
47        self.maxdeque = 6
48        self.maxstring = 30
49        self.maxlong = 40
50        self.maxother = 30
51
52    def repr(self, x):
53        return self.repr1(x, self.maxlevel)
54
55    def repr1(self, x, level):
56        typename = type(x).__name__
57        if ' ' in typename:
58            parts = typename.split()
59            typename = '_'.join(parts)
60        if hasattr(self, 'repr_' + typename):
61            return getattr(self, 'repr_' + typename)(x, level)
62        else:
63            return self.repr_instance(x, level)
64
65    def _repr_iterable(self, x, level, left, right, maxiter, trail=''):
66        n = len(x)
67        if level <= 0 and n:
68            s = self.fillvalue
69        else:
70            newlevel = level - 1
71            repr1 = self.repr1
72            pieces = [repr1(elem, newlevel) for elem in islice(x, maxiter)]
73            if n > maxiter:
74                pieces.append(self.fillvalue)
75            s = ', '.join(pieces)
76            if n == 1 and trail:
77                right = trail + right
78        return '%s%s%s' % (left, s, right)
79
80    def repr_tuple(self, x, level):
81        return self._repr_iterable(x, level, '(', ')', self.maxtuple, ',')
82
83    def repr_list(self, x, level):
84        return self._repr_iterable(x, level, '[', ']', self.maxlist)
85
86    def repr_array(self, x, level):
87        if not x:
88            return "array('%s')" % x.typecode
89        header = "array('%s', [" % x.typecode
90        return self._repr_iterable(x, level, header, '])', self.maxarray)
91
92    def repr_set(self, x, level):
93        if not x:
94            return 'set()'
95        x = _possibly_sorted(x)
96        return self._repr_iterable(x, level, '{', '}', self.maxset)
97
98    def repr_frozenset(self, x, level):
99        if not x:
100            return 'frozenset()'
101        x = _possibly_sorted(x)
102        return self._repr_iterable(x, level, 'frozenset({', '})',
103                                   self.maxfrozenset)
104
105    def repr_deque(self, x, level):
106        return self._repr_iterable(x, level, 'deque([', '])', self.maxdeque)
107
108    def repr_dict(self, x, level):
109        n = len(x)
110        if n == 0:
111            return '{}'
112        if level <= 0:
113            return '{' + self.fillvalue + '}'
114        newlevel = level - 1
115        repr1 = self.repr1
116        pieces = []
117        for key in islice(_possibly_sorted(x), self.maxdict):
118            keyrepr = repr1(key, newlevel)
119            valrepr = repr1(x[key], newlevel)
120            pieces.append('%s: %s' % (keyrepr, valrepr))
121        if n > self.maxdict:
122            pieces.append(self.fillvalue)
123        s = ', '.join(pieces)
124        return '{%s}' % (s,)
125
126    def repr_str(self, x, level):
127        s = builtins.repr(x[:self.maxstring])
128        if len(s) > self.maxstring:
129            i = max(0, (self.maxstring-3)//2)
130            j = max(0, self.maxstring-3-i)
131            s = builtins.repr(x[:i] + x[len(x)-j:])
132            s = s[:i] + self.fillvalue + s[len(s)-j:]
133        return s
134
135    def repr_int(self, x, level):
136        s = builtins.repr(x) # XXX Hope this isn't too slow...
137        if len(s) > self.maxlong:
138            i = max(0, (self.maxlong-3)//2)
139            j = max(0, self.maxlong-3-i)
140            s = s[:i] + self.fillvalue + s[len(s)-j:]
141        return s
142
143    def repr_instance(self, x, level):
144        try:
145            s = builtins.repr(x)
146            # Bugs in x.__repr__() can cause arbitrary
147            # exceptions -- then make up something
148        except Exception:
149            return '<%s instance at %#x>' % (x.__class__.__name__, id(x))
150        if len(s) > self.maxother:
151            i = max(0, (self.maxother-3)//2)
152            j = max(0, self.maxother-3-i)
153            s = s[:i] + self.fillvalue + s[len(s)-j:]
154        return s
155
156
157def _possibly_sorted(x):
158    # Since not all sequences of items can be sorted and comparison
159    # functions may raise arbitrary exceptions, return an unsorted
160    # sequence in that case.
161    try:
162        return sorted(x)
163    except Exception:
164        return list(x)
165
166aRepr = Repr()
167repr = aRepr.repr
168