4

Is there any way I can obtain a list of the decorators applied to a function without resorting to hackish things like decorating the decorators?

alexgolec
  • 26,898
  • 33
  • 107
  • 159
  • I've already mostly-answered this question here, in a slightly different (but easily adaptable) form: http://stackoverflow.com/questions/5910703/howto-get-all-methods-of-a-python-class-with-given-decorator Decorating the decorators is less hackish than sourcecode hacks, C-python hacks, etc. In my humble opinion, "decorating decorators" is not hackish at all and is a common technique. (Also one might consider this question to be a subset of the linked question, since methods are mostly functions, but allow a greater variety of tricks to be applied.) – ninjagecko Feb 29 '12 at 04:33

4 Answers4

3

I'm uneasy posting an answer like this, because something always seems to bubble up out of the depths of the python standard libraries to prove me wrong. But I'm pretty sure the answer is no.

The problem is that decorators are just functions themselves -- the whole @foo thing is just syntactic sugar for func = foo(func) -- so they don't give you anything extra that functions don't already give you. Which isn't very much.

It's possible you might be able to poke around in func_dict, func_globals, func_defaults, func_closure and so on, and make some guesses, but it's unlikely that there's a good general solution that doesn't involve hand-coding the behavior into the decorator. Or alternatively, as you say, by decorating the decorator.

senderle
  • 145,869
  • 36
  • 209
  • 233
3

Not really. First, because not all decorators return a wrapper function; it is possible for a decorator to simply modify the existing function (setting an attribute on it, perhaps) -- Python doesn't record what function touched every attribute, obviously. Second, while you can probably ask the garbage collector for closures that hold a reference to the decorated function, and to functions that have that closure, not every function that holds a reference to another function in a closure is a wrapper applied by a decorator. Then there's the issue of decorators that take arguments; there is an extra layer of inner functions before you get to the actual decorating function. Finally, not all decorators are functions, and not all decorated objects are functions.

In short, you might be able to approximate it some of the time, but even when it works, it's going to be an enormous hack that only works on CPython. There just isn't any simple, documented way to do it.

kindall
  • 178,883
  • 35
  • 278
  • 309
  • +1 for coming up with more imaginative hackery than I could. The upshot being that even a half-functional solution will be _way_ more hackish than "decorating the decorator." Which, by the bye, really isn't all that hackish. – senderle Feb 29 '12 at 04:33
1

I believe that this is not possible because the decorators have the option to replace the function, wrap the function, return anything they want. It's not so much that they are "applied" to the function.

gahooa
  • 131,293
  • 12
  • 98
  • 101
0

As the other answers say, the decorators could not wrap the function, but just modify it - in that case, ytou would not be able to find them.

For wrapper decorators, though, you could resort to using Python's debugging hooks - it won't be easy, and won't be "clean" - but it could be done: you seet a debugging hook to be called each time you enter a function,, and call your decorated function - each time your hook is called you have entered into a new function - use introspection to see whether that is your "final" function (easy if your decorators don't use functools.wrap themselves, and your desired function is the only one to keep the original __name__ attribute, trickier otherwise) - see the documentation on sys.settrace and on the bdb module and check if you come up with something.

But when you think of this, decorating the decorators doe not look that hackish anymore, does it?

jsbueno
  • 99,910
  • 10
  • 151
  • 209