views:

125

answers:

7

I am trying to figure out how to get the names of all decorators on a method. I can already get the method name and docstring, but cannot figure out how to get a list of decorators.

+2  A: 

That's not possible in my opinion. A decorator is not some kind of attribute or meta data of a method. A decorator is a convenient syntax for replacing a function with the result of a function call. See http://docs.python.org/whatsnew/2.4.html?highlight=decorators#pep-318-decorators-for-functions-and-methods for more details.

Achim
+1  A: 

It is impossible to do in a general way, because

@foo
def bar ...

is exactly the same as

def bar ...
bar = foo (bar)

You may do it in certain special cases, like probably @staticmethod by analyzing function objects, but not better than that.

doublep
+2  A: 

That's because decorators are "syntactic sugar". Say you have the following decorator:

def MyDecorator(func):
    def transformed(*args):
        print "Calling func " + func.__name__
        func()
    return transformed

And you apply it to a function:

@MyDecorator
def thisFunction():
    print "Hello!"

This is equivalent to:

thisFunction = MyDecorator(thisFunction)

You could embed a "history" into the function object, perhaps, if you're in control of the decorators. I bet there's some other clever way to do this (perhaps by overriding assignment), but I'm not that well-versed in Python unfortunately. :(

Faisal
+1  A: 

As Faisal notes, you could have the decorators themselves attach metadata to the function, but to my knowledge it isn't automatically done.

Russell Borogove
+8  A: 

If you can change the way you call the decorators from

class Foo(object):
    @many
    @decorators
    @here
    def bar(self):
        pass

to

class Foo(object):
    @register(many,decos,here)
    def bar(self):
        pass

then you could register the decorators this way:

def register(*decorators):
    def register_wrapper(func):
        for deco in decorators[::-1]:
            func=deco(func)
        func._decorators=decorators        
        return func
    return register_wrapper

For example:

def many(f):
    def wrapper(*args,**kwds):
        return f(*args,**kwds)
    return wrapper

decos = here = many

class Foo(object):
    @register(many,decos,here)
    def bar(self):
        pass

foo=Foo()

Here we access the tuple of decorators:

print(foo.bar._decorators)
# (<function many at 0xb76d9d14>, <function decos at 0xb76d9d4c>, <function here at 0xb76d9d84>)

Here we print just the names of the decorators:

print([d.func_name for d in foo.bar._decorators])
# ['many', 'decos', 'here']
unutbu
This is a great solution. :D It does assume you have access to the code that's assigning the decorators, though...
Faisal
Ok this could work, but why can't I just add the code func._whatever='something' into my existing decorator, and test for the value of the _whatever attribute when performing introspection on the method?
Tony
You can, but then you'll have to dirty every decorator you write with the cross-cutting concern of leaving its tracks behind in the function it modifies.
Faisal
+1  A: 

You cannot, by definition. Decorator:

@dec
def foo():
    return 1

is just shortcut for:

def foo_internal()
    return 1
foo = dec(foo_internal)

Notice that, decorator dec is simply some callable returning something (function, maybe some other callable object). You don't even know if foo has anything to do with decorated definition, e.g:

def dec(f):
    def not_foo():
        return 0
    return not_foo

If you need to put some additional information on methods, classes etc.---like e.g. attributes in .NET---simply set some attributes on them.

def foo()
    return 1
foo.decorated = True

Or implement decorators that set those attributes, if it really helps readability.

def dec(f):
    f.decorated = True
    return f

Well. Python is open-source. You can always extend Python interpreter to track decorators applied to object. Since (as shown above) object returned by decorator doesn't have to be related in any way to decorated object, this implementation would have to store information from decorated object before applying decorator, apply decorator, replace decorator info on returned object and add info about last decorator.

Can be done. But I am not sure if it's really useful. And it adds some overhead to each decorator. So I wouldn't expect such mechanism in mainstream Python, unless it provides some other benefits, like simplifying implementation of decorators, etc.

Tomek Szpakowicz
+3  A: 

You can't but even worse is there exists libraries to help hide the fact that you have decorated a function to begin with. See Functools or the decorator library (@decorator if I could find it) for more information.

wheaties