commit 9ad005ae420aab43253df8611406cb18bc0a8657
parent 9f71401bb5073e0c610ff20bdb594b4eb7e0bde0
Author: lash <dev@holbrook.no>
Date: Fri, 11 Mar 2022 10:31:08 +0000
Add verifier
Diffstat:
6 files changed, 28 insertions(+), 6 deletions(-)
diff --git a/CHANGELOG b/CHANGELOG
@@ -1,3 +1,7 @@
+- 0.1.1
+ * Add optional, pluggable verifier to protect state transition
+- 0.1.0
+ * Release version bump
- 0.0.19:
* Enable alias with comma separated values
- 0.0.18
diff --git a/setup.cfg b/setup.cfg
@@ -1,6 +1,6 @@
[metadata]
name = shep
-version = 0.1.0rc1
+version = 0.1.1rc1
description = Multi-state key stores using bit masks
author = Louis Holbrook
author_email = dev@holbrook.no
diff --git a/shep/error.py b/shep/error.py
@@ -26,3 +26,9 @@ class StateCorruptionError(RuntimeError):
"""An irrecoverable discrepancy between persisted state and memory state has occurred.
"""
pass
+
+
+class StateTransitionInvalid(Exception):
+ """Raised if state transition verification fails
+ """
+ pass
diff --git a/shep/state.py b/shep/state.py
@@ -1,12 +1,18 @@
+# standard imports
+import re
+
# local imports
from shep.error import (
StateExists,
StateInvalid,
StateItemExists,
StateItemNotFound,
+ StateTransitionInvalid,
)
+re_name = r'^[a-zA-Z_]+$'
+
class State:
"""State is an in-memory bitmasked state store for key-value pairs, or even just keys alone.
@@ -22,17 +28,17 @@ class State:
base_state_name = 'NEW'
- def __init__(self, bits, logger=None):
+ def __init__(self, bits, logger=None, verifier=None):
self.__bits = bits
self.__limit = (1 << bits) - 1
self.__c = 0
setattr(self, self.base_state_name, 0)
- #self.NEW = 0
self.__reverse = {0: getattr(self, self.base_state_name)}
self.__keys = {getattr(self, self.base_state_name): []}
self.__keys_reverse = {}
self.__contents = {}
+ self.verifier = verifier
@classmethod
@@ -54,8 +60,8 @@ class State:
# validates a state name and return its canonical representation
def __check_name_valid(self, k):
- if not k.isalpha():
- raise ValueError('only alpha')
+ if not re.match(re_name, k):
+ raise ValueError('only alpha and underscore')
return k.upper()
@@ -323,6 +329,11 @@ class State:
if current_state_list == None:
raise StateCorruptionError(to_state)
+ if self.verifier != None:
+ r = self.verifier(self, from_state, to_state)
+ if r != None:
+ raise StateTransitionInvalid('{} -> {}: {}'.format(from_state, to_state, r))
+
self.__add_state_list(to_state, key)
current_state_list.pop(idx)
diff --git a/shep/verify.py b/shep/verify.py
@@ -0,0 +1,2 @@
+def default_checker(statestore, old, new):
+ return None
diff --git a/tests/test_state.py b/tests/test_state.py
@@ -18,7 +18,6 @@ class TestState(unittest.TestCase):
for k in [
'f0o',
'f oo',
- 'f_oo',
]:
with self.assertRaises(ValueError):
states.add(k)