From bac3be549a24b7b520cc82521ed7b9d8e0af03db Mon Sep 17 00:00:00 2001 From: Nathaniel Starkman Date: Mon, 13 Apr 2026 23:35:01 -0400 Subject: [PATCH 1/9] 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 --- src/xmmutablemap/_core.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/xmmutablemap/_core.py b/src/xmmutablemap/_core.py index 3632880..ffe48b9 100644 --- a/src/xmmutablemap/_core.py +++ b/src/xmmutablemap/_core.py @@ -89,6 +89,14 @@ def __len__(self) -> int: """ return len(self._data) + def __eq__(self, other: Any) -> bool: + """Return whether two mappings contain the same items.""" + if isinstance(other, ImmutableMap): + return self._data == other._data + if isinstance(other, Mapping): + return self._data == dict(other.items()) + return NotImplemented + # =========================================== # Mapping Protocol From 3fd3eda7b14d7aefbeae2e2b1e86e89baa93c723 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 14 Apr 2026 03:41:14 +0000 Subject: [PATCH 2/9] 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> --- src/xmmutablemap/_core.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/xmmutablemap/_core.py b/src/xmmutablemap/_core.py index ffe48b9..06fcd37 100644 --- a/src/xmmutablemap/_core.py +++ b/src/xmmutablemap/_core.py @@ -89,7 +89,7 @@ def __len__(self) -> int: """ return len(self._data) - def __eq__(self, other: Any) -> bool: + def __eq__(self, other: object) -> bool: """Return whether two mappings contain the same items.""" if isinstance(other, ImmutableMap): return self._data == other._data From 10f461c612c70dc5a49721dd813837fb8af77b69 Mon Sep 17 00:00:00 2001 From: Nathaniel Starkman Date: Tue, 14 Apr 2026 00:09:15 -0400 Subject: [PATCH 3/9] Apply suggestions from code review Co-authored-by: Nathaniel Starkman Signed-off-by: Nathaniel Starkman --- src/xmmutablemap/_core.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/xmmutablemap/_core.py b/src/xmmutablemap/_core.py index 06fcd37..73c68f4 100644 --- a/src/xmmutablemap/_core.py +++ b/src/xmmutablemap/_core.py @@ -93,6 +93,8 @@ def __eq__(self, other: object) -> bool: """Return whether two mappings contain the same items.""" if isinstance(other, ImmutableMap): return self._data == other._data + if isinstance(other, dict): + return self._data = other if isinstance(other, Mapping): return self._data == dict(other.items()) return NotImplemented From 0c5e0f8f7218445d7bcad805fb0b3cc48dcc4581 Mon Sep 17 00:00:00 2001 From: Nathaniel Starkman Date: Tue, 14 Apr 2026 00:10:02 -0400 Subject: [PATCH 4/9] 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 --- src/xmmutablemap/_core.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/xmmutablemap/_core.py b/src/xmmutablemap/_core.py index 73c68f4..5cc79f6 100644 --- a/src/xmmutablemap/_core.py +++ b/src/xmmutablemap/_core.py @@ -99,6 +99,10 @@ def __eq__(self, other: object) -> bool: return self._data == dict(other.items()) return NotImplemented + def __hash__(self) -> int: + """Return an order-insensitive hash based on the mapping contents.""" + return hash(frozenset(self._data.items())) + # =========================================== # Mapping Protocol From be698434874fe5517253c0dea2498525f767aeff Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 14 Apr 2026 04:12:16 +0000 Subject: [PATCH 5/9] 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> --- src/xmmutablemap/_core.py | 8 +------- tests/test_immutablemap.py | 15 ++++++++++++++- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/src/xmmutablemap/_core.py b/src/xmmutablemap/_core.py index 5cc79f6..daa7146 100644 --- a/src/xmmutablemap/_core.py +++ b/src/xmmutablemap/_core.py @@ -93,16 +93,10 @@ def __eq__(self, other: object) -> bool: """Return whether two mappings contain the same items.""" if isinstance(other, ImmutableMap): return self._data == other._data - if isinstance(other, dict): - return self._data = other if isinstance(other, Mapping): return self._data == dict(other.items()) return NotImplemented - def __hash__(self) -> int: - """Return an order-insensitive hash based on the mapping contents.""" - return hash(frozenset(self._data.items())) - # =========================================== # Mapping Protocol @@ -229,7 +223,7 @@ def __hash__(self) -> int: True """ - return hash(tuple(self._data.items())) + return hash(frozenset(self._data.items())) def __repr__(self) -> str: """Return the representation. diff --git a/tests/test_immutablemap.py b/tests/test_immutablemap.py index 766457a..7feb52e 100644 --- a/tests/test_immutablemap.py +++ b/tests/test_immutablemap.py @@ -57,13 +57,26 @@ def test_len(self, d: ImmutableMap[str, Any]) -> None: def test_hash(self, d: ImmutableMap[str, Any]) -> None: """Test `__hash__`.""" - assert hash(d) == hash(tuple(d.items())) + assert hash(d) == hash(frozenset(d.items())) # Not hashable if values aren't hashable. d = ImmutableMap(a=1, b={"c"}) with pytest.raises(TypeError, match="unhashable type: 'set'"): hash(d) + def test_eq_with_other_mappings(self, d: ImmutableMap[str, Any]) -> None: + """Test mapping interoperability for `__eq__`.""" + assert d == {"a": 1, "b": 2} + assert d == OrderedDict([("a", 1), ("b", 2)]) + assert d == MappingProxyType({"a": 1, "b": 2}) + + def test_eq_and_hash_ignore_insertion_order(self) -> None: + """Test equality/hash contract for same items in different orders.""" + d1 = ImmutableMap(a=1, b=2) + d2 = ImmutableMap(b=2, a=1) + assert d1 == d2 + assert hash(d1) == hash(d2) + def test_keys(self, d: ImmutableMap[str, Any]) -> None: """Test `keys`.""" assert list(d.keys()) == ["a", "b"] From 63e43a07e29fa6422762b5cacf0353611ab43343 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 14 Apr 2026 04:13:08 +0000 Subject: [PATCH 6/9] 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> --- tests/test_immutablemap.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/test_immutablemap.py b/tests/test_immutablemap.py index 7feb52e..5b9ba4f 100644 --- a/tests/test_immutablemap.py +++ b/tests/test_immutablemap.py @@ -64,8 +64,9 @@ def test_hash(self, d: ImmutableMap[str, Any]) -> None: with pytest.raises(TypeError, match="unhashable type: 'set'"): hash(d) - def test_eq_with_other_mappings(self, d: ImmutableMap[str, Any]) -> None: + def test_eq_with_other_mappings(self) -> None: """Test mapping interoperability for `__eq__`.""" + d = ImmutableMap(a=1, b=2) assert d == {"a": 1, "b": 2} assert d == OrderedDict([("a", 1), ("b", 2)]) assert d == MappingProxyType({"a": 1, "b": 2}) From 2eabfa78a7e85eca5e6b5dac3b80a6ec0f27458e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 14 Apr 2026 04:14:53 +0000 Subject: [PATCH 7/9] 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> --- src/xmmutablemap/_core.py | 7 ++++++- tests/test_immutablemap.py | 7 +++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/xmmutablemap/_core.py b/src/xmmutablemap/_core.py index daa7146..9ed2362 100644 --- a/src/xmmutablemap/_core.py +++ b/src/xmmutablemap/_core.py @@ -94,7 +94,12 @@ def __eq__(self, other: object) -> bool: if isinstance(other, ImmutableMap): return self._data == other._data if isinstance(other, Mapping): - return self._data == dict(other.items()) + if len(self._data) != len(other): + return False + sentinel = object() + return all( + self._data.get(key, sentinel) == value for key, value in other.items() + ) return NotImplemented # =========================================== diff --git a/tests/test_immutablemap.py b/tests/test_immutablemap.py index 5b9ba4f..c0a5efc 100644 --- a/tests/test_immutablemap.py +++ b/tests/test_immutablemap.py @@ -67,9 +67,16 @@ def test_hash(self, d: ImmutableMap[str, Any]) -> None: def test_eq_with_other_mappings(self) -> None: """Test mapping interoperability for `__eq__`.""" d = ImmutableMap(a=1, b=2) + other_dict = {"a": 1, "b": 2} + other_ordered_dict = OrderedDict([("a", 1), ("b", 2)]) + other_proxy = MappingProxyType({"a": 1, "b": 2}) + assert d == {"a": 1, "b": 2} assert d == OrderedDict([("a", 1), ("b", 2)]) assert d == MappingProxyType({"a": 1, "b": 2}) + assert other_dict == d + assert other_ordered_dict == d + assert other_proxy == d def test_eq_and_hash_ignore_insertion_order(self) -> None: """Test equality/hash contract for same items in different orders.""" From 961991a2dcf8d66a60f297ddb49f0c7e5d0fb2b7 Mon Sep 17 00:00:00 2001 From: Nathaniel Starkman Date: Tue, 14 Apr 2026 01:25:33 -0400 Subject: [PATCH 8/9] 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 --- src/xmmutablemap/_core.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/xmmutablemap/_core.py b/src/xmmutablemap/_core.py index 9ed2362..cf1a039 100644 --- a/src/xmmutablemap/_core.py +++ b/src/xmmutablemap/_core.py @@ -96,10 +96,12 @@ def __eq__(self, other: object) -> bool: if isinstance(other, Mapping): if len(self._data) != len(other): return False - sentinel = object() - return all( - self._data.get(key, sentinel) == value for key, value in other.items() - ) + for key, value in other.items(): + if key not in self._data: + return False + if self._data[key] != value: + return False + return True return NotImplemented # =========================================== From b5668dedee065deb16325505e7e0e04f428fc2e8 Mon Sep 17 00:00:00 2001 From: Nathaniel Starkman Date: Tue, 14 Apr 2026 01:32:57 -0400 Subject: [PATCH 9/9] 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 --- src/xmmutablemap/_core.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/xmmutablemap/_core.py b/src/xmmutablemap/_core.py index cf1a039..544aa8e 100644 --- a/src/xmmutablemap/_core.py +++ b/src/xmmutablemap/_core.py @@ -97,9 +97,11 @@ def __eq__(self, other: object) -> bool: if len(self._data) != len(other): return False for key, value in other.items(): - if key not in self._data: + try: + self_value = self._data[key] + except KeyError: return False - if self._data[key] != value: + if self_value != value: return False return True return NotImplemented