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

Reply via email to