shep

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

commit 10f8617babdc82190866b4ca1bf508348c9864e0
parent e94f366d3cc31007ac843af103e1431f85b1bfcd
Author: lash <dev@holbrook.no>
Date:   Tue,  1 Feb 2022 07:47:07 +0000

Add cumulative bit changes

Diffstat:
MCHANGELOG | 2++
Msetup.cfg | 2+-
Mshep/state.py | 42++++++++++++++++++++++++++++++++++++++++--
Mtests/test_item.py | 34++++++++++++++++++++++++++++++++++
Mtests/test_state.py | 1+
5 files changed, 78 insertions(+), 3 deletions(-)

diff --git a/CHANGELOG b/CHANGELOG @@ -1,3 +1,5 @@ +- 0.0.8 + * Add single bit transition to aliases - 0.0.7 * Add content for keys - 0.0.6 diff --git a/setup.cfg b/setup.cfg @@ -1,6 +1,6 @@ [metadata] name = shep -version = 0.0.7 +version = 0.0.8 description = Multi-state key stores using bit masks author = Louis Holbrook author_email = dev@holbrook.no diff --git a/shep/state.py b/shep/state.py @@ -172,7 +172,11 @@ class State: if new_state == None: raise StateInvalid(to_state) - current_state_list = self.__keys.get(current_state) + self.__move(key, current_state, to_state) + + + def __move(self, key, from_state, to_state): + current_state_list = self.__keys.get(from_state) if current_state_list == None: raise StateCorruptionError(current_state) @@ -186,12 +190,46 @@ class State: current_state_list.pop(idx) + def set(self, key, or_state): + if not self.__is_pure(or_state): + raise ValueError('can only apply using single bit states') + + current_state = self.__keys_reverse.get(key) + if current_state == None: + raise StateItemNotFound(key) + + to_state = current_state | or_state + new_state = self.__reverse.get(to_state) + if new_state == None: + raise StateInvalid('resulting to state is unknown: {}'.format(to_state)) + + self.__move(key, current_state, to_state) + + + def unset(self, key, not_state): + if not self.__is_pure(not_state): + raise ValueError('can only apply using single bit states') + + current_state = self.__keys_reverse.get(key) + if current_state == None: + raise StateItemNotFound(key) + + to_state = current_state & (~not_state) + if to_state == current_state: + raise ValueError('invalid change for state {}: {}'.format(key, not_state)) + + new_state = self.__reverse.get(to_state) + if new_state == None: + raise StateInvalid('resulting to state is unknown: {}'.format(to_state)) + + self.__move(key, current_state, to_state) + + def purge(self, key): current_state = self.__keys_reverse.get(key) if current_state == None: raise StateItemNotFound(key) del self.__keys_reverse[key] - current_state_list = self.__keys.get(current_state) idx = self.__state_list_index(key, current_state_list) diff --git a/tests/test_item.py b/tests/test_item.py @@ -82,5 +82,39 @@ class TestStateItems(unittest.TestCase): self.assertEqual(v, 'bar') + def test_item_set(self): + item = b'foo' + self.states.put(item, self.states.FOO) + self.states.set(item, self.states.BAR) + self.assertEqual(self.states.state(item), self.states.PLUGH) + + + def test_item_set_invalid(self): + item = b'foo' + self.states.put(item, self.states.FOO) + with self.assertRaises(StateInvalid): + self.states.set(item, self.states.BAZ) + + item = b'bar' + self.states.put(item, self.states.BAR) + with self.assertRaises(ValueError): + self.states.set(item, self.states.XYZZY) + + + def test_item_set_invalid(self): + item = b'foo' + self.states.put(item, self.states.XYZZY) + self.states.unset(item, self.states.BAZ) + self.assertEqual(self.states.state(item), self.states.BAR) + + item = b'bar' + self.states.put(item, self.states.XYZZY) + with self.assertRaises(ValueError): + self.states.unset(item, self.states.PLUGH) + + with self.assertRaises(ValueError): + self.states.unset(item, self.states.FOO) # bit not set + + if __name__ == '__main__': unittest.main() diff --git a/tests/test_state.py b/tests/test_state.py @@ -74,6 +74,7 @@ class TestState(unittest.TestCase): states.add('bar') with self.assertRaises(StateInvalid): states.alias('baz', 5) + if __name__ == '__main__':