|
@@ -13,6 +13,7 @@ Very slow with no limits, super quick with limits.
|
|
|
from __future__ import absolute_import, unicode_literals
|
|
|
|
|
|
import sys
|
|
|
+import traceback
|
|
|
|
|
|
from collections import deque, namedtuple
|
|
|
|
|
@@ -38,13 +39,33 @@ else:
|
|
|
class range_t(object): # noqa
|
|
|
pass
|
|
|
|
|
|
+#: Node representing literal text.
|
|
|
+#: - .value: is the literal text value
|
|
|
+#: - .truncate: specifies if this text can be truncated, for things like
|
|
|
+#: LIT_DICT_END this will be False, as we always display
|
|
|
+#: the ending brackets, e.g: [[[1, 2, 3, ...,], ..., ]]
|
|
|
+#: - .direction: If +1 the current level is increment by one,
|
|
|
+#: if -1 the current level is decremented by one, and
|
|
|
+#: if 0 the current level is unchanged.
|
|
|
_literal = namedtuple('_literal', ('value', 'truncate', 'direction'))
|
|
|
+
|
|
|
+#: Node representing a dictionary key.
|
|
|
_key = namedtuple('_key', ('value',))
|
|
|
+
|
|
|
+#: Node representing quoted text, e.g. a string value.
|
|
|
_quoted = namedtuple('_quoted', ('value',))
|
|
|
+
|
|
|
+
|
|
|
+#: Recursion protection.
|
|
|
_dirty = namedtuple('_dirty', ('objid',))
|
|
|
|
|
|
+#: Types that are repsented as chars.
|
|
|
chars_t = (bytes, text_t)
|
|
|
+
|
|
|
+#: Types that are regarded as safe to call repr on.
|
|
|
safe_t = (Number,)
|
|
|
+
|
|
|
+#: Set types.
|
|
|
set_t = (frozenset, set)
|
|
|
|
|
|
LIT_DICT_START = _literal('{', False, +1)
|
|
@@ -61,6 +82,7 @@ LIT_TUPLE_END_SV = _literal(',)', False, -1)
|
|
|
|
|
|
|
|
|
def saferepr(o, maxlen=None, maxlevels=3, seen=None):
|
|
|
+ # type: (Any, int, int, Set) -> str
|
|
|
"""Safe version of :func:`repr`.
|
|
|
|
|
|
Warning:
|
|
@@ -76,6 +98,7 @@ def saferepr(o, maxlen=None, maxlevels=3, seen=None):
|
|
|
def _chaindict(mapping,
|
|
|
LIT_DICT_KVSEP=LIT_DICT_KVSEP,
|
|
|
LIT_LIST_SEP=LIT_LIST_SEP):
|
|
|
+ # type: (Dict, _literal, _literal) -> Iterator[Any]
|
|
|
size = len(mapping)
|
|
|
for i, (k, v) in enumerate(items(mapping)):
|
|
|
yield _key(k)
|
|
@@ -86,6 +109,7 @@ def _chaindict(mapping,
|
|
|
|
|
|
|
|
|
def _chainlist(it, LIT_LIST_SEP=LIT_LIST_SEP):
|
|
|
+ # type: (List) -> Iterator[Any]
|
|
|
size = len(it)
|
|
|
for i, v in enumerate(it):
|
|
|
yield v
|
|
@@ -94,10 +118,12 @@ def _chainlist(it, LIT_LIST_SEP=LIT_LIST_SEP):
|
|
|
|
|
|
|
|
|
def _repr_empty_set(s):
|
|
|
+ # type: (Set) -> str
|
|
|
return '%s()' % (type(s).__name__,)
|
|
|
|
|
|
|
|
|
def _safetext(val):
|
|
|
+ # type: (AnyStr) -> str
|
|
|
if isinstance(val, bytes):
|
|
|
try:
|
|
|
val.encode('utf-8')
|
|
@@ -109,6 +135,7 @@ def _safetext(val):
|
|
|
|
|
|
|
|
|
def _format_binary_bytes(val, maxlen, ellipsis='...'):
|
|
|
+ # type: (bytes, int, str) -> str
|
|
|
if maxlen and len(val) > maxlen:
|
|
|
# we don't want to copy all the data, just take what we need.
|
|
|
chunk = memoryview(val)[:maxlen].tobytes()
|
|
@@ -117,6 +144,7 @@ def _format_binary_bytes(val, maxlen, ellipsis='...'):
|
|
|
|
|
|
|
|
|
def _repr_binary_bytes(val):
|
|
|
+ # type: (bytes) -> str
|
|
|
try:
|
|
|
return val.decode('utf-8')
|
|
|
except UnicodeDecodeError:
|
|
@@ -132,13 +160,24 @@ def _repr_binary_bytes(val):
|
|
|
|
|
|
|
|
|
def _format_chars(val, maxlen):
|
|
|
+ # type: (AnyStr, int) -> str
|
|
|
if IS_PY3 and isinstance(val, bytes): # pragma: no cover
|
|
|
return _format_binary_bytes(val, maxlen)
|
|
|
else:
|
|
|
return "'{0}'".format(truncate(val, maxlen))
|
|
|
|
|
|
|
|
|
+def _repr(obj):
|
|
|
+ # type: (Any) -> str
|
|
|
+ try:
|
|
|
+ return repr(obj)
|
|
|
+ except Exception as exc:
|
|
|
+ return '<Unrepresentable {0!r}{1:#x}: {2!r} {3!r}>'.format(
|
|
|
+ type(obj), id(obj), exc, '\n'.join(traceback.format_stack()))
|
|
|
+
|
|
|
+
|
|
|
def _saferepr(o, maxlen=None, maxlevels=3, seen=None):
|
|
|
+ # type: (Any, int, int, Set) -> str
|
|
|
stack = deque([iter([o])])
|
|
|
for token, it in reprstream(stack, seen=seen, maxlevels=maxlevels):
|
|
|
if maxlen is not None and maxlen <= 0:
|
|
@@ -166,6 +205,7 @@ def _saferepr(o, maxlen=None, maxlevels=3, seen=None):
|
|
|
|
|
|
|
|
|
def _reprseq(val, lit_start, lit_end, builtin_type, chainer):
|
|
|
+ # type: (Sequence, _literal, _literal, Any, Any) -> Tuple[Any, ...]
|
|
|
if type(val) is builtin_type: # noqa
|
|
|
return lit_start, lit_end, chainer(val)
|
|
|
return (
|
|
@@ -177,6 +217,7 @@ def _reprseq(val, lit_start, lit_end, builtin_type, chainer):
|
|
|
|
|
|
def reprstream(stack, seen=None, maxlevels=3, level=0, isinstance=isinstance):
|
|
|
"""Streaming repr, yielding tokens."""
|
|
|
+ # type: (deque, Set, int, int, Callable) -> Iterator[Any]
|
|
|
seen = seen or set()
|
|
|
append = stack.append
|
|
|
popleft = stack.popleft
|
|
@@ -198,13 +239,13 @@ def reprstream(stack, seen=None, maxlevels=3, level=0, isinstance=isinstance):
|
|
|
elif isinstance(val, _key):
|
|
|
yield val, it
|
|
|
elif isinstance(val, Decimal):
|
|
|
- yield repr(val), it
|
|
|
+ yield _repr(val), it
|
|
|
elif isinstance(val, safe_t):
|
|
|
yield text_t(val), it
|
|
|
elif isinstance(val, chars_t):
|
|
|
yield _quoted(val), it
|
|
|
elif isinstance(val, range_t): # pragma: no cover
|
|
|
- yield repr(val), it
|
|
|
+ yield _repr(val), it
|
|
|
else:
|
|
|
if isinstance(val, set_t):
|
|
|
if not val:
|
|
@@ -226,7 +267,7 @@ def reprstream(stack, seen=None, maxlevels=3, level=0, isinstance=isinstance):
|
|
|
LIT_LIST_START, LIT_LIST_END, _chainlist(val))
|
|
|
else:
|
|
|
# other type of object
|
|
|
- yield repr(val), it
|
|
|
+ yield _repr(val), it
|
|
|
continue
|
|
|
|
|
|
if maxlevels and level >= maxlevels:
|