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