[POS-commit] Kiwi/Kiwi/FrameWork Models.py,1.1,1.2 accessors.py,1.3,1.4

nobody at async.com.br nobody at async.com.br
Sun May 11 04:23:14 BRST 2003


Update of /cvs/Kiwi/Kiwi/FrameWork
In directory anthem:/tmp/cvs-serv31621/Kiwi/FrameWork

Modified Files:
	Models.py accessors.py 
Log Message:
Reorganisation kgetattr/ksetattr:
-) new cache structure, that contains always only valid entries.
-) Model.get_[gs]etter removed. If you need the basic functionality, use
   accessors.get_default_[gs]etter.
-) The functionality of get_default_[gs]etter has been inlined in kgetattr and
   ksetattr to save one function call.

Unittests:
-) updated to reflect that kgetattr doesn't provide a get_getter anymore.
-) added a simple benchmark to compare the performance of kgetattr with
   the old way.



Index: Models.py
===================================================================
RCS file: /cvs/Kiwi/Kiwi/FrameWork/Models.py,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -d -r1.1 -r1.2
--- Models.py	1 May 2003 00:13:47 -0000	1.1
+++ Models.py	11 May 2003 07:23:11 -0000	1.2
@@ -32,25 +32,6 @@
     def __init__(self):
         self.ensure_init()
 
-    def get_getter(self, attr_name, cachecontrol=None):
-        """Returns a method through which a model value identified
-        by attr_name can be retrieved"""
-        func = getattr(self, "get_%s" % attr_name, None)
-        if callable(func):
-            return func
-        else:
-            return (self, attr_name)
-
-    def get_setter(self, attr_name, cachecontrol=None):
-        """Returns a method through which a model value identified
-        by attr_name can be changed."""
-        func = getattr(self, "set_%s" % attr_name, None)
-        if callable(func):
-            return func
-        else:
-            return (self, attr_name)
-
-
     def ensure_init(self):
         """Sets up the variables so the Model's getattr hook and proxy
 notification work properly."""

Index: accessors.py
===================================================================
RCS file: /cvs/Kiwi/Kiwi/FrameWork/accessors.py,v
retrieving revision 1.3
retrieving revision 1.4
diff -u -d -r1.3 -r1.4
--- accessors.py	9 May 2003 04:49:40 -0000	1.3
+++ accessors.py	11 May 2003 07:23:11 -0000	1.4
@@ -93,7 +93,9 @@
 try:
     import weakref
     _kgetattr_cache = {}
+    _kgetattr_wref = {}
     _ksetattr_cache = {}
+    _ksetattr_wref = {}
 except ImportError:
 
     class WeakRef:
@@ -125,6 +127,10 @@
     weakref=WeakRef()
     _kgetattr_cache = weakref
     _ksetattr_cache = weakref
+    # should not matter, because our primitive weakref emulation
+    # raises always TypeError ;)
+    _kgetattr_wref = weakref
+    _ksetattr_wref = weakref
 
 class CacheControl(object):
     __slots__ = ['key', 'cacheable']
@@ -146,6 +152,24 @@
     # indicates an unset value since None needs to be used
     pass
 
+def kgetattr_guard(wref):
+    try:
+        key, data = _kgetattr_wref[id(wref)]
+        del _kgetattr_wref[id(wref)]
+        del _kgetattr_cache[key]
+    except KeyError:
+        # This path is used only when the program terminates.
+        pass
+    
+def ksetattr_guard(wref):
+    try:
+        key, data = _ksetattr_wref[id(wref)]
+        del _ksetattr_wref[id(wref)]
+        del _ksetattr_cache[key]
+    except:
+        # This path is used only when the program terminates.
+        pass
+
 # 1. Break up attr_name into parts
 # 2. Loop around main lookup code for each part:
 #     2.1. Try and get accessor tuple out of cache
@@ -163,12 +187,19 @@
              ref=weakref.ref,
              TupleType=types.TupleType,
              MethodType=types.MethodType,
