[Python-Dev] possible use of __decorates__ in functools.decorator
Luis P Caamano
lcaamano at gmail.com
Sun May 7 17:35:26 CEST 2006
In a previous post related to the functools.decorator function, I
think Nick was wondering if the __decorator__ and __decorates__
attributes were useful and Guido was tempted to call YAGNI on them.
Coincidentally, I've run into a situation where I had to use the
__decorates__ attribute, which I'd like to show to you because it
might be a good example of why __decorates__ is useful or perhaps
there's another way to do what I'm doing without it that some of you
could hint at.
We have a tracing decorator that automatically logs enter/exits
to/from functions and methods but it also figures out by itself the
arguments values of the function call and the class or module the
function/method is defined on. I thought this was trivial until I ran
into methods extended in subclasses, at which point we had to deal
with bases and the mro.
The part that figures out the name of the class where the wrapped
method was defined has to start with the assumption that the first
argument is self and then find the defining class from it. This code
fragment is in the wrapper function of the decorator and it looks like
if numargs > 0:
# at definition time, class methods are not methods
# yet because the class doesn't exist when the
# decorators get called and thus, we have to figure
# out classname at runtime via self, which is then
# an instance of a class.
# assume first arg is self, see if f.__name__ is there
# as a method and if so, then grab it's class name
self = args
if type(self) == types.InstanceType:
# getattr will find the method anywhere in the
# class tree so start from the top
bases = list(inspect.getmro(self.__class__))
for c in bases:
# f was given to us in the deco_func
meth = getattr(c, f.__name__, None)
# we found a method with that name, which
# it's probably this same wrapper function
# we wrapped the original method with.
ofunc = getattr(meth, '__decorates__', False)
if ofunc and ofunc.func_code == f.func_code:
# got it
clsname = meth.im_class.__name__
del c, meth
This tracing code will correctly show calls to foomethod with the
appropriate class name.
def foomethod(self, x, y=5, **kwds):
def foomethod(self, x, y=1, **kwds):
return Base.foomethod(self, x, **kwds) + x + y
t = TClass()
rc = t.foomethod(4, d=1)
self.failUnless(rc == 14)
Is there a way to do this without the __decorates__ attribute?
Luis P Caamano
Atlanta, GA USA
More information about the Python-Dev