shep/shep/state.py

211 lines
4.9 KiB
Python
Raw Normal View History

2022-01-31 09:32:48 +01:00
# local imports
from shep.error import (
2022-01-31 09:38:14 +01:00
StateExists,
StateInvalid,
2022-01-31 12:23:51 +01:00
StateItemExists,
StateItemNotFound,
2022-01-31 09:38:14 +01:00
)
2022-01-31 09:32:48 +01:00
class State:
2022-01-31 13:10:04 +01:00
def __init__(self, bits, logger=None):
2022-01-31 09:32:48 +01:00
self.__bits = bits
2022-01-31 09:38:14 +01:00
self.__limit = (1 << bits) - 1
2022-01-31 09:32:48 +01:00
self.__c = 0
2022-01-31 12:23:51 +01:00
self.NEW = 0
2022-01-31 13:10:04 +01:00
self.__reverse = {0: self.NEW}
2022-02-01 07:29:51 +01:00
self.__keys = {self.NEW: []}
self.__keys_reverse = {}
self.__contents = {}
2022-01-31 09:32:48 +01:00
2022-01-31 09:38:14 +01:00
def __is_pure(self, v):
c = 1
for i in range(self.__bits):
if c & v > 0:
break
c <<= 1
return c == v
2022-01-31 09:32:48 +01:00
2022-01-31 12:23:51 +01:00
def __check_name_valid(self, k):
2022-01-31 10:12:49 +01:00
if not k.isalpha():
raise ValueError('only alpha')
2022-01-31 12:23:51 +01:00
def __check_name(self, k):
self.__check_name_valid(k)
2022-01-31 09:38:14 +01:00
k = k.upper()
2022-01-31 09:32:48 +01:00
try:
getattr(self, k)
raise StateExists(k)
except AttributeError:
pass
2022-01-31 09:38:14 +01:00
return k
2022-01-31 09:32:48 +01:00
def __check_valid(self, v):
2022-01-31 09:38:14 +01:00
v = int(v)
if self.__reverse.get(v):
2022-01-31 10:55:56 +01:00
raise StateExists(v)
return v
def __check_value(self, v):
v = self.__check_valid(v)
2022-01-31 09:38:14 +01:00
if v > self.__limit:
raise OverflowError(v)
return v
def __check_value_cursor(self, v):
v = self.__check_valid(v)
if v > 1 << self.__c:
raise StateInvalid(v)
return v
2022-01-31 09:38:14 +01:00
def __set(self, k, v):
setattr(self, k, v)
self.__reverse[v] = k
2022-01-31 09:32:48 +01:00
self.__c += 1
2022-01-31 09:38:14 +01:00
2022-02-01 07:29:51 +01:00
def __check_key(self, item):
if self.__keys_reverse.get(item) != None:
2022-01-31 12:23:51 +01:00
raise StateItemExists(item)
def __add_state_list(self, state, item):
2022-02-01 07:29:51 +01:00
if self.__keys.get(state) == None:
self.__keys[state] = []
self.__keys[state].append(item)
self.__keys_reverse[item] = state
2022-01-31 12:23:51 +01:00
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
2022-01-31 09:38:14 +01:00
def add(self, k):
v = 1 << self.__c
k = self.__check_name(k)
v = self.__check_value(v)
2022-01-31 09:38:14 +01:00
self.__set(k, v)
def alias(self, k, v):
k = self.__check_name(k)
v = self.__check_value_cursor(v)
2022-01-31 09:38:14 +01:00
if self.__is_pure(v):
raise ValueError('use add to add pure values')
self.__set(k, v)
2022-01-31 10:12:49 +01:00
def all(self):
l = []
for k in dir(self):
if k[0] == '_':
continue
if k.upper() != k:
continue
l.append(k)
l.sort()
return l
2022-01-31 13:10:04 +01:00
def name(self, v):
if v == None:
return self.NEW
k = self.__reverse.get(v)
if k == None:
raise StateInvalid(v)
return k
2022-01-31 10:55:56 +01:00
def match(self, v, pure=False):
alias = None
2022-01-31 10:55:56 +01:00
if not pure:
alias = self.__reverse.get(v)
r = []
c = 1
for i in range(self.__bits):
if v & c > 0:
2022-01-31 10:55:56 +01:00
try:
k = self.__reverse[c]
r.append(k)
except KeyError:
pass
c <<= 1
return (alias, r,)
2022-01-31 12:23:51 +01:00
2022-02-01 07:29:51 +01:00
def put(self, key, state=None, contents=None):
2022-01-31 12:23:51 +01:00
if state == None:
state = self.NEW
elif self.__reverse.get(state) == None:
raise StateInvalid(state)
2022-02-01 07:29:51 +01:00
self.__check_key(key)
self.__add_state_list(state, key)
self.__contents[key] = contents
2022-01-31 12:23:51 +01:00
2022-02-01 07:29:51 +01:00
def move(self, key, to_state):
current_state = self.__keys_reverse.get(key)
2022-01-31 12:23:51 +01:00
if current_state == None:
2022-02-01 07:29:51 +01:00
raise StateItemNotFound(key)
2022-01-31 12:23:51 +01:00
new_state = self.__reverse.get(to_state)
if new_state == None:
raise StateInvalid(to_state)
2022-02-01 07:29:51 +01:00
current_state_list = self.__keys.get(current_state)
2022-01-31 12:23:51 +01:00
if current_state_list == None:
raise StateCorruptionError(current_state)
2022-02-01 07:29:51 +01:00
idx = self.__state_list_index(key, current_state_list)
2022-01-31 12:23:51 +01:00
2022-02-01 07:29:51 +01:00
new_state_list = self.__keys.get(to_state)
2022-01-31 12:23:51 +01:00
if current_state_list == None:
raise StateCorruptionError(to_state)
2022-02-01 07:29:51 +01:00
self.__add_state_list(to_state, key)
2022-01-31 12:23:51 +01:00
current_state_list.pop(idx)
2022-02-01 07:29:51 +01:00
def purge(self, key):
current_state = self.__keys_reverse.get(key)
2022-01-31 12:23:51 +01:00
if current_state == None:
2022-02-01 07:29:51 +01:00
raise StateItemNotFound(key)
del self.__keys_reverse[key]
2022-01-31 12:23:51 +01:00
2022-02-01 07:29:51 +01:00
current_state_list = self.__keys.get(current_state)
2022-01-31 12:23:51 +01:00
2022-02-01 07:29:51 +01:00
idx = self.__state_list_index(key, current_state_list)
2022-01-31 12:23:51 +01:00
current_state_list.pop(idx)
2022-02-01 07:29:51 +01:00
def state(self, key):
state = self.__keys_reverse.get(key)
2022-01-31 12:23:51 +01:00
if state == None:
2022-02-01 07:29:51 +01:00
raise StateItemNotFound(key)
2022-01-31 12:23:51 +01:00
return state
2022-02-01 08:01:57 +01:00
def get(self, key):
return self.__contents[key]