[issue23764] functools.wraps should be able to change function argspec as well

productivememberofsociety666 report at bugs.python.org
Thu Mar 26 00:50:07 CET 2015


productivememberofsociety666 added the comment:

You're probably right and it's next to impossible to implement what I want in a clean manner. I'll get back to that at the end, but first for completeness's sake two examples to illustrate what this issue is even about.


````
import functools

def wrapper(func):
  @functools.wraps(func)
  def new_func(*args, **kwargs):
    pass
  return new_func

def to_be_wrapped(a):
  pass

wrapped = wrapper(to_be_wrapped)

wrapped(1) # Ok
wrapped(1, 2, 3) # Also ok, but shouldn't be! Should raise TypeError
````

The second call to wrapped() should fail because it's supposed to be a wrapper around to_be_wrapped(), which only takes 1 argument. But it succeeds because functools.wraps only changes "superficial" function attributes such as its signature or docstring. This creates a mismatch between expected and actual behaviour of wrapped().

Contrast this with how it works when using the decorator package I mentioned:

````
import decorator

def to_be_wrapped(x):
  print("f(x) called")
  pass

def _wrapper(func, *args, **kwargs):
  # Put actual functionality of your decorator here
  pass

def wrapper(func):
  return decorator.decorator(_wrapper, func)

wrapped = wrapper(to_be_wrapped)

wrapped(1) # Ok, because to_be_wrapped takes exactly 1 argument
wrapped(1, 2, 3) # raises TypeError for too many arguments, as it should
````

Like I said, the details of how it is used are different from those of functools.wraps. But the important thing is that, at the end, wrapped()'s argspec matches that of to_be_wrapped() and an appropriate error is raised by the second call.
Note that this does NOT work via propagation from a call to to_be_wrapped() or anything of the sort, as can be verified by the lack of output to stdout.


Now, the only problem is: I had a look at how this is achieved in the decorator package's source code and if I understand it correctly, they are doing some nasty nasty things with exec() to create a function with an argspec of their choosing at runtime. If this is in fact the only way to do it, I agree that it has no place in the standard library and should only be available via 3rd party libraries. Sorry for wasting your time then.

----------

_______________________________________
Python tracker <report at bugs.python.org>
<http://bugs.python.org/issue23764>
_______________________________________


More information about the Python-bugs-list mailing list