"""BeforeTraverse interface and helper classes"""
# Interface
def registerBeforeTraverse(container, object, app_handle, priority=99):
"""Register an object to be called before a container is traversed.
'app_handle' should be a string or other hashable value that
distinguishes the application of this object, and which must
be used in order to unregister the object.
If the container will be pickled, the object must be a callable class
instance, not a function or method.
'priority' is optional, and determines the relative order in which
registered objects will be called.
"""
btr = getattr(container, '__before_traverse__', {})
btr[(priority, app_handle)] = object
rewriteBeforeTraverse(container, btr)
def unregisterBeforeTraverse(container, app_handle):
"""Unregister a __before_traverse__ hook object, given its 'app_handle'.
Returns a list of unregistered objects."""
btr = getattr(container, '__before_traverse__', {})
objects = []
for k in btr.keys():
if k[1] == app_handle:
objects.append(btr[k])
del btr[k]
if objects:
rewriteBeforeTraverse(container, btr)
return objects
def queryBeforeTraverse(container, app_handle):
"""Find __before_traverse__ hook objects, given an 'app_handle'.
Returns a list of (priority, object) pairs."""
btr = getattr(container, '__before_traverse__', {})
objects = []
for k in btr.keys():
if k[1] == app_handle:
objects.append((k[0], btr[k]))
return objects
# Implementation tools
def rewriteBeforeTraverse(container, btr):
"""Rewrite the list of __before_traverse__ hook objects"""
container.__before_traverse__ = btr
hookname = '__before_publishing_traverse__'
dic = hasattr(container.__class__, hookname)
bpth = container.__dict__.get(hookname, None)
if isinstance(bpth, MultiHook):
bpth = bpth._prior
bpth = MultiHook(hookname, bpth, dic)
setattr(container, hookname, bpth)
keys = btr.keys()
keys.sort()
for key in keys:
bpth.add(btr[key])
class MultiHook:
"""Class used to multiplex hook.
MultiHook calls the named hook from the class of the container, then
the prior hook, then all the hooks in its list.
"""
def __init__(self, hookname, prior, defined_in_class):
self._hookname = hookname
self._prior = prior
self._defined_in_class = defined_in_class
self._list = []
def __call__(self, container, request):
if self._defined_in_class:
# Assume it's an unbound method
getattr(container.__class__, self._hookname)(container, request)
prior = self._prior
if prior is not None:
prior(container, request)
for cob in self._list:
cob(container, request)
def add(self, cob):
self._list.append(cob)
# Helper class
class NameCaller:
"""Class used to proxy sibling objects by name.
When called with a container and request object, it gets the named
attribute from the container and calls it. If the name is not
found, it fails silently.
>>> registerBeforeTraverse(folder, NameCaller('preop'), 'XApp')
"""
def __init__(self, name):
self.name = name
def __call__(self, container, request):
try:
meth = getattr(container, self.name)
except AttributeError:
return
args = getattr(getattr(meth, 'func_code', None), 'co_argcount', 2)
try:
apply(meth, (container, request, None)[:args])
except:
from zLOG import LOG,ERROR
import sys
LOG('BeforeTraverse', ERROR,
'Error while invoking hook: "%s"' % self.name, error=
sys.exc_info())
|