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:
parent
c978861b04
commit
171b3c76bc
2 changed files with 45 additions and 8 deletions
27
util/aop.py
27
util/aop.py
|
@ -34,13 +34,22 @@ class Notifier(object):
|
||||||
def __init__(self, method):
|
def __init__(self, method):
|
||||||
self.method = method
|
self.method = method
|
||||||
obj = method.im_self or method.im_class
|
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)
|
setattr(obj, name, self)
|
||||||
self.beforeSubs = []
|
self.beforeSubs = []
|
||||||
self.afterSubs = []
|
self.afterSubs = []
|
||||||
|
|
||||||
def __get__(self, instance, cls=None):
|
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):
|
def __call__(self, *args, **kw):
|
||||||
for sub, sargs, skw in self.beforeSubs:
|
for sub, sargs, skw in self.beforeSubs:
|
||||||
|
@ -62,15 +71,21 @@ class BoundNotifier(object):
|
||||||
def __init__(self, context, instance):
|
def __init__(self, context, instance):
|
||||||
self.context = context
|
self.context = context
|
||||||
self.instance = instance
|
self.instance = instance
|
||||||
|
self.beforeSubs = []
|
||||||
|
self.afterSubs = []
|
||||||
|
|
||||||
def __call__(self, *args, **kw):
|
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)
|
sub(None, *sargs, **skw)
|
||||||
result = self.context.method(self.instance, *args, **kw)
|
result = context.method(self.instance, *args, **kw)
|
||||||
for sub, aargs, akw in self.context.afterSubs:
|
for sub, aargs, akw in self.afterSubs + context.afterSubs:
|
||||||
sub(result, *aargs, **akw)
|
sub(result, *aargs, **akw)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def subscribe(self, before, after, *args, **kw):
|
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))
|
||||||
|
|
||||||
|
|
26
util/aop.txt
26
util/aop.txt
|
@ -28,8 +28,8 @@ Calling the wrapped method still works.
|
||||||
>>> demo.foo(8)
|
>>> demo.foo(8)
|
||||||
46
|
46
|
||||||
|
|
||||||
We can now get information about the returning of the method by
|
We can now get information about the start and end of the method call by
|
||||||
subscribing to it using some log function.
|
subscribing to it using a logging routine.
|
||||||
|
|
||||||
>>> def log(result, msg):
|
>>> def log(result, msg):
|
||||||
... print 'logging: %s, result=%s' % (msg, str(result))
|
... print 'logging: %s, result=%s' % (msg, str(result))
|
||||||
|
@ -51,14 +51,36 @@ subscribing to it using some log function.
|
||||||
Wrapping methods on class level
|
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 = Demo()
|
||||||
>>> demo.foo(4)
|
>>> demo.foo(4)
|
||||||
42
|
42
|
||||||
|
|
||||||
|
But we may also wrap the class itself and subscribe to the method on
|
||||||
|
the class level.
|
||||||
|
|
||||||
>>> getNotifier(Demo.foo)
|
>>> getNotifier(Demo.foo)
|
||||||
<...Notifier object ...>
|
<...Notifier object ...>
|
||||||
>>> Demo.foo.subscribe(None, log, 'after foo')
|
>>> Demo.foo.subscribe(None, log, 'after foo')
|
||||||
|
|
||||||
|
This now affects all instances of the class.
|
||||||
|
|
||||||
>>> demo.foo(4)
|
>>> demo.foo(4)
|
||||||
logging: after foo, result=42
|
logging: after foo, result=42
|
||||||
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
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue