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

kiko at async.com.br kiko at async.com.br
Fri May 9 01:49:42 BRST 2003


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

Modified Files:
	accessors.py 
Log Message:
Add comments, docstrings. I finally understand this code quite well.


Index: accessors.py
===================================================================
RCS file: /cvs/Kiwi/Kiwi/FrameWork/accessors.py,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -d -r1.2 -r1.3
--- accessors.py	2 May 2003 06:55:29 -0000	1.2
+++ accessors.py	9 May 2003 04:49:40 -0000	1.3
@@ -22,6 +22,16 @@
 # Author(s): Andreas Kostyrka <andreas at mtg.co.at>
 #            Christian Reis <kiko at async.com.br>
 
+"""The accessors module offers two important front-end functions:
+kgetattr and ksetattr. These functions allow retrieving attribute values
+from objects much in the same way as getattr/setattr allow, but with two
+important differences:
+    - They follow a dot hierarchy to retrieve or modify any value
+      reachable from the object.
+    - They cache the method used to access a certain attribute and reuse
+      it the next time the value is retrieved.
+"""
+
 import string
 import types
 
@@ -57,35 +67,28 @@
     class object:
         pass
 
-# This dictionaries caches getter/setter objects.
-#
-# This describes _kgetattr_cache, they apply equivalently to
-# _ksetattr_cache, just that all the calls have one attribute, the value
-# mode.
-#
-# key structure:
-# (objref, attrname)
-# objref   ... objref() == object or objref==object for types not supported by
-#              weakref.
-# attrname ... attribute name.
+# The _*_cache dictionaries cache the objects, attributes and callables
+# (called `accessor tuples' here) we retrieve values from. If possible,
+# we use weakrefs to avoid holding hard references to objects, allowing
+# them to be garbage collected.  Certain objects (ZODB.Persistent for
+# one) cannot be weakref()ed and *will* leak - be sure to call
+# clear_attr_cache() if you need them released.
 #
-# value structure:
+# Key structure:
+#   (objref_or_weakref, attrname)
 #
-# tuples:
-# (intcode, data1, data2)
+# Value structure (accessor tuples):
 #
-# intcode == 0
-#   call data1(data2())
-#   used for bound methods
+#   kgetattr: (access_code, data1, data2)
+#   ksetattr: (access_code, data1, data2, value_mode)
 #
-# intcode == 1
-#   call data1()
-#   used for not bound methods
+# Access codes:
 #
-# intcode == 2
-#   call getattr(data1(), data2)
-#   used for (obj, attrname) pseudo getter values.
-# 
+# 0: data1()                 unbound methods and functions (data2 is None)
+# 1: data2(data1())          bound methods and weakref
+# 2: getattr(data1(), data2) using straight getattr and weakref
+# 3: data2(data1)            bound methods (no weakref)
+# 4: getattr(data1, data2)   using straight getattr (no weakref)
 
 try:
     import weakref
@@ -111,22 +114,20 @@
             pass
 
         def __getitem__(self, k):
-            raise KeyError,"DummyObject"
+            raise KeyError, "DummyObject"
 
         def __delitem__(self, k):
-            raise KeyError,"DummyObject"
+            raise KeyError, "DummyObject"
 
         def get(self, *args):
-            raise KeyError,"DummyObject"
+            raise KeyError, "DummyObject"
     
     weakref=WeakRef()
     _kgetattr_cache = weakref
     _ksetattr_cache = weakref
 
 class CacheControl(object):
-
     __slots__ = ['key', 'cacheable']
-
     def __init__(self, key):
         self.key = key
         self.cacheable = 1
@@ -141,41 +142,61 @@
         if _ksetattr_cache.has_key(key):
             del _ksetattr_cache[key]
 
-
 class _AttrUnset:
+    # indicates an unset value since None needs to be used
     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
