Add verifier

This commit is contained in:
lash 2022-03-11 10:31:08 +00:00
parent 9f71401bb5
commit 9ad005ae42
Signed by: lash
GPG Key ID: 21D2E7BB88C2A746
6 changed files with 28 additions and 6 deletions

View File

@ -1,3 +1,7 @@
- 0.1.1
* Add optional, pluggable verifier to protect state transition
- 0.1.0
* Release version bump
- 0.0.19: - 0.0.19:
* Enable alias with comma separated values * Enable alias with comma separated values
- 0.0.18 - 0.0.18

View File

@ -1,6 +1,6 @@
[metadata] [metadata]
name = shep name = shep
version = 0.1.0rc1 version = 0.1.1rc1
description = Multi-state key stores using bit masks description = Multi-state key stores using bit masks
author = Louis Holbrook author = Louis Holbrook
author_email = dev@holbrook.no author_email = dev@holbrook.no

View File

@ -26,3 +26,9 @@ class StateCorruptionError(RuntimeError):
"""An irrecoverable discrepancy between persisted state and memory state has occurred. """An irrecoverable discrepancy between persisted state and memory state has occurred.
""" """
pass pass
class StateTransitionInvalid(Exception):
"""Raised if state transition verification fails
"""
pass

View File

@ -1,12 +1,18 @@
# standard imports
import re
# local imports # local imports
from shep.error import ( from shep.error import (
StateExists, StateExists,
StateInvalid, StateInvalid,
StateItemExists, StateItemExists,
StateItemNotFound, StateItemNotFound,
StateTransitionInvalid,
) )
re_name = r'^[a-zA-Z_]+$'
class State: class State:
"""State is an in-memory bitmasked state store for key-value pairs, or even just keys alone. """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' base_state_name = 'NEW'
def __init__(self, bits, logger=None): def __init__(self, bits, logger=None, verifier=None):
self.__bits = bits self.__bits = bits
self.__limit = (1 << bits) - 1 self.__limit = (1 << bits) - 1
self.__c = 0 self.__c = 0
setattr(self, self.base_state_name, 0) setattr(self, self.base_state_name, 0)
#self.NEW = 0
self.__reverse = {0: getattr(self, self.base_state_name)} self.__reverse = {0: getattr(self, self.base_state_name)}
self.__keys = {getattr(self, self.base_state_name): []} self.__keys = {getattr(self, self.base_state_name): []}
self.__keys_reverse = {} self.__keys_reverse = {}
self.__contents = {} self.__contents = {}
self.verifier = verifier
@classmethod @classmethod
@ -54,8 +60,8 @@ class State:
# validates a state name and return its canonical representation # validates a state name and return its canonical representation
def __check_name_valid(self, k): def __check_name_valid(self, k):
if not k.isalpha(): if not re.match(re_name, k):
raise ValueError('only alpha') raise ValueError('only alpha and underscore')
return k.upper() return k.upper()
@ -323,6 +329,11 @@ class State:
if current_state_list == None: if current_state_list == None:
raise StateCorruptionError(to_state) 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) self.__add_state_list(to_state, key)
current_state_list.pop(idx) current_state_list.pop(idx)

2
shep/verify.py Normal file
View File

@ -0,0 +1,2 @@
def default_checker(statestore, old, new):
return None

View File

@ -18,7 +18,6 @@ class TestState(unittest.TestCase):
for k in [ for k in [
'f0o', 'f0o',
'f oo', 'f oo',
'f_oo',
]: ]:
with self.assertRaises(ValueError): with self.assertRaises(ValueError):
states.add(k) states.add(k)