From 10246f07130eea761ebc7d83f9c68c5106005238 Mon Sep 17 00:00:00 2001 From: Harald Wolff Date: Fri, 9 Mar 2018 11:52:20 +0100 Subject: [PATCH] Initial Commit --- .gitignore | 2 + objectbroker/Proxy.py | 62 ++++++++++++++ objectbroker/__init__.py | 6 ++ objectbroker/aquisition.py | 161 +++++++++++++++++++++++++++++++++++ objectbroker/objectbroker.py | 115 +++++++++++++++++++++++++ objectbroker/objectstore.py | 25 ++++++ objectbroker/persistence.py | 11 +++ 7 files changed, 382 insertions(+) create mode 100644 .gitignore create mode 100644 objectbroker/Proxy.py create mode 100644 objectbroker/__init__.py create mode 100644 objectbroker/aquisition.py create mode 100644 objectbroker/objectbroker.py create mode 100644 objectbroker/objectstore.py create mode 100644 objectbroker/persistence.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..dde3895 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.DS_Store +*.pyc diff --git a/objectbroker/Proxy.py b/objectbroker/Proxy.py new file mode 100644 index 0000000..a9a8dbd --- /dev/null +++ b/objectbroker/Proxy.py @@ -0,0 +1,62 @@ + +def getter(attrib): + return lambda self, *args, **kwargs: getattr(object.__getattribute__( self, "_o"), attrib)(*args, **kwargs) + + +class InterceptedProxy: + _special_names=[ + '__abs__', '__add__', '__and__', '__call__', '__cmp__', '__coerce__', + '__contains__', '__delitem__', '__delslice__', '__div__', '__divmod__', + '__eq__', '__float__', '__floordiv__', '__ge__', '__getitem__', + '__getslice__', '__gt__', '__hash__', '__hex__', '__iadd__', '__iand__', + '__idiv__', '__idivmod__', '__ifloordiv__', '__ilshift__', '__imod__', + '__imul__', '__int__', '__invert__', '__ior__', '__ipow__', '__irshift__', + '__isub__', '__iter__', '__itruediv__', '__ixor__', '__le__', '__len__', + '__long__', '__lshift__', '__lt__', '__mod__', '__mul__', '__ne__', + '__neg__', '__oct__', '__or__', '__pos__', '__pow__', '__radd__', + '__rand__', '__rdiv__', '__rdivmod__', '__reduce__', '__reduce_ex__', + '__repr__', '__reversed__', '__rfloorfiv__', '__rlshift__', '__rmod__', + '__rmul__', '__ror__', '__rpow__', '__rrshift__', '__rshift__', '__rsub__', + '__rtruediv__', '__rxor__', '__setitem__', '__setslice__', '__sub__', + '__truediv__', '__xor__', 'next', + ] + + def _get_proxy_cls(cls,obj): + ocls = obj.__class__ + ns = {} + + for n in InterceptedProxy._special_names: + if (hasattr(ocls, n)): + ns[n] = getter(n) + return type( "InterceptedProxy(%s)" % (ocls.__name__,), (cls,), ns ) + + def __new__(cls,obj,listener): + pcls = InterceptedProxy._get_proxy_cls(cls,obj) + + inst = object.__new__( pcls ) + object.__setattr__( inst, "_o", obj) + object.__setattr__( inst, "_l", listener) + + return inst + + def __getattribute__(self,name): + v = getattr( object.__getattribute__( self, "_o"), name ) + return v + + def __setattr__(self,name,value): + o = object.__getattribute__( self, "_o") + setattr( o, name, value ) + object.__getattribute__( self, "_l").notify("__setattr__",o,name,value) + + def __delattr__(self,name): + delattr( object.__getattribute__( self, "_o"), name ) + + def __nonzero__(self): + return bool(object.__getattribute__(self, "_o")) + def __str__(self): + return str(object.__getattribute__(self, "_o")) + def __repr__(self): + return repr(object.__getattribute__(self, "_o")) + def __dir__(self): + return dir( object.__getattribute__(self, "_o") ) + diff --git a/objectbroker/__init__.py b/objectbroker/__init__.py new file mode 100644 index 0000000..f989ac1 --- /dev/null +++ b/objectbroker/__init__.py @@ -0,0 +1,6 @@ +from objectbroker.persistence import Persistence,NoPersistence +from objectbroker.objectstore import DiscObjectStore +from objectbroker.objectbroker import ObjectBroker +from objectbroker.aquisition import Aquisition, AquisitionProxy +from objectbroker.Proxy import InterceptedProxy + diff --git a/objectbroker/aquisition.py b/objectbroker/aquisition.py new file mode 100644 index 0000000..15e4bc2 --- /dev/null +++ b/objectbroker/aquisition.py @@ -0,0 +1,161 @@ +import collections + +__doc__ = """Einfache Objekt Aquisition für Python.""" + +class Aquisition: + """Markiert ein Objekt als Ziel von Aquisition durch AquisitionProxy""" + pass + + +def caller(inst,a): + return lambda *args,**kwargs: a.__func__(inst,*args,**kwargs) + +def call(self,method,*args,**kwargs): + m = getattr(object.__getattribute__( self, "_o"), method) + if hasattr(m,"__func__"): + return m.__func__(self,*args,**kwargs) + else: + return m(*args,**kwargs) + +def getter(attrib): + return lambda self, *args, **kwargs: call(self,attrib,*args, **kwargs) + + if hasattr(m, "__func__"): + return lambda self, *args, **kwargs: m.__func__(self, *args, **kwargs) + else: + return lambda self, *args, **kwargs: m(*args, **kwargs) + + +class AquisitionProxy: + """Basisobjekt für Aquisition.""" + _special_names=[ + '__abs__', '__add__', '__and__', '__call__', '__cmp__', '__coerce__', + '__contains__', '__delitem__', '__delslice__', '__div__', '__divmod__', + '__eq__', '__float__', '__floordiv__', '__ge__', '__getitem__', + '__getslice__', '__gt__', '__hash__', '__hex__', '__iadd__', '__iand__', + '__idiv__', '__idivmod__', '__ifloordiv__', '__ilshift__', '__imod__', + '__imul__', '__int__', '__invert__', '__ior__', '__ipow__', '__irshift__', + '__isub__', '__iter__', '__itruediv__', '__ixor__', '__le__', '__len__', + '__long__', '__lshift__', '__lt__', '__mod__', '__mul__', '__ne__', + '__neg__', '__oct__', '__or__', '__pos__', '__pow__', '__radd__', + '__rand__', '__rdiv__', '__rdivmod__', '__reduce__', '__reduce_ex__', + '__repr__', '__reversed__', '__rfloorfiv__', '__rlshift__', '__rmod__', + '__rmul__', '__ror__', '__rpow__', '__rrshift__', '__rshift__', '__rsub__', + '__rtruediv__', '__rxor__', '__setitem__', '__setslice__', '__sub__', + '__truediv__', '__xor__', 'next', + ] + + def _get_proxy_cls(cls,obj): + ocls = obj.__class__ + ns = {} + _methods = {} + + for n in AquisitionProxy._special_names: + if (hasattr(ocls, n)): + ns[n] = getter(n) + + return type( "AProxy(%s)" % (ocls.__name__,), (cls,), ns ) + + + def __new__(cls,obj,p = None,aq_name=None,aq_root=None): + pcls = AquisitionProxy._get_proxy_cls(cls,obj) + + inst = object.__new__( pcls ) + + _methods = {} + for n in dir(obj): +# print("AQ_ATTR: %s" % (n,)) + a = getattr(obj,n) + if isinstance( a, collections.Callable ) and hasattr( a, "__func__" ): # and not hasattr( AquisitionProxy, n ): +# print("AQ_METHOD: %s = %s" % (n,a)) + _methods[n] = caller(inst,a) + + object.__setattr__( inst, "_methods", _methods ) + object.__setattr__( inst, "_o", obj) + object.__setattr__( inst, "_p", p) + object.__setattr__( inst, "_aq_name", aq_name) + + if (aq_root is None): + aq_root = inst + + object.__setattr__( inst, "_aq_root", aq_root) + + return inst + + def __getattribute__(self,name): + v = None +# print("AQN: %s" % (name,)) + + if name.startswith("_aq_"): + if (name == "_aq_parent"): + return object.__getattribute__( self, "_p") +# return AquisitionProxy( p, self, aq_name=p._aq_name, aq_root=self._aq_root ) + if (name == "_aq_name"): + n = object.__getattribute__( self, "_aq_name") + if n is None: + return "/" + return n + if (name == "_aq_object"): + return object.__getattribute__( self, "_o") + + return object.__getattribute__( self, name ) + + _methods = object.__getattribute__(self,"_methods") + if name in _methods: + return _methods[name] + + try: + v = getattr( object.__getattribute__( self, "_o"), name ) + except AttributeError as ae: + try: + print("AQN_PARENT: %s %s" % (object.__getattribute__( self, "_p"), name)) + v = getattr( object.__getattribute__( self, "_p"), name) + if (isinstance(v, AquisitionProxy)): + v = object.__getattribute__( v, "_o" ) + except AttributeError as ae2: + raise ae2 + + if isinstance( v, Aquisition ): + v = AquisitionProxy( v, self, aq_name=name, aq_root=self._aq_root ) + + return v + + def __setattr__(self,name,value): + setattr( object.__getattribute__( self, "_o"), name, value ) + + def __delattr__(self,name): + delattr( object.__getattribute__( self, "_o"), name ) + + def __nonzero__(self): + return bool(object.__getattribute__(self, "_o")) + def __str__(self): + return str(object.__getattribute__(self, "_o")) + def __repr__(self): + return repr(object.__getattribute__(self, "_o")) + + def __dir__(self): + return dir( object.__getattribute__(self, "_o") ) + + def dir(self,cls): + realcls = object.__getattribute__( self, "__class__" ) + + ls = [] + + for an in dir(self): + if issubclass( getattr( self, an ).__class__, cls): + ls.append(an) + + if issubclass(realcls,AquisitionProxy): + p = object.__getattribute__( self, "_p") + if not p is None: + for an in AquisitionProxy.dir(p, cls): + if not an in ls: + ls.append( an ) + + return ls + + def _aq_path(self): + if (self._aq_name is None) or (self._aq_parent is None): + return "" + return "%s/%s" % (self._aq_parent._aq_path(),self._aq_name) + diff --git a/objectbroker/objectbroker.py b/objectbroker/objectbroker.py new file mode 100644 index 0000000..24062c3 --- /dev/null +++ b/objectbroker/objectbroker.py @@ -0,0 +1,115 @@ +import weakref +import collections +import pickle + +from simplelog import log,LLDEBUGCALL,LLDEBUG,LLINFO,LLERROR +from objectbroker import Persistence,NoPersistence + +def dump_state(state): + print("Persistent ID: %s" % (state["pid"],)) + print("Type: %s.%s" % (state["module"],state["class"])) + + print("Non-Persistent:") + for n in state["fields"].keys(): + print("K: %s = %s" % (n,state["fields"][n])) + + print("Persistent:") + for n in state["pfields"].keys(): + print("K: %s = %s" % (n,state["pfields"][n])) + print("") + +def obj_getstate(o): + if not isinstance(o, Persistence): + raise TypeError("obj_getstate() needs Persistence object") + + state = {} + state["pid"] = Persistence._persistence_id( o ) + state["module"] = o.__class__.__module__ + state["class"] = o.__class__.__name__ + state["fields"] = {} + state["pfields"] = {} + state["pvalues"] = {} + + + 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)): + state["fields"][n] = None + elif (isinstance( a, Persistence)): + state["pvalues"][n] = a + state["pfields"][n] = Persistence._persistence_id( a ) + else: + state["fields"][n] = a + + return state + + +class ObjectBroker: + """ObjectBroker """ + + def __init__(self, store={}): + self.__store = store + self.__cache = weakref.WeakValueDictionary() + self.__unsaved = {} + + log("ObjectBroker instantiated", LLDEBUGCALL) + + def save(self,o,recursed=False): + + if (o in self.__unsaved): + return + + state = obj_getstate(o) +# dump_state( state ) + + self.__cache[ state["pid"] ] = o + self.__unsaved[o] = state + + for pn in state["pvalues"]: + self.save( state["pvalues"][pn], recursed=True ) + +# dump_state(state) + + if not recursed: + + for n in list( self.__unsaved.keys() ): + s = self.__unsaved[n] + s["pvalues"] = None + self.__store[ s["pid"] ] = pickle.dumps(s) + del self.__unsaved[n] + + return state["pid"] + + def load(self,persistence_id): + + if (persistence_id in self.__cache): + o = self.__cache[persistence_id] + if not o is None: + return o + + state = pickle.loads(self.__store[ persistence_id ]) +# dump_state(state) + + m = __import__(state["module"],fromlist=[state["class"]]) + cls = getattr(m, state["class"] ) + + o = cls.__new__(cls) + + self.__cache[ persistence_id ] = o + + for n in state["fields"]: + setattr( o, n, state["fields"][n] ) + + for n in state["pfields"]: + print("SS: %s = %s" % (n,state["pfields"][n])) + setattr( o, n, self.load( state["pfields"][n] )) + + return o + + + + def remove(self,o=None,persistence_id=None): + pass + diff --git a/objectbroker/objectstore.py b/objectbroker/objectstore.py new file mode 100644 index 0000000..2cfab58 --- /dev/null +++ b/objectbroker/objectstore.py @@ -0,0 +1,25 @@ +import os + +class DiscObjectStore: + """DiscObjectStore ist ein Mapping Objekt, welches ein Verzeichnis im Dateisystem als Speicher nutzt und somit persistent zwischen Programmstarts arbeitet""" + def __init__(self,path = "./objects"): + self._path = path + self.check() + + def check(self): + if not os.path.exists( self._path ): + os.makedirs( self._path ) + + def __contains__(self, name): + return os.path.exists( "%s/%s" % (self._path, name)) + + def __getitem__(self, name): + f = open("%s/%s" % (self._path, name), "rb") + v = f.read() + f.close() + return v + + def __setitem__(self, name, value): + f = open("%s/%s" % (self._path, name), "wb") + v = f.write(value) + f.close() \ No newline at end of file diff --git a/objectbroker/persistence.py b/objectbroker/persistence.py new file mode 100644 index 0000000..788b4ef --- /dev/null +++ b/objectbroker/persistence.py @@ -0,0 +1,11 @@ +import uuid + +class Persistence: + + def _persistence_id(self): + if not hasattr(self, "__persistence_id__"): + setattr( self, "__persistence_id__", uuid.uuid4().hex ) + return getattr( self, "__persistence_id__" ) + +class NoPersistence: + pass