Last active
December 2, 2021 18:12
-
-
Save wshayes/ca20d1b4c26a80eadb300c91358a55fe to your computer and use it in GitHub Desktop.
[Safe repr] safe recursive repr function #python
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment