Add item lifetime controller interface

This commit is contained in:
lash 2022-01-31 11:23:51 +00:00
parent d2bca51342
commit 0eaf032b89
Signed by: lash
GPG Key ID: 21D2E7BB88C2A746
5 changed files with 184 additions and 8 deletions

View File

@ -1,3 +1,5 @@
- 0.0.4
* Add item lifetime control
- 0.0.3
* Split match to 2-element tuple, returning complex value and simple values separately
- 0.0.2

View File

@ -1,6 +1,6 @@
[metadata]
name = shep
version = 0.0.3
version = 0.0.4
description = Multi-state key value stores using bitmaskings
author = Louis Holbrook
author_email = dev@holbrook.no

View File

@ -4,3 +4,15 @@ class StateExists(Exception):
class StateInvalid(Exception):
pass
class StateItemExists(Exception):
pass
class StateItemNotFound(Exception):
pass
class StateCorruptionError(RuntimeError):
pass

View File

@ -1,22 +1,23 @@
# standard imports
import enum
# local imports
from shep.error import (
StateExists,
StateInvalid,
StateItemExists,
StateItemNotFound,
)
class State:
def __init__(self, bits, logger=None, store=None):
def __init__(self, bits, logger=None, store_factory=None):
self.__bits = bits
self.__limit = (1 << bits) - 1
self.__c = 0
self.__reverse = {}
self.__logger = logger
self.__store = store
self.NEW = 0
self.__items = {self.NEW: []}
self.__items_reverse = {}
def __is_pure(self, v):
@ -28,9 +29,13 @@ class State:
return c == v
def __check_name(self, k):
def __check_name_valid(self, k):
if not k.isalpha():
raise ValueError('only alpha')
def __check_name(self, k):
self.__check_name_valid(k)
k = k.upper()
try:
getattr(self, k)
@ -67,6 +72,31 @@ class State:
self.__c += 1
def __check_item(self, item):
if self.__items_reverse.get(item) != None:
raise StateItemExists(item)
def __add_state_list(self, state, item):
if self.__items.get(state) == None:
self.__items[state] = []
self.__items[state].append(item)
self.__items_reverse[item] = state
def __state_list_index(self, item, state_list):
idx = -1
try:
idx = state_list.index(item)
except ValueError:
pass
if idx == -1:
raise StateCorruptionError() # should have state int here as value
return idx
def add(self, k):
v = 1 << self.__c
k = self.__check_name(k)
@ -111,3 +141,56 @@ class State:
c <<= 1
return (alias, r,)
def put(self, item, state=None):
if state == None:
state = self.NEW
elif self.__reverse.get(state) == None:
raise StateInvalid(state)
self.__check_item(item)
self.__add_state_list(state, item)
def move(self, item, to_state):
current_state = self.__items_reverse.get(item)
if current_state == None:
raise StateItemNotFound(item)
new_state = self.__reverse.get(to_state)
if new_state == None:
raise StateInvalid(to_state)
current_state_list = self.__items.get(current_state)
if current_state_list == None:
raise StateCorruptionError(current_state)
idx = self.__state_list_index(item, current_state_list)
new_state_list = self.__items.get(to_state)
if current_state_list == None:
raise StateCorruptionError(to_state)
self.__add_state_list(to_state, item)
current_state_list.pop(idx)
def purge(self, item):
current_state = self.__items_reverse.get(item)
if current_state == None:
raise StateItemNotFound(item)
del self.__items_reverse[item]
current_state_list = self.__items.get(current_state)
idx = self.__state_list_index(item, current_state_list)
current_state_list.pop(idx)
def state(self, item):
state = self.__items_reverse.get(item)
if state == None:
raise StateItemNotFound(item)
return state

79
tests/test_item.py Normal file
View File

@ -0,0 +1,79 @@
# standard imports
import unittest
# local imports
from shep import State
from shep.error import (
StateExists,
StateItemExists,
StateInvalid,
StateItemNotFound,
)
class TestStateItems(unittest.TestCase):
def setUp(self):
self.states = State(4)
self.states.add('foo')
self.states.add('bar')
self.states.add('baz')
self.states.alias('xyzzy', self.states.BAZ | self.states.BAR)
self.states.alias('plugh', self.states.FOO | self.states.BAR)
def test_put(self):
item = b'foo'
# put in initial (no) state
self.states.put(item)
with self.assertRaises(StateItemExists):
self.states.put(item)
with self.assertRaises(StateItemExists):
self.states.put(item, self.states.BAZ)
def test_item_state(self):
item = b'foo'
self.states.put(item, self.states.XYZZY)
self.assertEqual(self.states.state(item), self.states.XYZZY)
def test_item_move(self):
item = b'foo'
self.states.put(item, self.states.FOO)
self.states.move(item, self.states.BAR)
self.assertEqual(self.states.state(item), self.states.BAR)
def test_item_move_from_alias(self):
item = b'foo'
self.states.put(item, self.states.FOO)
self.states.move(item, self.states.XYZZY)
self.assertEqual(self.states.state(item), self.states.XYZZY)
self.states.move(item, self.states.BAR)
self.assertEqual(self.states.state(item), self.states.BAR)
def test_item_move_from_new(self):
item = b'foo'
self.states.put(item)
self.assertEqual(self.states.state(item), self.states.NEW)
self.states.move(item, self.states.XYZZY)
self.assertEqual(self.states.state(item), self.states.XYZZY)
def test_item_purge(self):
item = b'foo'
self.states.put(item, self.states.BAZ)
self.assertEqual(self.states.state(item), self.states.BAZ)
self.states.purge(item)
with self.assertRaises(StateItemNotFound):
self.states.state(item)
if __name__ == '__main__':
unittest.main()