The problem of anonymity with decorators

  • Thread starter Franck Pommereau
  • Start date
F

Franck Pommereau

Dear all,

I would like to work around the "anonymizing" effect of decorators when
an exception is raised in the wrapped function. Let's consider the
following example:

def int_result (fun) :
def wrapped (*largs, **kwargs) :
result = fun(*largs, **kwargs)
if not isinstance(result, int) :
raise TypeError, "should return int"
return result
return wrapped

@int_result
def add (a, b) :
return a+b

print add(1, 2)
print add("foo", "bar")

As expected, the last line results in raising TypeError:

Traceback (most recent call last):
File "wrapping.py", line 14, in ?
print add("foo", "bar")
File "wrapping.py", line 5, in wrapped
raise TypeError, "should return int"
TypeError: should return int

My problem is that the errors comes from a function named "wrapped"
while I'd prefer to see here the name of the wrapped function. Indeed,
the code above is only a stripped down sample code but in my
application, I'll have a lot of functions, all wrapped the same way. So
I'd prefer a traceback like:

Traceback (most recent call last):
File "wrapping.py", line 14, in ?
print add("foo", "bar")
File "wrapping.py", line 8, in add (wrapped)
@int_result
TypeError: should return int

Where the exception seems to come from the point where add "was"
wrapped, instead of from the inside of the wrapper.

I tried to change wrapper.__name__ and wrapper.func_name but it does not
change the traceback, and wrapper.func_code.co_name is read-only. I also
tried, unsuccessfully, to rename functions with:

import new
def rename (fun, name) :
return new.function(fun.func_code, {}, name)

So, my questions:
- can I change a function name so that it affects the traceback when an
exception is raised in the function?
- is there some special trick to raise an exception making it, in
appearance, coming from somewhere else?

Thanks in advance for any answer.

Regards,
Franck
 
A

Alex Martelli

Franck Pommereau said:
import new
def rename (fun, name) :
return new.function(fun.func_code, {}, name)

You need to make a new code object too:

def int_result(fun) :
def wrapped(*largs, **kwargs) :
result = fun(*largs, **kwargs)
if not isinstance(result, int) :
raise TypeError, "should return int"
return result
w = wrapped.func_code
c = new.code(w.co_argcount, w.co_nlocals, w.co_stacksize,
w.co_flags, w.co_code, w.co_consts, w.co_names,
w.co_varnames, w.co_filename, fun.func_name,
w.co_firstlineno, w.co_lnotab,
w.co_freevars, w.co_cellvars)
return new.function(c, fun.func_globals, fun.func_name,
fun.func_defaults, wrapped.func_closure)

Of course, you should refactor these last three statement into a

def remix(wrapped, fun): ...
""" return a function like 'wrapped' but w/name and defaults fm 'fun'
"""
...same 3 statements as above, from w = ... onwards...



Alex
 
M

Michele Simionato

Using my decorator module
(http://www.phyast.pitt.edu/~micheles/python/documentation.html,
http://www.phyast.pitt.edu/~micheles/python/decorator.zip) you solve
the problem of the
name, but the traceback is still not perfect. You get:

# dec_traceback.py
from ms.decorator import decorator

@decorator
def int_result (fun, *largs, **kwargs) :
result = fun(*largs, **kwargs)
if not isinstance(result, int) :
raise TypeError, "should return int"
return result

@int_result
def add (a, b) :
return a+b

print add(1, 2)
print add("foo", "bar")

# end dec_traceback.py

$ python dec_traceback.py
3
Traceback (most recent call last):
File "dec_traceback.py", line 15, in ?
print add("foo", "bar")
File "<string>", line 2, in add
File "dec_traceback.py", line 7, in int_result
raise TypeError, "should return int"
TypeError: should return int

The reason of <string> is that the module internally uses 'exec'. There
should be a way around
that, anyway, but I have no time to check it right now.
Still, the module may be of inspiration to you.

Michele Simionato
 
F

Franck Pommereau

Alex, Michele and Skip,

Many thanks for your help, I should find my way by putting all these
information together.

Best regards,
Franck
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,015
Latest member
AmbrosePal

Latest Threads

Top