Skip to content

Commit

Permalink
Improve rudimentary_repr to not stumble through descriptors on __dict…
Browse files Browse the repository at this point in the history
…__. Ref #62.
  • Loading branch information
ionelmc committed May 13, 2019
1 parent b3a1310 commit ea24461
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 8 deletions.
26 changes: 19 additions & 7 deletions src/hunter/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,15 +74,27 @@ def rudimentary_repr(obj, maxdepth=5):
# hardcoded list of safe things. note that isinstance ain't used
# (we don't trust subclasses to do the right thing in __repr__)
return repr(obj)
elif not hasattr(obj, '__dict__'):
# note that this could be `not hasattr(obj, '__dict__') and not hasattr(obj, '__slots__')`
# but lots of objects safe to repr (like sockets) have __slots__
# (I don't want to have the burden of maintaining a huge list of safe to repr types)
#
# the assumption is that if you use __slots__ you know what you're doing and you ain't gonna be stupid enough to
# have a side-effect in __repr__ (I hope ...)
elif not hasdict(obj_type, obj):
return repr(obj)
else:
# if the object has a __dict__ then it's probably an instance of a pure python class, assume bad things
# with side-effects will be going on in __repr__ - use the default instead (object.__repr__)
return object.__repr__(obj)


def hasdict(obj_type, obj, tolerance=25):
"""
A contrived mess to check that object doesn't have a __dit__ but avoid checking it if any ancestor is evil enough to
explicitly define __dict__ (like apipkg.ApiModule has __dict__ as a property).
"""
ancestor_types = deque()
while obj_type is not type and tolerance:
ancestor_types.appendleft(obj_type)
obj_type = type(obj_type)
tolerance -= 1
for ancestor in ancestor_types:
__dict__ = getattr(ancestor, '__dict__', None)
if __dict__ is not None:
if '__dict__' in __dict__:
return True
return hasattr(obj, '__dict__')
20 changes: 19 additions & 1 deletion tests/test_util.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import _socket
import socket
from array import array
from collections import deque
from collections import namedtuple
from decimal import Decimal

import _socket
import py

from hunter.util import hasdict
from hunter.util import rudimentary_repr


Expand Down Expand Up @@ -59,3 +61,19 @@ def test_rudimentary_repr():
print(rudimentary_repr([[[data]]]))
print(rudimentary_repr([[[[data]]]]))
print(rudimentary_repr([[[[[data]]]]]))


def test_hasutil():
assert hasdict(type(py.io), py.io) is True
sock, _ = socket.socketpair()
assert hasdict(type(sock), sock) is False

class Foo(object):
pass

assert hasdict(Foo, Foo()) is True

class Bar:
pass

assert hasdict(Bar, Bar()) is True

0 comments on commit ea24461

Please sign in to comment.