-             # access codes:
+             split=string.split,
+             kgetattr_guard=kgetattr_guard,
+             # constants:
+             # access opcodes:
              LAMBDA_ACCESS = 0,
              METHOD_ACCESS = 1,
              TUPLE_ACCESS = 2,
              NWR_METHOD_ACCESS = 3,
              NWR_TUPLE_ACCESS = 4,
+             # FAST tuples do not store the object, as the input object
+             # is also the accesses object.
+             FAST_METHOD_ACCESS = 5,
+             FAST_TUPLE_ACCESS = 6,
              ):
     """Returns the value associated with the attribute in model
     named by attr_name. If default is provided and model does not
@@ -177,80 +208,105 @@
     be done."""
 
     # 1. Break up attr_name into parts
-    if flat:
-        names = [attrname, ]
+    if flat or "." not in attr_name:
+        names = [attr_name,]
     else:
-        names = string.split(attr_name, ".")
+        try:
+            names = attr_name.split(".")
+        except AttributeError:
+            names = split(attr_name, ".")
 
     # 2. Loop around main lookup code for each part:
     obj = model
     for name in names:
-
+        key = (id(obj), name)
         # First time round, obj is the model. Every subsequent loop, obj
         # is the subattribute value indicated by the current part in
         # [names]. The last loop grabs the target value and returns it.
 
         try:
-            key = (ref(obj), name)
-        except TypeError:
-            # oops, not weakrefable
-            key = (obj, name)
-
-        # 2.1. Try and get accessor tuple out of cache
-        try:
-            try:
-                icode, data1, data2 = _kgetattr_cache[key]
-                if (icode == TUPLE_ACCESS and data1() is None) or \
-                       (icode == METHOD_ACCESS and data2() is None):
-                    # invalid entry in the cache.
-                    del _kgetattr_cache[key]
-                    raise KeyError
-            except TypeError:
-                key = None
-                raise KeyError
-
-        # 2.2. If not there, generate tuple from callable and store it
+            # 2.1 Fetch the opcode tuple from the cache.
+            objref, icode, data1, data2 = _kgetattr_cache[key]
         except KeyError:
+            # 2.2. If not there, generate tuple from callable and store it
             cache = CacheControl(key)
             try:
                 get_getter = obj.__class__.get_getter
             except AttributeError:
-                get_getter = get_default_getter
-            try:
-                func = get_getter(obj, name, cache)
-            except AttributeError:
-                if default == _AttrUnset:
-                    raise
-                return default
+                get_getter = None
+                try:
+                    func = getattr(obj, "get_%s" % name)
+                except AttributeError:
+                    func = (obj, name)
+
+            if get_getter is not None:
+                try:
+                    func = get_getter(obj, name, cache)
+                except AttributeError:
+                    if default == _AttrUnset:
+                        raise
+                    return default
                 
             if isinstance(func, TupleType):
                 data1, data2 = func
-                try:
-                    data1 = ref(data1)
-                    icode = TUPLE_ACCESS
-                except TypeError:
-                    icode = NWR_TUPLE_ACCESS
+                if data1 == obj:
+                    data1 = None
+                    icode = FAST_TUPLE_ACCESS
+                else:
+                    try:
+                        data1 = ref(data1, kgetattr_guard)
+                        _kgetattr_wref[id(data1)] = (key, data1)
+                        icode = TUPLE_ACCESS
+                    except TypeError:
+                        icode = NWR_TUPLE_ACCESS
                 
             elif isinstance(func, MethodType):
                 data1 = func.im_func
-                try:
-                    data2 = ref(func.im_self)
-                    icode = METHOD_ACCESS
-                except TypeError:
-                    data2 = func.im_self
-                    icode = NWR_METHOD_ACCESS
+                data2 = func.im_self
+                if data2 == obj:
+                    data2 = None
+                    icode = FAST_METHOD_ACCESS
+                else:
+                    try:
+                        data2 = ref(func.im_self, kgetattr_guard)
+                        _kgetattr_wref[id(data2)] = (key, data2)
+                        icode = METHOD_ACCESS
+                    except TypeError:
+                        data2 = func.im_self
+                        icode = NWR_METHOD_ACCESS
             else:
                 icode = LAMBDA_ACCESS
                 data1 = func
                 data2 = None
