diff --git a/objectbroker/aquisition.py b/objectbroker/Aquisition.py similarity index 100% rename from objectbroker/aquisition.py rename to objectbroker/Aquisition.py diff --git a/objectbroker/objectbroker.py b/objectbroker/ObjectBroker.py similarity index 100% rename from objectbroker/objectbroker.py rename to objectbroker/ObjectBroker.py diff --git a/objectbroker/objectstore.py b/objectbroker/ObjectStore.py similarity index 100% rename from objectbroker/objectstore.py rename to objectbroker/ObjectStore.py diff --git a/objectbroker/persistence.py b/objectbroker/Persistence.py similarity index 100% rename from objectbroker/persistence.py rename to objectbroker/Persistence.py diff --git a/objectbroker/Serializer.py b/objectbroker/Serializer.py new file mode 100644 index 0000000..7a9d8e8 --- /dev/null +++ b/objectbroker/Serializer.py @@ -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 . + Returns the state dict. + After method returns, 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 diff --git a/objectbroker/XMLSerializer.py b/objectbroker/XMLSerializer.py new file mode 100644 index 0000000..aa77afa --- /dev/null +++ b/objectbroker/XMLSerializer.py @@ -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){ + + } + diff --git a/objectbroker/XMLStore.py b/objectbroker/XMLStore.py new file mode 100644 index 0000000..fa5fd53 --- /dev/null +++ b/objectbroker/XMLStore.py @@ -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 \ No newline at end of file diff --git a/objectbroker/__init__.py b/objectbroker/__init__.py index f989ac1..d62bd62 100644 --- a/objectbroker/__init__.py +++ b/objectbroker/__init__.py @@ -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