Skip to content

Commit d7d0478

Browse files
nstarmangithub-advanced-security[bot]CopilotCopilot
authored
Fix for Inconsistent equality and hashing (#50)
* Fix for Inconsistent equality and hashing Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Nathaniel Starkman <nstarman@users.noreply.github.com> * Fix pre-commit by using object type in __eq__ Agent-Logs-Url: https://github.com/GalacticDynamics/xmmutablemap/sessions/34262293-9387-470d-8439-ed11f3ebb34f Co-authored-by: nstarman <8949649+nstarman@users.noreply.github.com> * Apply suggestions from code review Co-authored-by: Nathaniel Starkman <nstarman@users.noreply.github.com> Signed-off-by: Nathaniel Starkman <nstarman@users.noreply.github.com> * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Signed-off-by: Nathaniel Starkman <nstarman@users.noreply.github.com> * Fix hash/equality contract and add mapping equality tests Agent-Logs-Url: https://github.com/GalacticDynamics/xmmutablemap/sessions/f39253f1-d8ea-46eb-8a8e-f004866a473d Co-authored-by: nstarman <8949649+nstarman@users.noreply.github.com> * Refine mapping equality test setup Agent-Logs-Url: https://github.com/GalacticDynamics/xmmutablemap/sessions/f39253f1-d8ea-46eb-8a8e-f004866a473d Co-authored-by: nstarman <8949649+nstarman@users.noreply.github.com> * Optimize mapping equality checks and add reverse comparison tests Agent-Logs-Url: https://github.com/GalacticDynamics/xmmutablemap/sessions/f39253f1-d8ea-46eb-8a8e-f004866a473d Co-authored-by: nstarman <8949649+nstarman@users.noreply.github.com> * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Signed-off-by: Nathaniel Starkman <nstarman@users.noreply.github.com> * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Signed-off-by: Nathaniel Starkman <nstarman@users.noreply.github.com> --------- Signed-off-by: Nathaniel Starkman <nstarman@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: nstarman <8949649+nstarman@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
1 parent 99aa90b commit d7d0478

2 files changed

Lines changed: 40 additions & 2 deletions

File tree

src/xmmutablemap/_core.py

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,23 @@ def __len__(self) -> int:
8989
"""
9090
return len(self._data)
9191

92+
def __eq__(self, other: object) -> bool:
93+
"""Return whether two mappings contain the same items."""
94+
if isinstance(other, ImmutableMap):
95+
return self._data == other._data
96+
if isinstance(other, Mapping):
97+
if len(self._data) != len(other):
98+
return False
99+
for key, value in other.items():
100+
try:
101+
self_value = self._data[key]
102+
except KeyError:
103+
return False
104+
if self_value != value:
105+
return False
106+
return True
107+
return NotImplemented
108+
92109
# ===========================================
93110
# Mapping Protocol
94111

@@ -215,7 +232,7 @@ def __hash__(self) -> int:
215232
True
216233
217234
"""
218-
return hash(tuple(self._data.items()))
235+
return hash(frozenset(self._data.items()))
219236

220237
def __repr__(self) -> str:
221238
"""Return the representation.

tests/test_immutablemap.py

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,13 +57,34 @@ def test_len(self, d: ImmutableMap[str, Any]) -> None:
5757

5858
def test_hash(self, d: ImmutableMap[str, Any]) -> None:
5959
"""Test `__hash__`."""
60-
assert hash(d) == hash(tuple(d.items()))
60+
assert hash(d) == hash(frozenset(d.items()))
6161

6262
# Not hashable if values aren't hashable.
6363
d = ImmutableMap(a=1, b={"c"})
6464
with pytest.raises(TypeError, match="unhashable type: 'set'"):
6565
hash(d)
6666

67+
def test_eq_with_other_mappings(self) -> None:
68+
"""Test mapping interoperability for `__eq__`."""
69+
d = ImmutableMap(a=1, b=2)
70+
other_dict = {"a": 1, "b": 2}
71+
other_ordered_dict = OrderedDict([("a", 1), ("b", 2)])
72+
other_proxy = MappingProxyType({"a": 1, "b": 2})
73+
74+
assert d == {"a": 1, "b": 2}
75+
assert d == OrderedDict([("a", 1), ("b", 2)])
76+
assert d == MappingProxyType({"a": 1, "b": 2})
77+
assert other_dict == d
78+
assert other_ordered_dict == d
79+
assert other_proxy == d
80+
81+
def test_eq_and_hash_ignore_insertion_order(self) -> None:
82+
"""Test equality/hash contract for same items in different orders."""
83+
d1 = ImmutableMap(a=1, b=2)
84+
d2 = ImmutableMap(b=2, a=1)
85+
assert d1 == d2
86+
assert hash(d1) == hash(d2)
87+
6788
def test_keys(self, d: ImmutableMap[str, Any]) -> None:
6889
"""Test `keys`."""
6990
assert list(d.keys()) == ["a", "b"]

0 commit comments

Comments
 (0)