+#     2.2. If not there, generate tuple from callable and store it
+#     2.3. Use accessor tuple to grab value
+#     2.4. Value wasn't found, return default or raise ValueError
+#   Use value as obj in next iteration
+# 3. Return value
+
 def kgetattr(model,
              attr_name,
              default=_AttrUnset,
              flat=0,
-             # speedup cache:
+             # bind to local variables for speed:
              ref=weakref.ref,
              TupleType=types.TupleType,
              MethodType=types.MethodType,
-             # constants:
+             # access codes:
+             LAMBDA_ACCESS = 0,
+             METHOD_ACCESS = 1,
              TUPLE_ACCESS = 2,
-             NWR_TUPLE_ACCESS = 3,
-             NWR_METHOD_ACCESS = 4,
-             METHOD_ACCESS = 0,
-             LAMBDA_ACCESS = 1,
+             NWR_METHOD_ACCESS = 3,
+             NWR_TUPLE_ACCESS = 4,
              ):
     """Returns the value associated with the attribute in model
     named by attr_name. If default is provided and model does not
     have an attribute called attr_name, the default value is
     returned. If flat=1 is specified, no dot path parsing will
     be done."""
+
+    # 1. Break up attr_name into parts
     if flat:
-        names = [attrname,]
+        names = [attrname, ]
     else:
         names = string.split(attr_name, ".")
 
+    # 2. Loop around main lookup code for each part:
     obj = model
     for name in names:
+
+        # 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]
@@ -187,6 +208,8 @@
             except TypeError:
                 key = None
                 raise KeyError
+
+        # 2.2. If not there, generate tuple from callable and store it
         except KeyError:
             cache = CacheControl(key)
             try:
@@ -220,8 +243,12 @@
                 icode = LAMBDA_ACCESS
                 data1 = func
                 data2 = None
+
+            # 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:
                 obj = getattr(data1(), data2)
@@ -234,31 +261,51 @@
             elif icode == LAMBDA_ACCESS:
                 obj = data1()
             else:
-                raise AssertionError,"Unknown tuple in _kgetattr_cache!!!!"
+                raise AssertionError, "Unknown tuple type in _kgetattr_cache"
+
+        # 2.4. Value wasn't found, return default or raise ValueError
         except AttributeError:
             if default == _AttrUnset:
                 raise
             return default
+
+        # At the end of the iteration, the value retrieved becomes the new obj
+
+    # 3. Return value
     return obj
 
+# A general algo for ksetattr:
+#
+# 1. Use attr_name to kgetattr the target object, and get the real attribute
+# 2. Try and get accessor tuple from cache
+# 3. If not there, generate accessor tuple and store it
+# 4. Set value to target object's attribute
+
 def ksetattr(model,
              attr_name,
              value,
              flat=0,
-             # speedup cache:
+             # bind to local variables for speed:
              ref=weakref.ref,
              TupleType=types.TupleType,
              MethodType=types.MethodType,
              # constants:
+             LAMBDA_ACCESS = 0,
+             METHOD_ACCESS = 1,
              TUPLE_ACCESS = 2,
-             NWR_TUPLE_ACCESS = 3,
-             NWR_METHOD_ACCESS = 4,
-             METHOD_ACCESS = 0,
-             LAMBDA_ACCESS = 1,
+             NWR_METHOD_ACCESS = 3,
+             NWR_TUPLE_ACCESS = 4,
              ):
     """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."""
+
+    # 1. kgetattr the target object, and get the real attribute
+    # This is the only section which is special about ksetattr. When you
+    # set foo.bar.baz to "x", what you really want to do is get hold of
+    # foo.bar and use an accessor (set_baz/setattr) on it. This bit gets
+    # the attribute name and the model we want.
+
     if not flat:
         lastdot = string.rfind(attr_name, ".")
         if lastdot != -1:
@@ -266,18 +313,21 @@
             attr_name = attr_name[lastdot+1:]
 
     # At this point we only have a flat attribute and the right model.
+
     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 entry in the cache.
+                # invalid weakref in the cache.
                 del _ksetattr_cache[key]
                 raise KeyError
         except TypeError:
@@ -285,7 +335,8 @@
             key = None
             # force cache miss
             raise KeyError              
-        
+    
+    # 3. If not there, generate accessor tuple and store it
     except KeyError:
         cache = CacheControl(key)
         try:
@@ -313,8 +364,12 @@
             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:
         setattr(data1(), data2, value)
     elif icode == NWR_TUPLE_ACCESS:
@@ -326,7 +381,7 @@
     elif icode == LAMBDA_ACCESS:
         data1(value)
     else:
-        raise AssertionError,"Unknown tuple in _ksetattr_cache!!!!"
+        raise AssertionError, "Unknown tuple type in _ksetattr_cache"
     
 def enable_attr_cache():
     """Enables the use of the kgetattr cache when using Python



More information about the POS-commit mailing list