[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