Add item lifetime controller interface
This commit is contained in:
parent
d2bca51342
commit
0eaf032b89
@ -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
|
||||
|
@ -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
|
||||
|
@ -4,3 +4,15 @@ class StateExists(Exception):
|
||||
|
||||
class StateInvalid(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class StateItemExists(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class StateItemNotFound(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class StateCorruptionError(RuntimeError):
|
||||
pass
|
||||
|
@ -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
79
tests/test_item.py
Normal 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()
|
Loading…
Reference in New Issue
Block a user