possible use of __decorates__ in functools.decorator
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 this: 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[0] 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__)) bases.reverse() 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__ break del c, meth del self This tracing code will correctly show calls to foomethod with the appropriate class name. def testMethodInBothClasses(self): class Base: @tracelevel(1) def foomethod(self, x, y=5, **kwds): return x+y class TClass(Base): @tracelevel(1) 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) return Is there a way to do this without the __decorates__ attribute? -- Luis P Caamano Atlanta, GA USA
Turns out I didn't need to use the __decorates__ attribute to get the name of the class. I posted on clp (what I should've done in the first place) and got an answer back pretty quickly. After a duh!, hitting my forehead and a couple "of couse" comments, it was as simple as saving the original caller via a sys._getframe(2) call in the decorator factory. Sorry for the noise. -- Luis P Caamano Atlanta, GA USA
participants (1)
-
Luis P Caamano