[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