a little more explicative error message?
Terry Reedy
tjreedy at udel.edu
Tue Jul 16 04:25:06 EDT 2013
On 7/16/2013 1:44 AM, Vito De Tullio wrote:
> Hi
>
> I was writing a decorator and lost half an hour for a stupid bug in my code,
> but honestly the error the python interpreter returned to me doesn't
> helped...
>
> $ python3
> Python 3.3.0 (default, Feb 24 2013, 09:34:27)
> [GCC 4.7.2] on linux
> Type "help", "copyright", "credits" or "license" for more information.
>>>> from functools import wraps
>>>> def dec(fun):
> ... @wraps
> ... def ret(*args, **kwargs):
> ... return fun(*args, **kwargs)
At this point, when dec(fun) is called, the interpreter *successfully*
executes ret = wraps(ret), which works, but is wrong, because wraps
should be called with the wrapped function fun as its argument, not the
wrapper function ret. The interpreter should instead execute
ret = partial(update_wrapper, wrapped = fun, ...)(ret)
which will update ret to look like fun.
> ... return ret
> ...
>>>> @dec
> ... def fun(): pass
At this point, the interpreter *successfully* executes fun = dec(fun) =
(wraps(ret))(fun), which causes ret = wraps(ret) before returning ret.
Notice that when dec returns, the wraps code is over and done with.
Instead the interpreter should execute
fun = (partial(update_wrapper, wrapped = fun, ...)(ret))(fun)
> ...
>>>> fun()
> Traceback (most recent call last):
> File "<stdin>", line 1, in <module>
> TypeError: update_wrapper() missing 1 required positional argument:
> 'wrapper'
Because fun has not been properly wrapped because you left out a call.
> Soo... at a first glance, no tricks... can you tell where is the error? :D
Not exactly, but it must have something to do with wraps, so the first
thing I would do is to look at the wraps doc if I had not before. It
only takes a couple of minutes.
>>> from functools import wraps
>>> help(wraps)
Help on function wraps in module functools:
wraps(wrapped, assigned=('__module__', '__name__', '__qualname__',
'__doc__', '__annotations__'), updated=('__dict__',))
Decorator factory to apply update_wrapper() to a wrapper function
Returns a decorator that invokes update_wrapper() with the decorated
function as the wrapper argument and the arguments to wraps() as
the remaining arguments. ...
This is pretty clear that wraps is not a decorator but a function that
returns decorator, which means that is must be called with an argument
in the @del statement.
To really understand what is intended to happen so I could write the
commentary above, I looked at the code to see
def wraps(wrapped, ...):
'<docstring>'
return partial(update_wrapper, wrapped=wrapped,
assigned=assigned, updated=updated)
> As I said, the error is totally mine, I just forgot to pass the function as
> parameter to wraps. But... what is "update_wrapper()"? and "wrapper"? There
> is no useful traceback or something... just... this.
>
> Ok, the documentation clearly says:
>
> This is a convenience function to simplify applying partial() to
> update_wrapper().
>
> So, again, shame on me... I just read carefully the doc *after* 20 minutes
> trying everything else... still... I think should be useful if wraps()
> intercept this error saying something more explicit about the missing fun
> parameter...
How is a function that has already been called and has returned without
apparent error supposed to catch a later error caused by its return not
being correct, due to its input not being correct?
Your request is like asking a function f(x) to catch ZeroDivisionError
if it return a 0 and that 0 is later used as a divisor after f returns,
as in 1/f(x).
When you call wraps with a callable, there is no way for it to know that
the callable in intended to the be the wrapper instead of the wrappee,
unless it were clairvoyant ;-).
--
Terry Jan Reedy
More information about the Python-list
mailing list