master
Harald Wolff 2018-03-29 13:12:24 +02:00
parent b0ab3694e3
commit 60f402ceee
8 changed files with 195 additions and 4 deletions

View File

@ -0,0 +1,112 @@
import weakref
import collections
from objectbroker.Persistence import Persistence,NoPersistence
class Serializer:
"""Base class for object serialization with support for persistence"""
def __init__(self, objStore, objCache = weakref.WeakValueDictionary() ):
self.__objStore = objStore
self.__objCache = objCache
def serialize(self, o):
slist = {}
state = self.objectToState( o, slist )
self.__objStore.save(state, slist)
def unserialize(self, pid):
o = self.objectFromCache( pid )
if o is None:
state = self.__objStore.load( pid )
o = self.stateToObject( state )
return o
def objectToState(self, o, states = {} ):
"""Create a state dict for an object <o>.
Returns the state dict.
After method returns, <states> will contain entries for all referenced persistent objects."""
if not isinstance(o, Persistence):
raise TypeError("needs to be based on Persistence class to be used as persistent object")
if Persistence._persistence_id( o ) in states:
return states[ Persistence._persistence_id( o ) ]
state = {}
state["pid"] = Persistence._persistence_id( o )
state["module"] = o.__class__.__module__
state["class"] = o.__class__.__name__
state["fields"] = {}
state["pfields"] = {}
nopersistence = self.collectClassAttribute(o.__class__,"NOPERSIST")
persistnone = self.collectClassAttribute(o.__class__,"PERSISTNONE")
states[ state["pid"] ] = state
d = object.__getattribute__(o, "__dict__")
for n in d.keys():
a = getattr( o, n )
if (not isinstance( a, collections.Callable)) or isinstance( a, object ):
if (isinstance( a, NoPersistence)) or (n in nopersistence):
state["fields"][n] = a
elif n in persistnone:
state["fields"][n] = None
elif (isinstance( a, Persistence)):
state["pfields"][n] = self.objectToState( a, states )["pid"]
else:
state["fields"][n] = a
return state
def stateToObject(self, state):
"""Returns an object that is constructed of the given state.
If the cache already holds a reference to an object with the same persistence id,
this reference is returned without any alternation of the object."""
o = self.objFromCache( state["pid"] )
if not o is None:
return o
m = __import__(state["module"],fromlist=[state["class"]])
cls = getattr(m, state["class"] )
o = cls.__new__(cls)
objToCache( o, state["pid"] )
for n in state["fields"]:
setattr( o, n, state["fields"][n] )
for n in state["pfields"]:
setattr( o, n, self.stateToObject( state["pfields"][n] ))
return o
def objFromCache(self, pid):
if not self.__objCache is None:
if pid in self.__objCache:
return self.__objCache[ pid ]
return None
def objToCache(self, o, pid = None):
if pid is None:
pid = Persistence._persistence_id( o )
if not self.__objCache is None:
if not pid in self.__objCache:
self.__objCache[ pid ] = o
def collectClassAttribute(self, c, name, default = set() ):
collection = default
classes = (self.__class__,) + self.__class__.__bases__
for clazz in classes:
if hasattr(clazz, name):
if isinstance( collection, dict ):
collection.update( getattr( clazz, name ) )
else:
collection = collection + getattr( clazz, name )
return collection

View File

@ -0,0 +1,20 @@
from objectbroker.Serializer import Serializer
import os
import os.path
class XMLSerializer(Serializer):
def __init__(self, path, objCache = None):
Serializer.__init__(self, objCache)
def serialize(self, o):
raise Exception("Serializer needs to be subclassed")
def unserialize(self, bytes):
raise Exception("Serializer needs to be subclassed")
def serializeState(self, state){
}

View File

@ -0,0 +1,55 @@
import xml.etree.ElementTree as ET
import pickle
import os
import os.path
import base64
class XMLStore:
def __init__(self, path, splitFiles = None):
if splitFiles is None:
splitFiles = path is None or os.path.isdir( path )
self.__path = path
self.__split = splitFiles
self.__xml = None
def save(self, state, persistentStates = {}, xml = None):
print("save(self,{0},{1})".format(state,persistentStates))
po = ET.Element("PersistentObject")
po.attrib["id"] = state["pid"]
po.attrib["class"] = state["class"]
po.attrib["module"] = state["module"]
for field in state["fields"]:
attr = ET.SubElement(po,"Attribute")
attr.attrib["name"] = field
attr.attrib["protocol"] = "pickle"
attr.text = base64.b64encode( pickle.dumps(state["fields"][field]) ).decode("utf-8")
for pfield in state["pfields"]:
attr = ET.SubElement(po,"Attribute")
attr.attrib["name"] = pfield
attr.attrib["protocol"] = "persistence"
attr.text = state["pfields"][pfield]
if xml is None:
xml = ET.Element("PersistentObjects")
xml.attrib["root"] = state["pid"]
xml.append( po )
for pstate in persistentStates:
ps = persistentStates[ pstate ]
if not ps == state:
self.save( ps, xml = xml )
self.__xml = xml
def load(self, pid):
pass
def lastXml(self):
return self.__xml

View File

@ -1,6 +1,10 @@
from objectbroker.persistence import Persistence,NoPersistence
from objectbroker.objectstore import DiscObjectStore
from objectbroker.objectbroker import ObjectBroker
from objectbroker.aquisition import Aquisition, AquisitionProxy
from objectbroker.Persistence import Persistence,NoPersistence
from objectbroker.Serializer import Serializer
from objectbroker.XMLStore import XMLStore
from objectbroker.ObjectStore import DiscObjectStore
from objectbroker.ObjectBroker import ObjectBroker
from objectbroker.Aquisition import Aquisition, AquisitionProxy
from objectbroker.Proxy import InterceptedProxy