proof-of-concept for a simple AOP implementation

git-svn-id: svn://svn.cy55.de/Zope3/src/cybertools/trunk@1966 fd906abe-77d9-0310-91a1-e0d9ade77398
This commit is contained in:
helmutm 2007-08-27 18:02:56 +00:00
parent c978861b04
commit 171b3c76bc
2 changed files with 45 additions and 8 deletions

View file

@ -34,13 +34,22 @@ class Notifier(object):
def __init__(self, method):
self.method = method
obj = method.im_self or method.im_class
name = method.im_func.func_name
name = self.name = method.im_func.func_name
setattr(obj, name, self)
self.beforeSubs = []
self.afterSubs = []
def __get__(self, instance, cls=None):
return BoundNotifier(self, instance)
if instance is None:
# class-level access
return self
existing = instance.__dict__.get(self.name)
if isinstance(existing, BoundNotifier):
# the instance's method is already wrapped
return existing
notifier = BoundNotifier(self, instance)
setattr(instance, self.name, notifier)
return notifier
def __call__(self, *args, **kw):
for sub, sargs, skw in self.beforeSubs:
@ -62,15 +71,21 @@ class BoundNotifier(object):
def __init__(self, context, instance):
self.context = context
self.instance = instance
self.beforeSubs = []
self.afterSubs = []
def __call__(self, *args, **kw):
for sub, sargs, skw in self.context.beforeSubs:
context = self.context
for sub, sargs, skw in self.beforeSubs + context.beforeSubs:
sub(None, *sargs, **skw)
result = self.context.method(self.instance, *args, **kw)
for sub, aargs, akw in self.context.afterSubs:
result = context.method(self.instance, *args, **kw)
for sub, aargs, akw in self.afterSubs + context.afterSubs:
sub(result, *aargs, **akw)
return result
def subscribe(self, before, after, *args, **kw):
self.context.subscribe(before, after, *args, **kw)
if before is not None:
self.beforeSubs.append((before, args, kw))
if after is not None:
self.afterSubs.append((after, args, kw))

View file

@ -28,8 +28,8 @@ Calling the wrapped method still works.
>>> demo.foo(8)
46
We can now get information about the returning of the method by
subscribing to it using some log function.
We can now get information about the start and end of the method call by
subscribing to it using a logging routine.
>>> def log(result, msg):
... print 'logging: %s, result=%s' % (msg, str(result))
@ -51,14 +51,36 @@ subscribing to it using some log function.
Wrapping methods on class level
===============================
The above code did only wrap the bound method ``foo()`` of a certain
instance of the ``Demo`` class. Other instances of this class are not
affected.
>>> demo = Demo()
>>> demo.foo(4)
42
But we may also wrap the class itself and subscribe to the method on
the class level.
>>> getNotifier(Demo.foo)
<...Notifier object ...>
>>> Demo.foo.subscribe(None, log, 'after foo')
This now affects all instances of the class.
>>> demo.foo(4)
logging: after foo, result=42
42
>>> demo.foo(8)
logging: after foo, result=46
46
Combining class and instance level wrapping
-------------------------------------------
>>> demo.foo.subscribe(None, log, 'after demo.foo')
>>> demo.foo(10)
logging: after demo.foo, result=48
logging: after foo, result=48
48