codekingpro/portable-devtools
114k
1"""Redo the builtin repr() (representation) but with limits on most sizes."""2 3__all__ = ["Repr", "repr", "recursive_repr"]4 5import builtins6from itertools import islice7from _thread import get_ident8 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 fillvalue19 repr_running.add(key)20 try:21 result = user_function(self)22 finally:23 repr_running.discard(key)24 return result25 26 # Can't use functools.wraps() here because of bootstrap issues27 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_function34 return wrapper35 36 return decorating_function37 38class 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 = maxlevel57 self.maxtuple = maxtuple58 self.maxlist = maxlist59 self.maxarray = maxarray60 self.maxdict = maxdict61 self.maxset = maxset62 self.maxfrozenset = maxfrozenset63 self.maxdeque = maxdeque64 self.maxstring = maxstring65 self.maxlong = maxlong66 self.maxother = maxother67 self.fillvalue = fillvalue68 self.indent = indent69 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 class84 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 intended88 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.indent99 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) * indent107 except TypeError as error:108 raise TypeError(109 f'Repr.indent must be a str, int or None, not {type(indent)}'110 ) from error111 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.fillvalue117 else:118 newlevel = level - 1119 repr1 = self.repr1120 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 + right126 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.typecode137 header = "array('%s', [" % x.typecode138 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 - 1163 repr1 = self.repr1164 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 s182 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 system189 # where the reprlib module is imported before the math module.190 import math, sys191 # Integers with more than sys.get_int_max_str_digits() digits192 # 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 s205 206 def repr_instance(self, x, level):207 try:208 s = builtins.repr(x)209 # Bugs in x.__repr__() can cause arbitrary210 # exceptions -- then make up something211 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 s218 219 220def _possibly_sorted(x):221 # Since not all sequences of items can be sorted and comparison222 # functions may raise arbitrary exceptions, return an unsorted223 # sequence in that case.224 try:225 return sorted(x)226 except Exception:227 return list(x)228 229aRepr = Repr()230repr = aRepr.repr231 