From 'wrapping unbound' methods to 'aspect oriented' design (Aspect implementation)
Pedro Rodriguez
pedro_rodriguez at club-internet.fr
Sun Jan 13 05:22:54 EST 2002
Here is an implementation of a (primitive-) Aspect.
It is not a full featured thing like AspectJ, and looks quite
dumb in regard with aspectr (from Ruby), but I found its simplicity
quite "pythonic".
There may be gotchas regarding how to nest advices, and a possible
bug with Python 2.2 (new.instancemethod refuses a new class object
as the third argument - will submit a bug just in case...).
So the idea is :
- you create an aspect class by subclassing Aspect
- you write your advices with the following signature
def an_advice(self, method, methodName, inst, *args, **kwargs):
where :
- self is the aspect
- method is the next advice to be called or the original method
- methodName is the name of the aspect-ified method
- inst is the object on which the method will be called
- args, kwargs are the arguments to the real method
- "method" should only be used for "around" advices, since they MUST
call your real method (or the next advice) :
apply(method, args, kwargs)
- "method" is useless for "before" and "after" advices
- you can then create an aspect to wrap any other class method with
anAspect.wrap_around
# Aspect implementation -----------------------------------------------
import new
class Aspect:
def wrap_around(self, adviceName, cls, methodName):
adviceMethod = getattr(self, adviceName)
AdviceAround(adviceMethod, cls, methodName)
def wrap_before(self, adviceName, cls, methodName):
adviceMethod = getattr(self, adviceName)
AdviceBefore(adviceMethod, cls, methodName)
def wrap_after(self, adviceName, cls, methodName):
adviceMethod = getattr(self, adviceName)
AdviceAfter(adviceMethod, cls, methodName)
class Advice:
def __init__(self, adviceMethod, cls, methodName):
self.methodName = methodName
self.adviceMethod = adviceMethod
self.origMethod = getattr(cls, methodName)
newMethod = new.instancemethod(self.do_aspect, None, cls)
setattr(cls, methodName, newMethod)
def do_advice(self, *args, **kwargs):
inst = args[0]
apply(self.adviceMethod \
, (self.origMethod, self.methodName, inst) + args
, kwargs
)
def do_method(self, *args, **kwargs):
apply(self.origMethod, args, kwargs)
def do_aspect(self, *args, **kwargs):
raise NotImplementedError
class AdviceAround(Advice):
def do_aspect(self, *args, **kwargs):
apply(self.do_advice, args, kwargs)
class AdviceBefore(Advice):
def do_aspect(self, *args, **kwargs):
apply(self.do_advice, args, kwargs)
apply(self.do_method, args, kwargs)
class AdviceAfter(Advice):
def do_aspect(self, *args, **kwargs):
apply(self.do_method, args, kwargs)
apply(self.do_advice, args, kwargs)
# Sample code ---------------------------------------------------------
class A:
def f(self, x, y):
print "real f %s %s" % (x, y)
def g(self, x):
print "real g %s" % x
class B:
def z(self, *args, **kwargs):
print "real z %s %s" % (str(args), str(kwargs))
class MyAspect(Aspect):
def around_advice(self, method, methodName, inst, *args, **kwargs):
print "start"
apply(method, args, kwargs)
print "stop"
def before_advice(self, method, methodName, inst, *args, **kwargs):
print ">>>>>>>>>"
print "before", methodName, inst, args, kwargs
def after_advice(self, method, methodName, inst, *args, **kwargs):
print "after", methodName, inst, args, kwargs
print "<<<<<<<<<"
aspect = MyAspect()
aspect.wrap_around("around_advice", A, "f")
aspect.wrap_before("before_advice", A, "g")
aspect.wrap_before("before_advice", B, "z")
aspect.wrap_after("after_advice", B, "z")
a = A()
a.f(1,2)
a.g(3)
print
b = B()
b.z(1,2,3, x=42)
b.z(4)
-----------------------------------------------------------------------
This fails with Python 2.2 :
...
newMethod = new.instancemethod(self.do_aspect, None, cls)
TypeError: instancemethod() argument 3 must be class, not type
-----------------------------------------------------------------------
class A(object):
def f(self, x):
print "real f", x
aspect.wrap_around("around_advice", A, "f")
a = A()
a.f(1)
--
Pedro
More information about the Python-list
mailing list