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