You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
134 lines
3.2 KiB
134 lines
3.2 KiB
import six
|
|
import pickle
|
|
|
|
from six.moves import map, UserDict
|
|
|
|
|
|
ROOT_SENTINEL = u'\u200B'
|
|
SEP_SENTINEL = u'\u200D'
|
|
OBJ_SENTINEL = u'\u200C'
|
|
CAST_SENTINEL = u'\u24EA'
|
|
|
|
|
|
def join_key(*args):
|
|
nargs = []
|
|
for arg in args:
|
|
if not isinstance(arg, six.string_types):
|
|
arg = CAST_SENTINEL + pickle.dumps(arg)
|
|
nargs.append(arg)
|
|
return SEP_SENTINEL.join(nargs)
|
|
|
|
|
|
def true_key(key):
|
|
key = key.rsplit(SEP_SENTINEL, 1)[-1]
|
|
if key.startswith(CAST_SENTINEL):
|
|
return pickle.loads(key)
|
|
return key
|
|
|
|
|
|
class BaseProvider(object):
|
|
def __init__(self, config):
|
|
self.config = config
|
|
self.data = {}
|
|
|
|
def exists(self, key):
|
|
return key in self.data
|
|
|
|
def keys(self, other):
|
|
count = other.count(SEP_SENTINEL) + 1
|
|
for key in self.data.keys():
|
|
if key.startswith(other) and key.count(SEP_SENTINEL) == count:
|
|
yield key
|
|
|
|
def get_many(self, keys):
|
|
for key in keys:
|
|
yield key, self.get(key)
|
|
|
|
def get(self, key):
|
|
return self.data[key]
|
|
|
|
def set(self, key, value):
|
|
self.data[key] = value
|
|
|
|
def delete(self, key):
|
|
del self.data[key]
|
|
|
|
def load(self):
|
|
pass
|
|
|
|
def save(self):
|
|
pass
|
|
|
|
def root(self):
|
|
return StorageDict(self)
|
|
|
|
|
|
class StorageDict(UserDict):
|
|
def __init__(self, parent_or_provider, key=None):
|
|
if isinstance(parent_or_provider, BaseProvider):
|
|
self.provider = parent_or_provider
|
|
self.parent = None
|
|
else:
|
|
self.parent = parent_or_provider
|
|
self.provider = self.parent.provider
|
|
self._key = key or ROOT_SENTINEL
|
|
|
|
def keys(self):
|
|
return map(true_key, self.provider.keys(self.key))
|
|
|
|
def values(self):
|
|
for key in self.keys():
|
|
yield self.provider.get(key)
|
|
|
|
def items(self):
|
|
for key in self.keys():
|
|
yield (true_key(key), self.provider.get(key))
|
|
|
|
def ensure(self, key, typ=dict):
|
|
if key not in self:
|
|
self[key] = typ()
|
|
return self[key]
|
|
|
|
def update(self, obj):
|
|
for k, v in six.iteritems(obj):
|
|
self[k] = v
|
|
|
|
@property
|
|
def data(self):
|
|
obj = {}
|
|
|
|
for raw, value in self.provider.get_many(self.provider.keys(self.key)):
|
|
key = true_key(raw)
|
|
|
|
if value == OBJ_SENTINEL:
|
|
value = self.__class__(self, key=key).data
|
|
obj[key] = value
|
|
return obj
|
|
|
|
@property
|
|
def key(self):
|
|
if self.parent is not None:
|
|
return join_key(self.parent.key, self._key)
|
|
return self._key
|
|
|
|
def __setitem__(self, key, value):
|
|
if isinstance(value, dict):
|
|
obj = self.__class__(self, key)
|
|
obj.update(value)
|
|
value = OBJ_SENTINEL
|
|
|
|
self.provider.set(join_key(self.key, key), value)
|
|
|
|
def __getitem__(self, key):
|
|
res = self.provider.get(join_key(self.key, key))
|
|
|
|
if res == OBJ_SENTINEL:
|
|
return self.__class__(self, key)
|
|
|
|
return res
|
|
|
|
def __delitem__(self, key):
|
|
return self.provider.delete(join_key(self.key, key))
|
|
|
|
def __contains__(self, key):
|
|
return self.provider.exists(join_key(self.key, key))
|
|
|