diff --git a/pytools/__init__.py b/pytools/__init__.py index c8cfa90b96583a1a59475ce6d752858f4cc5ab20..f1f1ebf31a3fee12fc99e36b3b104b1be326127a 100644 --- a/pytools/__init__.py +++ b/pytools/__init__.py @@ -2623,7 +2623,7 @@ def resolve_name(name): # {{{ unordered_hash -def unordered_hash(hash_constructor, iterable): +def unordered_hash(hash_instance, iterable, hash_constructor=None): """Using a hash algorithm given by the parameter-less constructor *hash_constructor*, return a hash object whose internal state depends on the entries of *iterable*, but not their order. If *hash* @@ -2631,6 +2631,10 @@ def unordered_hash(hash_constructor, iterable): the each entry *i* of the iterable must permit ``hash.upate(i)`` to succeed. An example of *hash_constructor* is ``hashlib.sha256`` from :mod:`hashlib`. ``hash.digest_size`` must also be defined. + If *hash_constructor* is not provided, ``hash_instance.name`` is + used to deduce it. + + :returns: the updated *hash_instance*. .. warning:: @@ -2639,6 +2643,12 @@ def unordered_hash(hash_constructor, iterable): .. versionadded:: 2021.2 """ + + if hash_constructor is None: + from functools import partial + import hashlib + hash_constructor = partial(hashlib.new, hash_instance.name) + h_int = 0 for i in iterable: h_i = hash_constructor() @@ -2650,9 +2660,8 @@ def unordered_hash(hash_constructor, iterable): # mix adjacent bits). h_int = h_int ^ int.from_bytes(h_i.digest(), sys.byteorder) - h = hash_constructor() - h.update(h_int.to_bytes(h.digest_size, sys.byteorder)) - return h + hash_instance.update(h_int.to_bytes(hash_instance.digest_size, sys.byteorder)) + return hash_instance # }}} diff --git a/pytools/persistent_dict.py b/pytools/persistent_dict.py index f48027fde89649a9e9ee7f6dd6d488166b4b0765..c3a7b1da76cd0d7973715ba093ff227a9d895e40 100644 --- a/pytools/persistent_dict.py +++ b/pytools/persistent_dict.py @@ -294,11 +294,9 @@ class KeyBuilder: def update_for_frozenset(self, key_hash, key): from pytools import unordered_hash - self.rec(key_hash, - unordered_hash( - self.new_hash, - (self.rec(self.new_hash(), key_i).digest() for key_i in key) - ).digest()) + unordered_hash( + key_hash, + (self.rec(self.new_hash(), key_i).digest() for key_i in key)) @staticmethod def update_for_NoneType(key_hash, key): # noqa diff --git a/test/test_pytools.py b/test/test_pytools.py index 49a4390dae466af422045e9f3e7ffab449539a6a..356e47c34e4bb12016c9b262b6d575181dbaca32 100644 --- a/test/test_pytools.py +++ b/test/test_pytools.py @@ -446,15 +446,15 @@ def test_unordered_hash(): random.shuffle(lst) from pytools import unordered_hash - assert (unordered_hash(hashlib.sha256, lorig).digest() - == unordered_hash(hashlib.sha256, lst).digest()) - assert (unordered_hash(hashlib.sha256, lorig).digest() - == unordered_hash(hashlib.sha256, lorig).digest()) - assert (unordered_hash(hashlib.sha256, lorig).digest() - != unordered_hash(hashlib.sha256, lorig[:-1]).digest()) + assert (unordered_hash(hashlib.sha256(), lorig).digest() + == unordered_hash(hashlib.sha256(), lst).digest()) + assert (unordered_hash(hashlib.sha256(), lorig).digest() + == unordered_hash(hashlib.sha256(), lorig).digest()) + assert (unordered_hash(hashlib.sha256(), lorig).digest() + != unordered_hash(hashlib.sha256(), lorig[:-1]).digest()) lst[0] = b"aksdjfla;sdfjafd" - assert (unordered_hash(hashlib.sha256, lorig).digest() - != unordered_hash(hashlib.sha256, lst).digest()) + assert (unordered_hash(hashlib.sha256(), lorig).digest() + != unordered_hash(hashlib.sha256(), lst).digest()) if __name__ == "__main__":