commit af8ce95e22407bc22082fffecd3cf125dd833abc
parent d68286ee6ca7366ba80fa7af95198016369ea9bb
Author: lash <dev@holbrook.no>
Date: Wed, 16 Mar 2022 16:49:00 +0000
Optional allow undefined alias states
Diffstat:
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')