+            if cache.cacheable:
+                # Store access opcode:
+                # objref or obj are used as a protection against id-aliasing
+                # as we use just a plain id(obj) in the cache entry key.
+                #
+                # We either have to use a weakref, so we get to know when the
+                # object dies. We just remove the cache entry containing the
+                # weakref, _kgetattr_wref is used to associate which key has
+                # to be killed for a given weakref.
+                
+                try:
+                    objref = ref(obj, kgetattr_guard)
+                    _kgetattr_wref[id(objref)] = (key, objref)
+                    _kgetattr_cache[key] = (objref, icode, data1, data2)
+                except TypeError:
+                    # it's not weakrefable (probably ZODB!)
+                    # store a hard reference.
+                    _kgetattr_cache[key] = (obj, icode, data1, data2)
+            else:
+                if _kgetattr_cache.has_key(key):
+                    del _kgetattr_cache[key]
 
-            # Store accessor tuple
-            if cache.cacheable and key is not None:
-                _kgetattr_cache[key] = (icode, data1, data2)
- 
         # 2.3. Use accessor tuple to grab value
         try:
-            if icode == TUPLE_ACCESS:
+            if icode == FAST_METHOD_ACCESS:
+                obj = data1(obj)
+            elif icode == FAST_TUPLE_ACCESS:
+                obj = getattr(obj, data2)
+            elif icode == TUPLE_ACCESS:
                 obj = getattr(data1(), data2)
             elif icode == NWR_TUPLE_ACCESS:
                 obj = getattr(data1, data2)
@@ -285,17 +341,22 @@
              attr_name,
              value,
              flat=0,
+
              # bind to local variables for speed:
              ref=weakref.ref,
              TupleType=types.TupleType,
              MethodType=types.MethodType,
+             ksetattr_guard=ksetattr_guard,
+             
              # constants:
              LAMBDA_ACCESS = 0,
              METHOD_ACCESS = 1,
              TUPLE_ACCESS = 2,
              NWR_METHOD_ACCESS = 3,
              NWR_TUPLE_ACCESS = 4,
-             ):
+             FAST_METHOD_ACCESS = 5,
+             FAST_TUPLE_ACCESS = 6,
+                 ):
     """Set the value associated with the attribute in model
     named by attr_name. If flat=1 is specified, no dot path parsing will
     be done."""
@@ -313,64 +374,77 @@
             attr_name = attr_name[lastdot+1:]
 
     # At this point we only have a flat attribute and the right model.
+    key = (id(model), attr_name)
 
     try:
-        key = (ref(model), attr_name)
-    except TypeError:
-        # oops, not weakrefable
-        key = (model, attr_name)
-
-    # 2. Try and get accessor tuple from cache
-    try:
-        try:
-            icode, data1, data2 = _ksetattr_cache[key]
-
-            if (icode == TUPLE_ACCESS and data1() is None) or \
-                   (icode == METHOD_ACCESS and data2() is None):
-                # invalid weakref in the cache.
-                del _ksetattr_cache[key]
-                raise KeyError
-        except TypeError:
-            # do not cache the results for nonhashable objects:
-            key = None
-            # force cache miss
-            raise KeyError              
-    
-    # 3. If not there, generate accessor tuple and store it
+        # 2. Try and get accessor tuple from cache
+        objref, icode, data1, data2 = _ksetattr_cache[key]
     except KeyError:
+        # 3. If not there, generate accessor tuple and store it        cache = CacheControl(key)
         cache = CacheControl(key)
         try:
             get_setter = model.__class__.get_setter
         except AttributeError:
