# https://blog.ionelmc.ro/2020/01/20/is-there-anything-safe-in-python/ def safe_repr(obj, maxdepth=5): if not maxdepth: return '...' obj_type = type(obj) obj_type_type = type(obj_type) newdepth = maxdepth - 1 # only represent exact builtins # (subclasses can have side-effects due to __class__ being # a property, __instancecheck__, __subclasscheck__ etc) if obj_type is dict: return '{%s}' % ', '.join('%s: %s' % ( safe_repr(k, maxdepth), safe_repr(v, newdepth) ) for k, v in obj.items()) elif obj_type is list: return '[%s]' % ', '.join( safe_repr(i, newdepth) for i in obj ) elif obj_type is tuple: return '(%s%s)' % ( ', '.join(safe_repr(i, newdepth) for i in obj), ',' if len(obj) == 1 else '' ) elif obj_type is set: return '{%s}' % ', '.join( safe_repr(i, newdepth) for i in obj ) elif obj_type is frozenset: return '%s({%s})' % ( obj_type.__name__, ', '.join(safe_repr(i, newdepth) for i in obj) ) elif obj_type is deque: return '%s([%s])' % ( obj_type.__name__, ', '.join(safe_repr(i, newdepth) for i in obj) ) elif obj_type in (Counter, OrderedDict, defaultdict): return '%s({%s})' % ( obj_type.__name__, ', '.join('%s: %s' % ( safe_repr(k, maxdepth), safe_repr(v, newdepth) ) for k, v in obj.items()) ) elif obj_type is types.MethodType: # noqa self = obj.__self__ name = getattr(obj, '__qualname__', None) if name is None: name = obj.__name__ return '<%sbound method %s of %s>' % ( 'un' if self is None else '', name, safe_repr(self, newdepth) ) elif obj_type_type is type and BaseException in obj_type.__mro__: return '%s(%s)' % ( obj_type.__name__, ', '.join(safe_repr(i, newdepth) for i in obj.args) ) elif obj_type_type is type and \ obj_type is not InstanceType and \ obj_type.__module__ in (builtins.__name__, 'io', 'socket', '_socket'): # hardcoded list of safe things. note that isinstance ain't used # (and we don't trust subclasses to do the right thing in __repr__) return repr(obj) else: return object.__repr__(obj)