From 06837d5c877c6d06fc0fef193b80b1e2dc76691b Mon Sep 17 00:00:00 2001
From: Valentin Lorentz <vlorentz@softwareheritage.org>
Date: Tue, 7 Jul 2020 15:10:53 +0200
Subject: [PATCH] Implement ImmutableDict.__hash__.

---
 swh/model/collections.py            |  3 +++
 swh/model/tests/test_collections.py | 20 ++++++++++++++++++++
 2 files changed, 23 insertions(+)

diff --git a/swh/model/collections.py b/swh/model/collections.py
index 495b43c5..38d28a32 100644
--- a/swh/model/collections.py
+++ b/swh/model/collections.py
@@ -42,6 +42,9 @@ class ImmutableDict(Mapping, Generic[KT, VT]):
     def items(self):
         yield from self.data
 
+    def __hash__(self):
+        return hash(tuple(sorted(self.data)))
+
     def copy_pop(self, popped_key) -> Tuple[Optional[VT], "ImmutableDict[KT, VT]"]:
         """Returns a copy of this ImmutableDict without the given key,
         as well as the value associated to the key."""
diff --git a/swh/model/tests/test_collections.py b/swh/model/tests/test_collections.py
index b042c592..d096009d 100644
--- a/swh/model/tests/test_collections.py
+++ b/swh/model/tests/test_collections.py
@@ -64,3 +64,23 @@ def test_immutabledict_copy_pop():
     assert d.copy_pop("foo") == ("bar", ImmutableDict({"baz": "qux"}))
 
     assert d.copy_pop("not a key") == (None, d)
+
+
+def test_hash():
+    assert hash(ImmutableDict()) == hash(ImmutableDict({}))
+    assert hash(ImmutableDict({"foo": "bar"})) == hash(ImmutableDict({"foo": "bar"}))
+    assert hash(ImmutableDict({"foo": "bar", "baz": "qux"})) == hash(
+        ImmutableDict({"foo": "bar", "baz": "qux"})
+    )
+    assert hash(ImmutableDict({"foo": "bar", "baz": "qux"})) == hash(
+        ImmutableDict({"baz": "qux", "foo": "bar"})
+    )
+
+
+def test_equality_order():
+    assert ImmutableDict({"foo": "bar", "baz": "qux"}) == ImmutableDict(
+        {"foo": "bar", "baz": "qux"}
+    )
+    assert ImmutableDict({"foo": "bar", "baz": "qux"}) == ImmutableDict(
+        {"baz": "qux", "foo": "bar"}
+    )
-- 
GitLab