User
User

Reputation: 14853

python decorator with arguments of decorated function

When I wrap a function with @, how do I make the wrapper function look & feel exactly like the wrapped function? help(function) in particular.

Some code:

>>> def wraps(f):
    def call(*args, **kw):
        print('in', f, args, kw) # example code. I need to transfer the arguments to another process and pickle them.
        return f(*args, **kw)
    return call

>>> def g():pass

>>> @wraps
def f(a, b = 1, g = g, *args, **kw):
    pass

>>> help(f)
Help on function call in module __main__:

call(*args, **kw) # this line bothers me. It should look different, look below

>>> def f(a, b = 1, g = g, *args, **kw):
    pass

>>> help(f)
Help on function f in module __main__:

f(a, b=1, g=<function g at 0x02EE14B0>, *args, **kw) # help(f) should look like this.

Motivation: It would also be nice to see the arguments when the help window pops up, when I type f( * plopp * I see (a, b = 1, g = g, *args, **kw). (in this case in the IDLE Python Shell)

I had a look at the inspect module which helps me with nice formatting. The problem is still there: how do I do this with arguments..

Default mutable argument passing like def f(d = {}): does not need to work since I transfer the arguments to another process and the identity would be lost anyway.

Upvotes: 3

Views: 883

Answers (3)

whereswalden
whereswalden

Reputation: 4959

I think the other answers are preferable, but if for some reason you don't want to use an external module, you could always alter your decorator like so:

def wraps(f):
  def call(*args, **kw):
    print('in', f, args, kw)
    return f(*args, **kw)
call.__name__ = f.__name__
call.__doc__ = f.__doc__
return call

Upvotes: 1

unutbu
unutbu

Reputation: 879143

functools.wraps can be used to copy the name and docstring of the function. Copying the original function signature is considerably harder to do from scratch.

If you use the third-party decorator module, however, then

import decorator


@decorator.decorator
def wraps(f):
    def call(*args, **kw):
        print('in', f, args, kw) 
        return f(*args, **kw)
    return call


def g():pass

@wraps
def f(a, b = 1, g = g, *args, **kw):
    pass

help(f)

yields

Help on function f in module __main__:

f(a, b=1, g=<function g>, *args, **kw)

Upvotes: 3

roippi
roippi

Reputation: 25954

Use functools.wraps:

from functools import wraps

def wrapper(f):
    @wraps(f)
    def call(*args, **kw):
        print('in', f, args, kw)
        return f(*args, **kw)
    return call

@wrapper
def f(a, b = 1, g = g, *args, **kw):
    pass

help(f)
Help on function f in module __main__:

f(a, b=1, g=<function g at 0x7f5ad14a6048>, *args, **kw)

This preserves the __name__ and __doc__ attributes of your wrapped function.

Upvotes: 2

Related Questions