-            get_setter = get_default_setter
-        func = get_setter(model, attr_name, cache)
+            get_setter = None
+            try:
+                func = getattr(model, "set_%s" % attr_name)
+            except AttributeError:
+                func = (model, attr_name)
+            
+        if get_setter is not None:
+            func = get_setter(model, attr_name, cache)
 
         if isinstance(func, TupleType):
             data1, data2 = func
-            try:
-                data1 = ref(data1)
-                icode = TUPLE_ACCESS
-            except TypeError:
-                icode = NWR_TUPLE_ACCESS
+            if data1 == model:
+                data1 = None
+                icode = FAST_TUPLE_ACCESS
+            else:
+                try:
+                    data1 = ref(data1, ksetattr_guard)
+                    _ksetattr_wref[id(data1)] = (key, data1)
+                    icode = TUPLE_ACCESS
+                except TypeError:
+                    icode = NWR_TUPLE_ACCESS
         elif isinstance(func, MethodType):
             data1 = func.im_func
-            try:
-                data2 = ref(func.im_self)
-                icode = METHOD_ACCESS
-            except TypeError:
-                data2 = func.im_self
-                icode = NWR_METHOD_ACCESS
+            data2 = func.im_self
+            if data2 == model:
+                data2 = None
+                icode = FAST_METHOD_ACCESS
+            else:
+                try:
+                    data2 = ref(data2, ksetattr_guard)
+                    _ksetattr_wref[id(data2)] = (key, data2)
+                    icode = METHOD_ACCESS
+                except TypeError:
+                    data2 = func.im_self
+                    icode = NWR_METHOD_ACCESS
         else:
             icode = LAMBDA_ACCESS
             data1 = func
             data2 = None
-        
-        # Store accessor tuple
-        if cache.cacheable and key is not None:
-            _ksetattr_cache[key] = (icode, data1, data2)
 
-    # 4. Set value to target object's attribute
-    if icode == TUPLE_ACCESS:
+        if cache.cacheable:
+            # store the access opcode.
+            # for the use of model/objref as first value in the opcode tuple
+            # see the kgetattr comments.
+            try:
+                objref = ref(model, ksetattr_guard)
+                _ksetattr_wref[id(objref)] = (key, objref)
+                _ksetattr_cache[key] = (objref, icode, data1, data2)
+            except TypeError:
+                # it's not weakref-able, store a hard reference.
+                _ksetattr_cache[key] = (model, icode, data1, data2)
+        else:
+            if _ksetattr_cache.has_key(key):
+                del _ksetattr_cache.has_key[key]
+
+    if icode == FAST_TUPLE_ACCESS:
+        setattr(model, data2, value)
+    elif icode == FAST_METHOD_ACCESS:
+        data1(model, value)
+    elif icode == TUPLE_ACCESS:
         setattr(data1(), data2, value)
     elif icode == NWR_TUPLE_ACCESS:
         setattr(data1, data2, value)
@@ -388,15 +462,17 @@
     versions that do not support weakrefs (1.5.x and earlier). Be
     warned, using the cache in these versions causes leaked
     references to accessor methods and models!"""
-    global _kgetattr_cache, _ksetattr_cache
+    global _kgetattr_cache, _ksetattr_cache, _kgetattr_wref, _ksetattr_wref
     _kgetattr_cache = {}
     _ksetattr_cache = {}
+    _kgetattr_wref = {}
+    _ksetattr_wref = {}
 
 def clear_attr_cache():
     """Clears the kgetattr cache. It must be called repeatedly to
     avoid memory leaks in Python 2.0 and earlier."""
-    global _kgetattr_cache, _ksetattr_cache
+    global _kgetattr_cache, _ksetattr_cache, _kgetattr_wref, _ksetattr_wref
     _kgetattr_cache = {}
     _ksetattr_cache = {}
-
-
+    _kgetattr_wref = {}
+    _ksetattr_wref = {}



More information about the POS-commit mailing list