From 57f98afef9b540f9a74da51d7869e2908473bf7d Mon Sep 17 00:00:00 2001 From: 0x1r1s2 Date: Tue, 25 Aug 2026 19:23:31 -0400 Subject: [PATCH] Fix DunderMixin hash equality contract for dictionary merging purposes --- steer_core/Mixins/Dunder.py | 17 +++++++++-------- test/test_dunder_mixin.py | 15 ++++++++++----- 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/steer_core/Mixins/Dunder.py b/steer_core/Mixins/Dunder.py index b6fcd9f..6a2d4b3 100644 --- a/steer_core/Mixins/Dunder.py +++ b/steer_core/Mixins/Dunder.py @@ -252,15 +252,17 @@ def __eq__(self, other): return True def __hash__(self): - """Hash based on object identity. + """Return a stable hash compatible with value-based equality. - Because the objects using this mixin are mutable, a content-based - hash would be unstable. Using ``id()`` means that two distinct - objects that compare equal via ``__eq__`` will have different - hashes -- avoid using these objects as ``set`` members or ``dict`` - keys when value-based identity matters. + Instances using this mixin are mutable, so hashing their property + values would make their hash unstable. A constant hash preserves + the requirement that objects which compare equal have equal hashes + while allowing ``dict`` and ``set`` lookups to use ``__eq__``. + + The tradeoff is that hash-based lookups among these objects are + linear rather than constant-time. """ - return hash(id(self)) + return 0 def __str__(self): """ @@ -276,4 +278,3 @@ def __repr__(self): Official string representation of the instance. """ return self.__str__() - diff --git a/test/test_dunder_mixin.py b/test/test_dunder_mixin.py index f7ac1e4..9f9da19 100644 --- a/test/test_dunder_mixin.py +++ b/test/test_dunder_mixin.py @@ -33,12 +33,17 @@ def test_different_name(self, sample_obj): class TestDunderHash: - def test_hash_is_identity_based(self, sample_obj): - assert hash(sample_obj) == hash(id(sample_obj)) + def test_equal_objects_have_equal_hashes(self, sample_obj): + equivalent = SampleObject(name="test", value=1.0) - def test_different_objects_different_hash(self, sample_obj): - other = SampleObject() - assert hash(sample_obj) != hash(other) + assert sample_obj == equivalent + assert hash(sample_obj) == hash(equivalent) + + def test_equivalent_object_resolves_dict_key(self, sample_obj): + equivalent = SampleObject(name="test", value=1.0) + values = {sample_obj: 7} + + assert values[equivalent] == 7 class TestDunderStr: