Python decorator handling docstrings

17,992

Solution 1

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.

Solution 2

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?

Solution 3

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

Share:
17,992
Günther Jena
Author by

Günther Jena

I started programming with a Commodore Plus/4 (a successor of the famous C64). Hacking in BASIC became boring so I started to program in assembler. Learning C in school and C++, C# and Java later I ended up with Python, C and C++ as my favorite languages. I'm part time teacher at a polytechnical school in Austria teaching programming and digital electronics. In my main job I'm working on embedded systems for lasers.

Updated on June 13, 2022

Comments

  • Günther Jena
    Günther Jena about 2 years

    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?

  • Scott Griffiths
    Scott Griffiths over 14 years
    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.
  • Pär Wieslander
    Pär Wieslander over 14 years
    @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.
  • Scott Griffiths
    Scott Griffiths over 14 years
    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... :)
  • thomas
    thomas over 14 years
    In fact this is (almost ?) exactly what functools.wraps do :)
  • Nadia Alramli
    Nadia Alramli over 14 years
    There is a way to do it. The decorator module 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.
  • jcdyer
    jcdyer over 14 years
    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'."
  • John Carrell
    John Carrell over 6 years
    It seems that functools.wraps does enable help() to work properly now. I'm struggling to find when this changed but I'm still using Python 2.7. Happy day!
  • MikeyE
    MikeyE over 6 years
    How would I use this method with the docorator @classmethod?
  • Azat Ibrakov
    Azat Ibrakov almost 6 years
    you should not re-invent the wheel, especially when there is a working function from standard library which does this, well-tested, maintained and documented
  • oxer
    oxer over 5 years
    What about putting a line like _decorator.__doc__ += '\n(decorated by <decorator description>)' right before return _decorator? Seems like that would make the docstring more informative but I haven't seen that so wondering if there is a reason not to do that.
  • Markus
    Markus about 5 years
    I cannot go along with @Azat Ibrakov; I'd always prefer a clear to read solution which is not dependent on any (also eventually changing) libraries. Therefore, I realy like this simple and straight forward approach (+1).
  • patzm
    patzm about 3 years
    also, this solution doesn't copy the docstrings of the arguments of f (if it had any) and return type. functools.wraps does this though.