Skip to content

Commit c701383

Browse files
committed
fingerprinting base case handles empty __dict__
1 parent 0bbc7f8 commit c701383

File tree

2 files changed

+20
-4
lines changed

2 files changed

+20
-4
lines changed

hamilton/caching/fingerprinting.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,11 @@ def hash_value(obj, *args, depth=0, **kwargs) -> str:
7171
if depth > MAX_DEPTH:
7272
return UNHASHABLE
7373

74-
if hasattr(obj, "__dict__"):
74+
# __dict__ attribute contains the instance attributes of the object.
75+
# this is typically sufficient to define the object and its behavior, so it's a good target
76+
# for a hash in the default case.
77+
# Objects that return an empty dict should be skipped (very odd behavior, happens with pandas type)
78+
if getattr(obj, "__dict__", {}) != {}:
7579
return hash_value(obj.__dict__, depth=depth + 1)
7680

7781
# check if the object comes from a module part of the standard library

tests/caching/test_fingerprinting.py

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,24 @@ def test_hash_no_dict_attribute():
2323
class Foo:
2424
__slots__ = ()
2525

26-
def __init__(self):
27-
pass
26+
obj = Foo()
27+
assert not hasattr(obj, "__dict__")
28+
29+
fingerprint = fingerprinting.hash_value(obj)
30+
31+
assert fingerprint == fingerprinting.UNHASHABLE
32+
33+
34+
def test_empty_dict_attr_is_unhashable():
35+
"""Classes with an empty __dict__ can't be hashed during the base case."""
36+
37+
class Foo: ... # noqa: E701
2838

2939
obj = Foo()
40+
assert obj.__dict__ == {}
41+
3042
fingerprint = fingerprinting.hash_value(obj)
31-
assert not hasattr(obj, "__dict__")
43+
3244
assert fingerprint == fingerprinting.UNHASHABLE
3345

3446

0 commit comments

Comments
 (0)