From 95fef993fd095f83d27d07d8ac8fb5586849c8ca Mon Sep 17 00:00:00 2001
From: "Andrew J. Hesford" <ajh@sideband.org>
Date: Tue, 23 Jul 2024 11:41:09 -0400
Subject: [PATCH] persistent_dict: make siphash24 optional

---
 .github/workflows/ci.yml   |  5 +++--
 pyproject.toml             |  4 +++-
 pytools/persistent_dict.py | 14 ++++++++------
 3 files changed, 14 insertions(+), 9 deletions(-)

diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 3df093d..13f219a 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -66,7 +66,7 @@ jobs:
                 python-version: '3.x'
         -   name: "Main Script"
             run: |
-                EXTRA_INSTALL="numpy"
+                EXTRA_INSTALL="numpy siphash24"
                 curl -L -O https://gitlab.tiker.net/inducer/ci-support/raw/main/prepare-and-run-mypy.sh
                 . ./prepare-and-run-mypy.sh python3 mypy
 
@@ -89,7 +89,7 @@ jobs:
                 # AK, 2020-12-13
                 rm pytools/mpiwrap.py
 
-                EXTRA_INSTALL="numpy frozendict immutabledict orderedsets constantdict immutables pyrsistent attrs"
+                EXTRA_INSTALL="numpy frozendict immutabledict orderedsets constantdict immutables pyrsistent attrs siphash24"
                 curl -L -O https://gitlab.tiker.net/inducer/ci-support/raw/main/build-and-test-py-project.sh
                 . ./build-and-test-py-project.sh
 
@@ -104,6 +104,7 @@ jobs:
                 python-version: '3.x'
         -   name: "Main Script"
             run: |
+                EXTRA_INSTALL="siphash24"
                 rm pytools/{convergence,spatial_btree,obj_array,mpiwrap}.py
                 curl -L -O https://gitlab.tiker.net/inducer/ci-support/raw/main/build-and-test-py-project.sh
                 . ./build-and-test-py-project.sh
diff --git a/pyproject.toml b/pyproject.toml
index 2fce107..a134159 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -34,7 +34,6 @@ dependencies = [
     "platformdirs>=2.2",
     # for dataclass_transform with frozen_default
     "typing-extensions>=4; python_version<'3.13'",
-    "siphash24>=1.6",
 ]
 
 [project.optional-dependencies]
@@ -46,6 +45,9 @@ test = [
     "pytest",
     "ruff",
 ]
+siphash = [
+    "siphash24>=1.6",
+]
 
 [project.urls]
 Documentation = "https://documen.tician.de/pytools/"
diff --git a/pytools/persistent_dict.py b/pytools/persistent_dict.py
index 50e3507..f3a5a05 100644
--- a/pytools/persistent_dict.py
+++ b/pytools/persistent_dict.py
@@ -50,9 +50,15 @@ from typing import (
     TypeVar,
     cast,
 )
+from warnings import warn
 
-from siphash24 import siphash13
 
+try:
+    from siphash24 import siphash13 as _default_hash
+except ImportError:
+    warn("pytools.persistent_dict: unable to import 'siphash24.siphash13', "
+         "falling back to hashlib.sha256", stacklevel=1)
+    from hashlib import sha256 as _default_hash
 
 if TYPE_CHECKING:
     from _typeshed import ReadableBuffer
@@ -161,7 +167,7 @@ class KeyBuilder:
 
     # this exists so that we can (conceivably) switch algorithms at some point
     # down the road
-    new_hash: Callable[..., Hash] = siphash13
+    new_hash: Callable[..., Hash] = _default_hash
 
     def rec(self, key_hash: Hash, key: Any) -> Hash:
         """
@@ -429,7 +435,6 @@ class CollisionWarning(UserWarning):
 def __getattr__(name: str) -> Any:
     if name in ("NoSuchEntryInvalidKeyError",
                 "NoSuchEntryInvalidContentsError"):
-        from warnings import warn
         warn(f"pytools.persistent_dict.{name} has been removed.", stacklevel=2)
         return NoSuchEntryError
 
@@ -506,7 +511,6 @@ class _PersistentDictBase(Mapping[K, V]):
         # https://www.sqlite.org/pragma.html#pragma_synchronous
         if safe_sync is None or safe_sync:
             if safe_sync is None:
-                from warnings import warn
                 warn(f"pytools.persistent_dict '{identifier}': "
                      "enabling safe_sync as default. "
                      "This provides strong protection against data loss, "
@@ -531,7 +535,6 @@ class _PersistentDictBase(Mapping[K, V]):
     def _collision_check(self, key: K, stored_key: K) -> None:
         if stored_key != key:
             # Key collision, oh well.
-            from warnings import warn
             warn(f"{self.identifier}: key collision in cache at "
                     f"'{self.container_dir}' -- these are sufficiently unlikely "
                     "that they're often indicative of a broken hash key "
@@ -571,7 +574,6 @@ class _PersistentDictBase(Mapping[K, V]):
                             and not e.sqlite_errorcode == sqlite3.SQLITE_BUSY):
                         raise
                     if n % 20 == 0:
-                        from warnings import warn
                         warn(f"PersistentDict: database '{self.filename}' busy, {n} "
                              "retries", stacklevel=3)
                 else:
-- 
GitLab