views:

399

answers:

3

I have a problem using docstrings with decorators. Given the following example:

def decorator(f):
    def _decorator():
        print 'decorator active'
        f()
    return _decorator

@decorator
def foo():
    '''the magic foo function'''
    print 'this is function foo'

help(foo)

Now the help doesn't show me the docstring of foo as expected, it shows:

Help on function _decorator in module __main__:

_decorator()

Without the decorator, the help is correct:

Help on function foo in module __main__:

foo()
    the magic foo function

I know, that the function foo is wrapped by the decorator, and so the function object is not the function foo any more. But what is a nice solution to get the docstring (and the help) as expected?

+1  A: 

I found a solution, but don't know if it's really nice:

def decorator(f):
    def _decorator():
        print 'decorator active'
        f()
    _decorator.__name__=f.__name__
    _decorator.__doc__=f.__doc__
    return _decorator

The part with _decorator.__name__=f.__name__ seems a little bit hideous... What do you think?

Günther Jehle
In fact this is (almost ?) exactly what functools.wraps do :)
thomas
It doesn't look hideous to me. It says exactly what you want it to say. "I want the name of this function to be 'myfunction' instead of '_decorator'."
jcdyer
+10  A: 

Use functools.wraps() to update the attributes of the decorator:

from functools import wraps

def decorator(f):
    @wraps(f)
    def _decorator():
        print 'decorator active'
        f()
    return _decorator

@decorator
def foo():
    '''the magic foo function'''
    print 'this is function foo'

help(foo)

Also see the Standard Library documentation for functools.

Pär Wieslander
This doesn't work if `foo` takes any arguments - they get replaced by whatever the `_decorator` uses. This is a problem especially when you want your decorator to take `*args, **kwds`. I've never been able to find a way to get the docstring correct using `functools.wraps`.
Scott Griffiths
@Scott Griffiths: The docstring will still be correct even if `foo` takes arguments. However, `help(foo)` will display the parameter list of the `_decorator`, since it actually replaces the `foo` function. There's no good way around this if you're writing decorators that take arbitrary arguments using `*args, **kwargs`, but for me the important point is that the docstring is kept intact. Parameter details could always be specified in the docstring for clarity.
Pär Wieslander
Thanks for the extra information. I've recently been failing to get the help description correct for decorated functions - it seems a pretty poor state of affairs, but I understand the difficulty as the decorated function could have a completely different signature. Still, there must be a way... :)
Scott Griffiths
There is a way to do it. The decorator module http://pypi.python.org/pypi/decorator does it by using a trick. The trick is to rebuild the decorator signature and run exec on it. You can find the trick in line 118 in decorator.py. I think however that this approach is extreme.
Nadia Alramli
+1  A: 

Take a look at functools.wraps: http://docs.python.org/library/functools.html

Mark Byers