Skip to content

Commit f0b39cb

Browse files
authored
Merge pull request #577 from tpvasconcelos/master
Fix bug in DeepHash for classes with uninitialised slots
2 parents 8676729 + c474322 commit f0b39cb

File tree

2 files changed

+100
-1
lines changed

2 files changed

+100
-1
lines changed

deepdiff/deephash.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -415,7 +415,7 @@ def _prep_obj(self, obj: Any, parent: str, parents_ids: frozenset = EMPTY_FROZEN
415415
obj_to_dict_strategies.append(lambda o: o.__dict__)
416416

417417
if hasattr(obj, "__slots__"):
418-
obj_to_dict_strategies.append(lambda o: {i: getattr(o, i) for i in o.__slots__})
418+
obj_to_dict_strategies.append(lambda o: {i: getattr(o, i) for i in o.__slots__ if hasattr(o, i)})
419419
else:
420420
import inspect
421421
obj_to_dict_strategies.append(lambda o: dict(inspect.getmembers(o, lambda m: not inspect.isroutine(m))))

tests/test_hash.py

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,105 @@ def test_uuid6_deepdiff_negative(self):
232232
assert 'set_item_added' in diff
233233
assert str(dummy_id_2) in str(diff)
234234

235+
def test_slots_with_uninitialized_attributes(self):
236+
"""Test that DeepHash handles classes with __slots__ where not all slots are initialized."""
237+
class PartiallyInitialized:
238+
__slots__ = ['x', 'y', 'z']
239+
240+
def __init__(self, x):
241+
self.x = x
242+
243+
obj = PartiallyInitialized(10)
244+
result = DeepHash(obj)
245+
# Should not raise AttributeError iternally, and should successfully hash the object
246+
assert result[obj] is not unprocessed
247+
248+
def test_slots_fully_initialized(self):
249+
"""Test that DeepHash correctly hashes classes with __slots__ where all slots are initialized."""
250+
class FullyInitialized:
251+
__slots__ = ['x', 'y']
252+
253+
def __init__(self, x, y):
254+
self.x = x
255+
self.y = y
256+
257+
obj1 = FullyInitialized(1, 2)
258+
obj2 = FullyInitialized(1, 2)
259+
obj3 = FullyInitialized(3, 4)
260+
261+
hash1 = DeepHash(obj1)
262+
hash2 = DeepHash(obj2)
263+
hash3 = DeepHash(obj3)
264+
265+
# Same values should produce same hash
266+
assert hash1[obj1] == hash2[obj2]
267+
# Different values should produce different hash
268+
assert hash1[obj1] != hash3[obj3]
269+
270+
def test_slots_with_no_initialized_attributes(self):
271+
"""Test that DeepHash handles classes with __slots__ where no slots are initialized."""
272+
class EmptySlots:
273+
__slots__ = ['a', 'b', 'c']
274+
275+
def __init__(self):
276+
pass
277+
278+
obj = EmptySlots()
279+
result = DeepHash(obj)
280+
assert result[obj] is not unprocessed
281+
282+
def test_slots_inheritance(self):
283+
"""Test that DeepHash handles inherited __slots__ correctly."""
284+
class Base:
285+
__slots__ = ['x']
286+
287+
def __init__(self, x):
288+
self.x = x
289+
290+
class Derived(Base):
291+
__slots__ = ['y', 'z']
292+
293+
def __init__(self, x, y):
294+
super().__init__(x)
295+
self.y = y
296+
297+
obj = Derived(1, 2)
298+
result = DeepHash(obj)
299+
# Should still not raise AttributeError internally for uninitialized 'z'
300+
assert result[obj] is not unprocessed
301+
302+
def test_slots_deepdiff_comparison(self):
303+
"""Test that DeepDiff also works correctly with __slots__ classes (incl inherited and uninitialized attributes)."""
304+
class Base:
305+
__slots__ = ['x']
306+
307+
def __init__(self, x):
308+
self.x = x
309+
310+
class Derived(Base):
311+
__slots__ = ['y', 'z']
312+
313+
def __init__(self, x, y):
314+
super().__init__(x)
315+
self.y = y
316+
317+
obj1 = Derived(1, 2)
318+
obj2 = Derived(1, 2)
319+
obj3 = Derived(3, 4)
320+
321+
# Same initialized values should show no difference
322+
diff1 = DeepDiff(obj1, obj2)
323+
assert diff1 == {}
324+
325+
# Different values should show difference
326+
diff2 = DeepDiff(obj1, obj3)
327+
assert diff2 == {
328+
'values_changed': {
329+
'root.x': {'new_value': 3, 'old_value': 1},
330+
'root.y': {'new_value': 4, 'old_value': 2},
331+
}
332+
}
333+
235334
class TestDeepHashPrep:
236335
"""DeepHashPrep Tests covering object serialization."""
237336

0 commit comments

Comments
 (0)