boltons icon indicating copy to clipboard operation
boltons copied to clipboard

Python `funcutils.wraps` breaks when keyword only arguments involved

Open thorwhalen opened this issue 4 years ago • 1 comments

Code worth 1000 words:

>>> from boltons.funcutils import wraps
>>>
>>> def g(a: float, b=10):
...     return a * b
>>>
>>> def f(a: int,  *, b=1):
...     return a * b
>>>
>>> # all is well here...
>>> assert f(3) == 3
>>> assert g(3) == 30
>>> assert wraps(f)(g)(3) == 3  # yay, g got the f default (not so with functools.wraps!)
>>>
>>> # but this doesn't work
>>> wraps(g)(f)(3)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<boltons.funcutils.FunctionBuilder-1>", line 2, in g
TypeError: f() takes 1 positional argument but 2 were given

thorwhalen avatar Jul 07 '20 22:07 thorwhalen

Ah, I was confused when this bug came in, bc I've got scars from maintaining this area and dealing with the expansion of function signature semantics in python 3

You'll notice that when kw-only args are involved the first time (wraps(f)(g)) it works fine. The other way around, however, wraps(g)(f) fails, because by the very nature of funcutils.wraps, you're trying to give f the same signature as g. And f's signature is not like g's. g supports positional b and f() does not.

If you want to make a wrapper which converts a keyword-only argument to one that can be positional or keyword only, that'll require a different approach for now.

A potential fix would be to pass all function arguments as keywords. But doubt that's the right direction, because, while I have yet to add positional argument only support, that'll definitely throw a wrench into things.

mahmoud avatar Jul 20 '20 02:07 mahmoud