Add support for collections.OrderedDict and collections.deque to Attribute serializer mechanism. Also some cleanups in dbserializer.

This commit is contained in:
Griatch 2016-08-31 22:45:57 +02:00
parent 80befa96b6
commit 88b217c0b8
2 changed files with 112 additions and 21 deletions

View file

@ -144,6 +144,7 @@ class Attribute(SharedMemoryModel):
see self.__value_get. see self.__value_get.
""" """
self.db_value = to_pickle(new_value) self.db_value = to_pickle(new_value)
#print "value_set, self.db_value:", repr(self.db_value)
self.save(update_fields=["db_value"]) self.save(update_fields=["db_value"])
#@value.deleter #@value.deleter

View file

@ -22,6 +22,7 @@ from builtins import object, int
from functools import update_wrapper from functools import update_wrapper
from collections import defaultdict, MutableSequence, MutableSet, MutableMapping from collections import defaultdict, MutableSequence, MutableSet, MutableMapping
from collections import OrderedDict, deque
try: try:
from cPickle import dumps, loads from cPickle import dumps, loads
except ImportError: except ImportError:
@ -127,8 +128,8 @@ class _SaverMutable(object):
""" """
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
"store all properties for tracking the tree" "store all properties for tracking the tree"
self._parent = kwargs.pop("parent", None) self._parent = kwargs.pop("_parent", None)
self._db_obj = kwargs.pop("db_obj", None) self._db_obj = kwargs.pop("_db_obj", None)
self._data = None self._data = None
def __nonzero__(self): def __nonzero__(self):
@ -145,22 +146,22 @@ class _SaverMutable(object):
logger.log_err("_SaverMutable %s has no root Attribute to save to." % self) logger.log_err("_SaverMutable %s has no root Attribute to save to." % self)
def _convert_mutables(self, data): def _convert_mutables(self, data):
"converts mutables to Saver* variants and assigns .parent property" "converts mutables to Saver* variants and assigns ._parent property"
def process_tree(item, parent): def process_tree(item, parent):
"recursively populate the tree, storing parents" "recursively populate the tree, storing parents"
dtype = type(item) dtype = type(item)
if dtype in (basestring, int, float, bool, tuple): if dtype in (basestring, int, float, bool, tuple):
return item return item
elif dtype == list: elif dtype == list:
dat = _SaverList(parent=parent) dat = _SaverList(_parent=parent)
dat._data.extend(process_tree(val, dat) for val in item) dat._data.extend(process_tree(val, dat) for val in item)
return dat return dat
elif dtype == dict: elif dtype == dict:
dat = _SaverDict(parent=parent) dat = _SaverDict(_parent=parent)
dat._data.update((key, process_tree(val, dat)) for key, val in item.items()) dat._data.update((key, process_tree(val, dat)) for key, val in item.items())
return dat return dat
elif dtype == set: elif dtype == set:
dat = _SaverSet(parent=parent) dat = _SaverSet(_parent=parent)
dat._data.update(process_tree(val, dat) for val in item) dat._data.update(process_tree(val, dat) for val in item)
return dat return dat
return item return item
@ -178,6 +179,9 @@ class _SaverMutable(object):
def __getitem__(self, key): def __getitem__(self, key):
return self._data.__getitem__(key) return self._data.__getitem__(key)
def __eq__(self, other):
return self._data == other
@_save @_save
def __setitem__(self, key, value): def __setitem__(self, key, value):
self._data.__setitem__(key, self._convert_mutables(value)) self._data.__setitem__(key, self._convert_mutables(value))
@ -193,7 +197,7 @@ class _SaverList(_SaverMutable, MutableSequence):
""" """
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(_SaverList, self).__init__(*args, **kwargs) super(_SaverList, self).__init__(*args, **kwargs)
self._data = list(*args) self._data = list()
@_save @_save
def __add__(self, otherlist): def __add__(self, otherlist):
@ -214,13 +218,14 @@ class _SaverList(_SaverMutable, MutableSequence):
return self._data.index(value, *args) return self._data.index(value, *args)
class _SaverDict(_SaverMutable, MutableMapping): class _SaverDict(_SaverMutable, MutableMapping):
""" """
A dict that stores changes to an Attribute when updated A dict that stores changes to an Attribute when updated
""" """
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(_SaverDict, self).__init__(*args, **kwargs) super(_SaverDict, self).__init__(*args, **kwargs)
self._data = dict(*args) self._data = dict()
def has_key(self, key): def has_key(self, key):
return key in self._data return key in self._data
@ -232,7 +237,7 @@ class _SaverSet(_SaverMutable, MutableSet):
""" """
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(_SaverSet, self).__init__(*args, **kwargs) super(_SaverSet, self).__init__(*args, **kwargs)
self._data = set(*args) self._data = set()
def __contains__(self, value): def __contains__(self, value):
return self._data.__contains__(value) return self._data.__contains__(value)
@ -245,6 +250,64 @@ class _SaverSet(_SaverMutable, MutableSet):
def discard(self, value): def discard(self, value):
self._data.discard(value) self._data.discard(value)
class _SaverOrderedDict(_SaverMutable, MutableMapping):
"""
An ordereddict that can be saved and operated on.
"""
def __init__(self, *args, **kwargs):
super(_SaverOrderedDict, self).__init__(*args, **kwargs)
self._data = OrderedDict()
def has_key(self, key):
return key in self._data
class _SaverDeque(_SaverMutable):
"""
A deque that can be saved and operated on.
"""
def __init__(self, *args, **kwargs):
super(_SaverDeque, self).__init__(*args, **kwargs)
self._data = deque()
@_save
def appendleft(self, *args, **kwargs):
self._data.appendleft(*args, **kwargs)
@_save
def clear(self):
self._data.clear()
@_save
def extendleft(self, *args, **kwargs):
self._data.extendleft(*args, **kwargs)
# maxlen property
def _getmaxlen(self):
return self._data.maxlen
def _setmaxlen(self, value):
self._data.maxlen = value
def _delmaxlen(self):
del self._data.maxlen
maxlen = property(_getmaxlen, _setmaxlen, _delmaxlen)
@_save
def pop(self, *args, **kwargs):
return self._data.pop(*args, **kwargs)
@_save
def popleft(self, *args, **kwargs):
return self._data.popleft(*args, **kwargs)
@_save
def reverse(self):
self._data.reverse()
@_save
def rotate(self):
self._data.rotate()
# #
# serialization helpers # serialization helpers
# #
@ -376,6 +439,11 @@ def to_pickle(data):
return dict((process_item(key), process_item(val)) for key, val in item.items()) return dict((process_item(key), process_item(val)) for key, val in item.items())
elif dtype in (set, _SaverSet): elif dtype in (set, _SaverSet):
return set(process_item(val) for val in item) return set(process_item(val) for val in item)
elif dtype in (OrderedDict, _SaverOrderedDict):
return OrderedDict((process_item(key), process_item(val)) for key, val in item.items())
elif dtype in (deque, _SaverDeque):
return deque(process_item(val) for val in item)
elif hasattr(item, '__iter__'): elif hasattr(item, '__iter__'):
# we try to conserve the iterable class, if not convert to list # we try to conserve the iterable class, if not convert to list
try: try:
@ -426,6 +494,10 @@ def from_pickle(data, db_obj=None):
return dict((process_item(key), process_item(val)) for key, val in item.items()) return dict((process_item(key), process_item(val)) for key, val in item.items())
elif dtype == set: elif dtype == set:
return set(process_item(val) for val in item) return set(process_item(val) for val in item)
elif dtype == OrderedDict:
return OrderedDict((process_item(key), process_item(val)) for key, val in item.items())
elif dtype == deque:
return deque(process_item(val) for val in item)
elif hasattr(item, '__iter__'): elif hasattr(item, '__iter__'):
try: try:
# we try to conserve the iterable class if # we try to conserve the iterable class if
@ -446,25 +518,34 @@ def from_pickle(data, db_obj=None):
elif dtype == tuple: elif dtype == tuple:
return tuple(process_tree(val, item) for val in item) return tuple(process_tree(val, item) for val in item)
elif dtype == list: elif dtype == list:
dat = _SaverList(parent=parent) dat = _SaverList(_parent=parent)
dat._data.extend(process_tree(val, dat) for val in item) dat._data.extend(process_tree(val, dat) for val in item)
return dat return dat
elif dtype == dict: elif dtype == dict:
dat = _SaverDict(parent=parent) dat = _SaverDict(_parent=parent)
dat._data.update(dict((process_item(key), process_tree(val, dat)) dat._data.update((process_item(key), process_tree(val, dat))
for key, val in item.items())) for key, val in item.items())
return dat return dat
elif dtype == set: elif dtype == set:
dat = _SaverSet(parent=parent) dat = _SaverSet(_parent=parent)
dat._data.update(set(process_tree(val, dat) for val in item)) dat._data.update(set(process_tree(val, dat) for val in item))
return dat return dat
elif dtype == OrderedDict:
dat = _SaverOrderedDict(_parent=parent)
dat._data.update((process_item(key), process_tree(val, dat))
for key, val in item.items())
return dat
elif dtype == deque:
dat = _SaverDeque(_parent=parent)
dat._data.extend(process_item(val) for val in item)
return dat
elif hasattr(item, '__iter__'): elif hasattr(item, '__iter__'):
try: try:
# we try to conserve the iterable class if it # we try to conserve the iterable class if it
# accepts an iterator # accepts an iterator
return item.__class__(process_tree(val, parent) for val in item) return item.__class__(process_tree(val, parent) for val in item)
except (AttributeError, TypeError): except (AttributeError, TypeError):
dat = _SaverList(parent=parent) dat = _SaverList(_parent=parent)
dat._data.extend(process_tree(val, dat) for val in item) dat._data.extend(process_tree(val, dat) for val in item)
return dat return dat
return item return item
@ -474,17 +555,26 @@ def from_pickle(data, db_obj=None):
# is only relevant if the "root" is an iterable of the right type. # is only relevant if the "root" is an iterable of the right type.
dtype = type(data) dtype = type(data)
if dtype == list: if dtype == list:
dat = _SaverList(db_obj=db_obj) dat = _SaverList(_db_obj=db_obj)
dat._data.extend(process_tree(val, parent=dat) for val in data) dat._data.extend(process_tree(val, dat) for val in data)
return dat return dat
elif dtype == dict: elif dtype == dict:
dat = _SaverDict(db_obj=db_obj) dat = _SaverDict(_db_obj=db_obj)
dat._data.update((process_item(key), process_tree(val, parent=dat)) dat._data.update((process_item(key), process_tree(val, dat))
for key, val in data.items()) for key, val in data.items())
return dat return dat
elif dtype == set: elif dtype == set:
dat = _SaverSet(db_obj=db_obj) dat = _SaverSet(_db_obj=db_obj)
dat._data.update(process_tree(val, parent=dat) for val in data) dat._data.update(process_tree(val, dat) for val in data)
return dat
elif dtype == OrderedDict:
dat = _SaverOrderedDict(_db_obj=db_obj)
dat._data.update((process_item(key), process_tree(val, dat))
for key, val in data.items())
return dat
elif dtype == deque:
dat = _SaverDeque(_db_obj=db_obj)
dat._data.extend(process_item(val) for val in data)
return dat return dat
return process_item(data) return process_item(data)