Python creates decorator save function arguments

I am trying to write a decorator that stores the arguments of the functions that it decorates. The motivation for this is to write a decorator that interacts well with pytest.fixtures .

Suppose we have a function foo . It takes one argument a .

 def foo(a): pass 

If we get the argument spec foo

 >>> inspect.getargspec(foo) ArgSpec(args=['a'], varargs=None, keywords=None, defaults=None) 

We often want to create a decorator where the wrapper function passes all its arguments verbatim to the wrapped function. The most obvious way to do this is using *args and **kwargs .

 def identity_decorator(wrapped): def wrapper(*args, **kwargs): return wrapped(*args, **kwargs) return wrapper def identity_decorator(wrapped): def wrapper(*args, **kwargs): return wrapped(*args, **kwargs) return wrapper @identity_decorator def foo(a): pass 

This, unsurprisingly, creates a function with an argument specification that reflects *args and **kwargs .

 >>> inspect.getargspec(foo) ArgSpec(args=[], varargs='args', keywords='kwargs', defaults=None) 

Is there a way to change the argument specification to match the completed function, or create a function with the correct argument specification initially?

+6
source share
2 answers

As stated in the comments, you can use the decorator module, or you can use eval evil powers to create a lambda function with the correct signature:

 import inspect def identity_decorator(wrapped): argspec = inspect.getargspec(wrapped) args = inspect.formatargspec(*argspec) def wrapper(*args, **kwargs): return wrapped(*args, **kwargs) func = eval('lambda %s: wrapper%s' % (args.strip('()'), args), locals()) return func @identity_decorator def foo(a): pass 

This is kind of hacky, but it saves the function arguments:

 >>> inspect.getargspec(foo) ArgSpec(args=['a'], varargs=None, keywords=None, defaults=None) 
+2
source

AFAIK, this is only possible with Python 3.3 with a Signature object:

 def identity_decorator(wrapped): def wrapper(*args, **kwargs): return wrapped(*args, **kwargs) wrapper.__signature__ = inspect.signature(wrapped) # the magic is here! return wrapper 

Then you can do:

 @identity_decorator def foo(a): pass 

and finally:

 >>> inspect.getargspec(foo) ArgSpec(args=['a'], varargs=None, keywords=None, defaults=None) 
+1
source

All Articles