"""Streaming, truncating, non-recursive version of :func:`repr`.Differences from regular :func:`repr`:- Sets are represented the Python 3 way: ``{1, 2}`` vs ``set([1, 2])``.- Unicode strings does not have the ``u'`` prefix, even on Python 2.- Empty set formatted as ``set()`` (Python 3), not ``set([])`` (Python 2).- Longs don't have the ``L`` suffix.Very slow with no limits, super quick with limits."""importtracebackfromcollectionsimportdeque,namedtuplefromdecimalimportDecimalfromitertoolsimportchainfromnumbersimportNumberfrompprintimport_recursionfromtypingimportAny,AnyStr,Callable,Dict,Iterator,List,Optional,Sequence,Set,Tuple# noqafrom.textimporttruncate__all__=('saferepr','reprstream')#: 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 represented as chars.chars_t=(bytes,str)#: 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)LIT_DICT_KVSEP=_literal(': ',True,0)LIT_DICT_END=_literal('}',False,-1)LIT_LIST_START=_literal('[',False,+1)LIT_LIST_END=_literal(']',False,-1)LIT_LIST_SEP=_literal(', ',True,0)LIT_SET_START=_literal('{',False,+1)LIT_SET_END=_literal('}',False,-1)LIT_TUPLE_START=_literal('(',False,+1)LIT_TUPLE_END=_literal(')',False,-1)LIT_TUPLE_END_SV=_literal(',)',False,-1)
[文档]defsaferepr(o,maxlen=None,maxlevels=3,seen=None):# type: (Any, int, int, Set) -> str"""Safe version of :func:`repr`. Warning: Make sure you set the maxlen argument, or it will be very slow for recursive objects. With the maxlen set, it's often faster than built-in repr. """return''.join(_saferepr(o,maxlen=maxlen,maxlevels=maxlevels,seen=seen))
def_chaindict(mapping,LIT_DICT_KVSEP=LIT_DICT_KVSEP,LIT_LIST_SEP=LIT_LIST_SEP):# type: (Dict, _literal, _literal) -> Iterator[Any]size=len(mapping)fori,(k,v)inenumerate(mapping.items()):yield_key(k)yieldLIT_DICT_KVSEPyieldvifi<(size-1):yieldLIT_LIST_SEPdef_chainlist(it,LIT_LIST_SEP=LIT_LIST_SEP):# type: (List) -> Iterator[Any]size=len(it)fori,vinenumerate(it):yieldvifi<(size-1):yieldLIT_LIST_SEPdef_repr_empty_set(s):# type: (Set) -> strreturnf'{type(s).__name__}()'def_safetext(val):# type: (AnyStr) -> strifisinstance(val,bytes):try:val.encode('utf-8')exceptUnicodeDecodeError:# is bytes with unrepresentable characters, attempt# to convert back to unicodereturnval.decode('utf-8',errors='backslashreplace')returnvaldef_format_binary_bytes(val,maxlen,ellipsis='...'):# type: (bytes, int, str) -> strifmaxlenandlen(val)>maxlen:# we don't want to copy all the data, just take what we need.chunk=memoryview(val)[:maxlen].tobytes()return_bytes_prefix(f"'{_repr_binary_bytes(chunk)}{ellipsis}'")return_bytes_prefix(f"'{_repr_binary_bytes(val)}'")def_bytes_prefix(s):return'b'+sdef_repr_binary_bytes(val):# type: (bytes) -> strtry:returnval.decode('utf-8')exceptUnicodeDecodeError:# possibly not unicode, but binary data so format as hex.returnval.hex()def_format_chars(val,maxlen):# type: (AnyStr, int) -> strifisinstance(val,bytes):# pragma: no coverreturn_format_binary_bytes(val,maxlen)else:return"'{}'".format(truncate(val,maxlen).replace("'","\\'"))def_repr(obj):# type: (Any) -> strtry:returnrepr(obj)exceptExceptionasexc:stack='\n'.join(traceback.format_stack())returnf'<Unrepresentable {type(obj)!r}{id(obj):#x}: {exc!r}{stack!r}>'def_saferepr(o,maxlen=None,maxlevels=3,seen=None):# type: (Any, int, int, Set) -> strstack=deque([iter([o])])fortoken,itinreprstream(stack,seen=seen,maxlevels=maxlevels):ifmaxlenisnotNoneandmaxlen<=0:yield', ...'# move rest back to stack, so that we can include# dangling parens.stack.append(it)breakifisinstance(token,_literal):val=token.valueelifisinstance(token,_key):val=saferepr(token.value,maxlen,maxlevels)elifisinstance(token,_quoted):val=_format_chars(token.value,maxlen)else:val=_safetext(truncate(token,maxlen))yieldvalifmaxlenisnotNone:maxlen-=len(val)forrest1instack:# maxlen exceeded, process any dangling parens.forrest2inrest1:ifisinstance(rest2,_literal)andnotrest2.truncate:yieldrest2.valuedef_reprseq(val,lit_start,lit_end,builtin_type,chainer):# type: (Sequence, _literal, _literal, Any, Any) -> Tuple[Any, ...]iftype(val)isbuiltin_type:returnlit_start,lit_end,chainer(val)return(_literal(f'{type(val).__name__}({lit_start.value}',False,+1),_literal(f'{lit_end.value})',False,-1),chainer(val))
[文档]defreprstream(stack:deque,seen:Optional[Set]=None,maxlevels:int=3,level:int=0,isinstance:Callable=isinstance)->Iterator[Any]:"""Streaming repr, yielding tokens."""seen=seenorset()append=stack.appendpopleft=stack.popleftis_in_seen=seen.__contains__discard_from_seen=seen.discardadd_to_seen=seen.addwhilestack:lit_start=lit_end=Noneit=popleft()forvalinit:orig=valifisinstance(val,_dirty):discard_from_seen(val.objid)continueelifisinstance(val,_literal):level+=val.directionyieldval,itelifisinstance(val,_key):yieldval,itelifisinstance(val,Decimal):yield_repr(val),itelifisinstance(val,safe_t):yieldstr(val),itelifisinstance(val,chars_t):yield_quoted(val),itelifisinstance(val,range):# pragma: no coveryield_repr(val),itelse:ifisinstance(val,set_t):ifnotval:yield_repr_empty_set(val),itcontinuelit_start,lit_end,val=_reprseq(val,LIT_SET_START,LIT_SET_END,set,_chainlist,)elifisinstance(val,tuple):lit_start,lit_end,val=(LIT_TUPLE_START,LIT_TUPLE_END_SViflen(val)==1elseLIT_TUPLE_END,_chainlist(val))elifisinstance(val,dict):lit_start,lit_end,val=(LIT_DICT_START,LIT_DICT_END,_chaindict(val))elifisinstance(val,list):lit_start,lit_end,val=(LIT_LIST_START,LIT_LIST_END,_chainlist(val))else:# other type of objectyield_repr(val),itcontinueifmaxlevelsandlevel>=maxlevels:yieldf'{lit_start.value}...{lit_end.value}',itcontinueobjid=id(orig)ifis_in_seen(objid):yield_recursion(orig),itcontinueadd_to_seen(objid)# Recurse into the new list/tuple/dict/etc by tacking# the rest of our iterable onto the new it: this way# it works similar to a linked list.append(chain([lit_start],val,[_dirty(objid),lit_end],it))break