shep

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

commit af8ce95e22407bc22082fffecd3cf125dd833abc
parent d68286ee6ca7366ba80fa7af95198016369ea9bb
Author: lash <dev@holbrook.no>
Date:   Wed, 16 Mar 2022 16:49:00 +0000

Optional allow undefined alias states

Diffstat:
MCHANGELOG | 5+++--
Mshep/persist.py | 13+++++++++++++
Mshep/state.py | 33++++++++++++++++++++++++---------
Mshep/store/file.py | 2+-
Mtests/test_state.py | 29+++++++++++++++++++++++++++--
5 files changed, 68 insertions(+), 14 deletions(-)

diff --git a/CHANGELOG b/CHANGELOG @@ -1,6 +1,7 @@ - 0.1.1 - * Add optional, pluggable verifier to protect state transition - * Add change method for atomic simultaneous set and unset + * Optional, pluggable verifier to protect state transition + * Change method for atomic simultaneous set and unset + * Optionally allow undefined composite states - 0.1.0 * Release version bump - 0.0.19: diff --git a/shep/persist.py b/shep/persist.py @@ -1,3 +1,6 @@ +# standard imports +import datetime + # local imports from .state import State from .error import StateItemExists @@ -95,6 +98,8 @@ class PersistedState(State): self.__stores[k_to].add(key, contents) self.__stores[k_from].remove(key) + self.register_modify(key) + return to_state @@ -119,6 +124,8 @@ class PersistedState(State): self.__stores[k_to].add(key, contents) self.__stores[k_from].remove(key) + self.register_modify(key) + return to_state @@ -192,3 +199,9 @@ class PersistedState(State): state = self.state(key) k = self.name(state) return self.__stores[k].replace(key, contents) + + + def modified(self, key): + state = self.state(key) + k = self.name(state) + return self.__stores[k].modified(key) diff --git a/shep/state.py b/shep/state.py @@ -30,7 +30,7 @@ class State: base_state_name = 'NEW' - def __init__(self, bits, logger=None, verifier=None): + def __init__(self, bits, logger=None, verifier=None, check_alias=True): self.__bits = bits self.__limit = (1 << bits) - 1 self.__c = 0 @@ -40,8 +40,9 @@ class State: self.__keys = {getattr(self, self.base_state_name): []} self.__keys_reverse = {} self.__contents = {} - self.__change = {} + self.modified_last = {} self.verifier = verifier + self.check_alias = check_alias @classmethod @@ -135,7 +136,6 @@ class State: if not self.__is_pure(state) or state == 0: self.__keys[state].append(item) c = 1 - import sys for i in range(self.__bits): part = c & state if part > 0: @@ -215,6 +215,18 @@ class State: return l + def elements(self, v): + r = [] + if v == None or v == 0: + return self.base_state_name + c = 1 + for i in range(1, self.__bits): + if v & c > 0: + r.append(self.name(c)) + c <<= 1 + return '*' + ','.join(r) + + def name(self, v): """Retrieve that string representation of the state attribute represented by the given state integer value. @@ -224,11 +236,14 @@ class State: :rtype: str :return: State name """ - if v == None or v == 0: - return self.base_state_name k = self.__reverse.get(v) if k == None: - raise StateInvalid(v) + if self.check_alias: + raise StateInvalid(v) + else: + k = self.elements(v) + elif v == None or v == 0: + return self.base_state_name return k @@ -379,7 +394,7 @@ class State: to_state = current_state | or_state new_state = self.__reverse.get(to_state) - if new_state == None: + if new_state == None and self.check_alias: raise StateInvalid('resulting to state is unknown: {}'.format(to_state)) return self.__move(key, current_state, to_state) @@ -558,8 +573,8 @@ class State: def modified(self, key): - return self.__change[key] + return self.modified_last[key] def register_modify(self, key): - self.__change[key] = datetime.datetime.now().timestamp() + self.modified_last[key] = datetime.datetime.now().timestamp() diff --git a/shep/store/file.py b/shep/store/file.py @@ -106,7 +106,7 @@ class SimpleFileStore: def modified(self, k): path = self.path(k) st = os.stat(path) - return float(st.st_ctime()) + return st.st_ctime def register_modify(self, k): diff --git a/tests/test_state.py b/tests/test_state.py @@ -1,5 +1,6 @@ # standard imports import unittest +import logging # local imports from shep import State @@ -8,6 +9,9 @@ from shep.error import ( StateInvalid, ) +logging.basicConfig(level=logging.DEBUG) +logg = logging.getLogger() + class TestState(unittest.TestCase): @@ -81,7 +85,29 @@ class TestState(unittest.TestCase): states.add('bar') with self.assertRaises(StateInvalid): states.alias('baz', 5) - + + + def test_alias_invalid(self): + states = State(3) + states.add('foo') + states.add('bar') + states.put('abcd') + states.set('abcd', states.FOO) + with self.assertRaises(StateInvalid): + states.set('abcd', states.BAR) + + + def test_alias_invalid_ignore(self): + states = State(3, check_alias=False) + states.add('foo') + states.add('bar') + states.put('abcd') + states.set('abcd', states.FOO) + states.set('abcd', states.BAR) + v = states.state('abcd') + s = states.name(v) + self.assertEqual(s, '*FOO,BAR') + def test_peek(self): states = State(3) @@ -106,7 +132,6 @@ class TestState(unittest.TestCase): self.assertEqual(states.from_name('foo'), states.FOO) - def test_change(self): states = State(3) states.add('foo')