Skip to content
Merged
19 changes: 18 additions & 1 deletion src/xmmutablemap/_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,23 @@ def __len__(self) -> int:
"""
return len(self._data)

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, Mapping):
if len(self._data) != len(other):
return False
for key, value in other.items():
try:
self_value = self._data[key]
except KeyError:
return False
if self_value != value:
return False
return True
return NotImplemented
Comment thread
nstarman marked this conversation as resolved.

Comment thread
nstarman marked this conversation as resolved.
# ===========================================
# Mapping Protocol

Expand Down Expand Up @@ -215,7 +232,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.
Expand Down
23 changes: 22 additions & 1 deletion tests/test_immutablemap.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,13 +57,34 @@ 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) -> 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})

Comment thread
nstarman marked this conversation as resolved.
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."""
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"]
Expand Down
Loading