2020-08-19 13:01:37 +00:00
|
|
|
import time
|
|
|
|
from itertools import takewhile
|
|
|
|
import operator
|
|
|
|
from collections import OrderedDict
|
|
|
|
from abc import abstractmethod, ABC
|
|
|
|
|
2020-08-20 19:45:10 +00:00
|
|
|
BSEP_ST = b'||||'
|
|
|
|
|
|
|
|
import base64
|
2020-08-20 18:54:31 +00:00
|
|
|
def xprint(*xx):
|
|
|
|
raise Exception('\n'.join(str(x) for x in xx))
|
|
|
|
|
|
|
|
|
2020-08-19 13:01:37 +00:00
|
|
|
|
|
|
|
class IStorage(ABC):
|
|
|
|
"""
|
|
|
|
Local storage for this node.
|
|
|
|
IStorage implementations of get must return the same type as put in by set
|
|
|
|
"""
|
|
|
|
|
|
|
|
@abstractmethod
|
|
|
|
def __setitem__(self, key, value):
|
|
|
|
"""
|
|
|
|
Set a key to the given value.
|
|
|
|
"""
|
|
|
|
|
|
|
|
@abstractmethod
|
|
|
|
def __getitem__(self, key):
|
|
|
|
"""
|
|
|
|
Get the given key. If item doesn't exist, raises C{KeyError}
|
|
|
|
"""
|
|
|
|
|
|
|
|
@abstractmethod
|
|
|
|
def get(self, key, default=None):
|
|
|
|
"""
|
|
|
|
Get given key. If not found, return default.
|
|
|
|
"""
|
|
|
|
|
|
|
|
@abstractmethod
|
|
|
|
def iter_older_than(self, seconds_old):
|
|
|
|
"""
|
|
|
|
Return the an iterator over (key, value) tuples for items older
|
|
|
|
than the given secondsOld.
|
|
|
|
"""
|
|
|
|
|
|
|
|
@abstractmethod
|
|
|
|
def __iter__(self):
|
|
|
|
"""
|
|
|
|
Get the iterator for this storage, should yield tuple of (key, value)
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
class ForgetfulStorage(IStorage):
|
|
|
|
def __init__(self, ttl=604800):
|
|
|
|
"""
|
|
|
|
By default, max age is a week.
|
|
|
|
"""
|
|
|
|
self.data = OrderedDict()
|
|
|
|
self.ttl = ttl
|
|
|
|
|
|
|
|
def __setitem__(self, key, value):
|
|
|
|
if key in self.data:
|
|
|
|
del self.data[key]
|
|
|
|
self.data[key] = (time.monotonic(), value)
|
|
|
|
self.cull()
|
|
|
|
|
|
|
|
def cull(self):
|
|
|
|
for _, _ in self.iter_older_than(self.ttl):
|
|
|
|
self.data.popitem(last=False)
|
|
|
|
|
|
|
|
def get(self, key, default=None):
|
|
|
|
self.cull()
|
|
|
|
if key in self.data:
|
|
|
|
return self[key]
|
|
|
|
return default
|
|
|
|
|
|
|
|
def __getitem__(self, key):
|
|
|
|
self.cull()
|
|
|
|
return self.data[key][1]
|
|
|
|
|
|
|
|
def __repr__(self):
|
|
|
|
self.cull()
|
|
|
|
return repr(self.data)
|
|
|
|
|
|
|
|
def iter_older_than(self, seconds_old):
|
|
|
|
min_birthday = time.monotonic() - seconds_old
|
|
|
|
zipped = self._triple_iter()
|
|
|
|
matches = takewhile(lambda r: min_birthday >= r[1], zipped)
|
|
|
|
return list(map(operator.itemgetter(0, 2), matches))
|
|
|
|
|
|
|
|
def _triple_iter(self):
|
|
|
|
ikeys = self.data.keys()
|
|
|
|
ibirthday = map(operator.itemgetter(0), self.data.values())
|
|
|
|
ivalues = map(operator.itemgetter(1), self.data.values())
|
|
|
|
return zip(ikeys, ibirthday, ivalues)
|
|
|
|
|
|
|
|
def __iter__(self):
|
|
|
|
self.cull()
|
|
|
|
ikeys = self.data.keys()
|
|
|
|
ivalues = map(operator.itemgetter(1), self.data.values())
|
|
|
|
return zip(ikeys, ivalues)
|
2020-08-20 18:54:31 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class HalfForgetfulStorage(ForgetfulStorage):
|
2020-08-20 20:01:00 +00:00
|
|
|
def __init__(self, fn='dbm', ttl=604800, log=print):
|
2020-08-20 18:54:31 +00:00
|
|
|
"""
|
|
|
|
By default, max age is a week.
|
|
|
|
"""
|
|
|
|
self.fn=fn
|
|
|
|
self.log=log
|
|
|
|
|
2020-08-20 19:45:10 +00:00
|
|
|
# import pickledb
|
|
|
|
# self.data = pickledb.load(self.fn,False)
|
|
|
|
import dbm
|
|
|
|
self.data = dbm.open(self.fn,flag='cs')
|
2020-08-20 18:54:31 +00:00
|
|
|
self.ttl = ttl
|
|
|
|
|
2020-08-20 20:01:00 +00:00
|
|
|
self.log('have %s keys' % len(self))
|
|
|
|
|
2020-08-20 18:54:31 +00:00
|
|
|
|
|
|
|
def keys(self):
|
2020-08-20 19:45:10 +00:00
|
|
|
# return self.data.getall()
|
|
|
|
return self.data.keys()
|
2020-08-20 18:54:31 +00:00
|
|
|
|
|
|
|
def __len__(self):
|
|
|
|
return len(self.keys())
|
|
|
|
|
|
|
|
def __setitem__(self, key, value):
|
|
|
|
self.set(key,value)
|
|
|
|
|
|
|
|
def set(self, key,value):# try:
|
2020-08-20 20:16:18 +00:00
|
|
|
self.log(f'key: {key},\nvalue:{value}')
|
2020-08-20 19:45:10 +00:00
|
|
|
time_b=str(time.monotonic()).encode()
|
2020-08-20 20:16:18 +00:00
|
|
|
if type(value)!=bytes:
|
|
|
|
value = str(json.dumps(value)).encode()
|
|
|
|
|
2020-08-20 19:45:10 +00:00
|
|
|
newdat = BSEP_ST.join([time_b, value])
|
|
|
|
self.data[key]=newdat
|
2020-08-20 20:01:00 +00:00
|
|
|
# return True
|
2020-08-20 18:54:31 +00:00
|
|
|
|
|
|
|
def get(self, key, default=None):
|
|
|
|
# print(f'??!?\n{key}\n{self.data[key]}')
|
|
|
|
# return self.data[key][1]
|
|
|
|
# (skip time part of tuple)
|
2020-08-20 19:45:10 +00:00
|
|
|
val=self.data[key] if key in self.data else None
|
|
|
|
self.log('VALLLL',val)
|
|
|
|
if val is None: return None
|
2020-08-20 18:54:31 +00:00
|
|
|
|
2020-08-20 19:45:10 +00:00
|
|
|
time_b,val_b = val.split(BSEP_ST)
|
|
|
|
rval = (float(time_b.decode()), val_b)
|
|
|
|
self.log('rvalll',rval)
|
2020-08-20 20:01:00 +00:00
|
|
|
return rval
|
2020-08-20 18:54:31 +00:00
|
|
|
|
|
|
|
def __getitem__(self, key):
|
|
|
|
return self.get(key)
|
|
|
|
|
|
|
|
#return data_list
|