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
--
http://mail.python.org/mailman/listinfo/python-list