From dea0d8780a2d42ea6c46938bd11bfe5f460cb244 Mon Sep 17 00:00:00 2001
From: Matthias Diener <mdiener@illinois.edu>
Date: Mon, 27 Nov 2023 14:20:39 -0600
Subject: [PATCH] persistent_dict: separate different sources of KeyErrors
 (#189)

* persistent_dict: separate different sources of KeyErrors

* add to doc
---
 pytools/persistent_dict.py           | 51 +++++++++++++++++++---------
 pytools/test/test_persistent_dict.py | 10 +++---
 2 files changed, 40 insertions(+), 21 deletions(-)

diff --git a/pytools/persistent_dict.py b/pytools/persistent_dict.py
index 65fb6e9..e8f1d7e 100644
--- a/pytools/persistent_dict.py
+++ b/pytools/persistent_dict.py
@@ -57,6 +57,9 @@ valid across interpreter invocations, unlike Python's built-in hashes.
 This module also provides a disk-backed dictionary that uses persistent hashing.
 
 .. autoexception:: NoSuchEntryError
+.. autoexception:: NoSuchEntryInvalidKeyError
+.. autoexception:: NoSuchEntryInvalidContentsError
+.. autoexception:: NoSuchEntryCollisionError
 .. autoexception:: ReadOnlyEntryError
 
 .. autoexception:: CollisionWarning
@@ -528,6 +531,18 @@ class NoSuchEntryError(KeyError):
     pass
 
 
+class NoSuchEntryInvalidKeyError(NoSuchEntryError):
+    pass
+
+
+class NoSuchEntryInvalidContentsError(NoSuchEntryError):
+    pass
+
+
+class NoSuchEntryCollisionError(NoSuchEntryError):
+    pass
+
+
 class ReadOnlyEntryError(KeyError):
     pass
 
@@ -629,7 +644,7 @@ class _PersistentDictBase:
             # This is here so we can step through equality comparison to
             # see what is actually non-equal.
             stored_key == key  # pylint:disable=pointless-statement  # noqa: B015
-            raise NoSuchEntryError(key)
+            raise NoSuchEntryCollisionError(key)
 
     def __getitem__(self, key):
         return self.fetch(key, _stacklevel=1)
@@ -783,7 +798,7 @@ class WriteOncePersistentDict(_PersistentDictBase):
                     f"Remove the directory '{item_dir}' if necessary. "
                     f"(caught: {type(e).__name__}: {e})",
                     stacklevel=1 + _stacklevel)
-            raise NoSuchEntryError(key)
+            raise NoSuchEntryInvalidKeyError(key)
 
         self._collision_check(key, read_key, 1 + _stacklevel)
 
@@ -796,12 +811,13 @@ class WriteOncePersistentDict(_PersistentDictBase):
 
         try:
             read_contents = self._read(contents_file)
-        except Exception:
+        except Exception as e:
             self._warn(f"{type(self).__name__}({self.identifier}) "
-                    f"encountered an invalid key file for key {hexdigest_key}. "
-                    f"Remove the directory '{item_dir}' if necessary.",
+                    f"encountered an invalid contents file for key {hexdigest_key}. "
+                    f"Remove the directory '{item_dir}' if necessary."
+                    f"(caught: {type(e).__name__}: {e})",
                     stacklevel=1 + _stacklevel)
-            raise NoSuchEntryError(key)
+            raise NoSuchEntryInvalidContentsError(key)
 
         # }}}
 
@@ -892,13 +908,14 @@ class PersistentDict(_PersistentDictBase):
 
                 try:
                     read_key = self._read(key_path)
-                except Exception:
+                except Exception as e:
                     item_dir_m.reset()
                     self._warn(f"{type(self).__name__}({self.identifier}) "
                             "encountered an invalid key file for key "
-                            f"{hexdigest_key}. Entry deleted.",
+                            f"{hexdigest_key}. Entry deleted."
+                            f"(caught: {type(e).__name__}: {e})",
                             stacklevel=1 + _stacklevel)
-                    raise NoSuchEntryError(key)
+                    raise NoSuchEntryInvalidKeyError(key)
 
                 self._collision_check(key, read_key, 1 + _stacklevel)
 
@@ -911,13 +928,14 @@ class PersistentDict(_PersistentDictBase):
 
                 try:
                     read_contents = self._read(value_path)
-                except Exception:
+                except Exception as e:
                     item_dir_m.reset()
                     self._warn(f"{type(self).__name__}({self.identifier}) "
-                            "encountered an invalid key file for key "
-                            f"{hexdigest_key}. Entry deleted.",
+                            "encountered an invalid contents file for key "
+                            f"{hexdigest_key}. Entry deleted."
+                            f"(caught: {type(e).__name__}: {e})",
                             stacklevel=1 + _stacklevel)
-                    raise NoSuchEntryError(key)
+                    raise NoSuchEntryInvalidContentsError(key)
 
                 return read_contents
 
@@ -950,13 +968,14 @@ class PersistentDict(_PersistentDictBase):
 
                 try:
                     read_key = self._read(key_file)
-                except Exception:
+                except Exception as e:
                     item_dir_m.reset()
                     self._warn(f"{type(self).__name__}({self.identifier}) "
                             "encountered an invalid key file for key "
-                            f"{hexdigest_key}. Entry deleted",
+                            f"{hexdigest_key}. Entry deleted"
+                            f"(caught: {type(e).__name__}: {e})",
                             stacklevel=1 + _stacklevel)
-                    raise NoSuchEntryError(key)
+                    raise NoSuchEntryInvalidKeyError(key)
 
                 self._collision_check(key, read_key, 1 + _stacklevel)
 
diff --git a/pytools/test/test_persistent_dict.py b/pytools/test/test_persistent_dict.py
index 2c3aeab..8557682 100644
--- a/pytools/test/test_persistent_dict.py
+++ b/pytools/test/test_persistent_dict.py
@@ -7,8 +7,8 @@ from enum import Enum, IntEnum
 import pytest
 
 from pytools.persistent_dict import (
-    CollisionWarning, KeyBuilder, NoSuchEntryError, PersistentDict,
-    ReadOnlyEntryError, WriteOncePersistentDict)
+    CollisionWarning, KeyBuilder, NoSuchEntryCollisionError, NoSuchEntryError,
+    PersistentDict, ReadOnlyEntryError, WriteOncePersistentDict)
 from pytools.tag import Tag, tag_dataclass
 
 
@@ -214,12 +214,12 @@ def test_persistent_dict_cache_collisions():
 
         # check lookup
         with pytest.warns(CollisionWarning):
-            with pytest.raises(NoSuchEntryError):
+            with pytest.raises(NoSuchEntryCollisionError):
                 pdict.fetch(key2)
 
         # check deletion
         with pytest.warns(CollisionWarning):
-            with pytest.raises(NoSuchEntryError):
+            with pytest.raises(NoSuchEntryCollisionError):
                 del pdict[key2]
 
         # check presence after deletion
@@ -342,7 +342,7 @@ def test_write_once_persistent_dict_cache_collisions():
 
         # check lookup
         with pytest.warns(CollisionWarning):
-            with pytest.raises(NoSuchEntryError):
+            with pytest.raises(NoSuchEntryCollisionError):
                 pdict.fetch(key2)
 
         # check update
-- 
GitLab