shep

Multi-state key stores using bit masks for python3
git clone git://git.defalsify.org/shep.git
Log | Files | Refs | LICENSE

commit 9ad005ae420aab43253df8611406cb18bc0a8657
parent 9f71401bb5073e0c610ff20bdb594b4eb7e0bde0
Author: lash <dev@holbrook.no>
Date:   Fri, 11 Mar 2022 10:31:08 +0000

Add verifier

Diffstat:
MCHANGELOG | 4++++
Msetup.cfg | 2+-
Mshep/error.py | 6++++++
Mshep/state.py | 19+++++++++++++++----
Ashep/verify.py | 2++
Mtests/test_state.py | 1-
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)