Get Only the Last Items in a Traceback

G

gregpinero

I'm running code via the "exec in context" statement within a much
larger program. What I would like to do is capture any possible
errors and show a pretty traceback just like the Python interactive
interpreter does, but only show the part of the traceback relating to
the code sent to exec.

For example here is the code I'm using:

try:
exec code
except Exception,Err:
traceback.print_exc()

Which gives me something like this when it catches an exception:

Traceback (most recent call last):
File "/....py", line 843, in run
semi_safe_exec.safe_eval(code.replace('\r',''),context,5)
File ".....py", line 400, in safe_eval
exec_timed(code, context, timeout_secs)
File ".....py", line 353, in exec_timed
exec code in context
File "<string>", line 7, in ?
File "<string>", line 5, in func2
File "<string>", line 2, in func1
ZeroDivisionError: integer division or modulo by zero

What I want to print instead is just something like:

Traceback (most recent call last):
File "<string>", line 7, in ?
File "<string>", line 5, in func2
File "<string>", line 2, in func1
ZeroDivisionError: integer division or modulo by zero

Thanks in advance for the help.

-Greg

P.S. if it matters, for this example, code in exec code is:

def func1():
print 7/0

def func2():
func1()

func2()
 
P

Peter Otten

Am Wed, 12 Sep 2007 02:09:28 +0000 schrieb (e-mail address removed):
I'm running code via the "exec in context" statement within a much
larger program. What I would like to do is capture any possible
errors and show a pretty traceback just like the Python interactive
interpreter does, but only show the part of the traceback relating to
the code sent to exec.

For example here is the code I'm using:

try:
exec code
except Exception,Err:
traceback.print_exc()

Which gives me something like this when it catches an exception:

Traceback (most recent call last):
File "/....py", line 843, in run
semi_safe_exec.safe_eval(code.replace('\r',''),context,5)
File ".....py", line 400, in safe_eval
exec_timed(code, context, timeout_secs)
File ".....py", line 353, in exec_timed
exec code in context
File "<string>", line 7, in ?
File "<string>", line 5, in func2
File "<string>", line 2, in func1
ZeroDivisionError: integer division or modulo by zero

What I want to print instead is just something like:

Traceback (most recent call last):
File "<string>", line 7, in ?
File "<string>", line 5, in func2
File "<string>", line 2, in func1
ZeroDivisionError: integer division or modulo by zero

Thanks in advance for the help.

-Greg

P.S. if it matters, for this example, code in exec code is:

def func1():
print 7/0

def func2():
func1()

func2()

Your assessment is wrong. You only get the extra lines in the traceback if
you don't immediately wrap the exec statement in a try ... except block:

$ cat snip_traceback1.py
import traceback

def alpha():
try:
beta()
except Exception, e:
traceback.print_exc()

def beta():
gamma()

def gamma():
exec s in {}

s = """
def delta():
epsilon()

def epsilon():
1/0
delta()
"""

if __name__ == "__main__":
alpha()

$ python snip_traceback1.py
Traceback (most recent call last):
File "snip_traceback1.py", line 5, in alpha
beta()
File "snip_traceback1.py", line 10, in beta
gamma()
File "snip_traceback1.py", line 13, in gamma
exec s in {}
File "<string>", line 7, in <module>
File "<string>", line 3, in delta
File "<string>", line 6, in epsilon
ZeroDivisionError: integer division or modulo by zero

So the first step is to move the try ... except closer to the exec:

$ cat snip_traceback2.py
import traceback

def alpha():
beta()

def beta():
gamma()

def gamma():
try:
exec s in {}
except Exception, e:
traceback.print_exc()

s = """
def delta():
epsilon()

def epsilon():
1/0
delta()
"""

if __name__ == "__main__":
alpha()

$ python snip_traceback2.py
Traceback (most recent call last):
File "snip_traceback2.py", line 11, in gamma
exec s in {}
File "<string>", line 7, in <module>
File "<string>", line 3, in delta
File "<string>", line 6, in epsilon
ZeroDivisionError: integer division or modulo by zero

You are almost there. Now let's strip off the outermost traceback:

$ cat snip_traceback3.py
import sys
import traceback

def alpha():
beta()

def beta():
gamma()

def gamma():
try:
exec s in {}
except Exception, e:
etype, value, tb = sys.exc_info()
traceback.print_exception(etype, value, tb.tb_next)

s = """
def delta():
epsilon()

def epsilon():
1/0
delta()
"""

if __name__ == "__main__":
alpha()

$ python snip_traceback3.py
Traceback (most recent call last):
File "<string>", line 7, in <module>
File "<string>", line 3, in delta
File "<string>", line 6, in epsilon
ZeroDivisionError: integer division or modulo by zero

Heureka.

Peter
 
?

=?ISO-8859-1?Q?Michael_Str=F6der?=

I'm running code via the "exec in context" statement within a much
larger program. What I would like to do is capture any possible
errors and show a pretty traceback just like the Python interactive
interpreter does, but only show the part of the traceback relating to
the code sent to exec.

For example here is the code I'm using:

try:
exec code
except Exception,Err:
traceback.print_exc()

Guess what's argument limit is for. Excerpt from the Python docs:

-------------------------------- snip --------------------------------
print_exc( [limit[, file]])
This is a shorthand for print_exception(sys.exc_type, sys.exc_value,
sys.exc_traceback, limit, file). (In fact, it uses sys.exc_info() to
retrieve the same information in a thread-safe way instead of using the
deprecated variables.)
-------------------------------- snip --------------------------------

Ciao, Michael.
 
P

Peter Otten

Am Wed, 12 Sep 2007 11:21:48 +0200 schrieb Michael Ströder:
Guess what's argument limit is for. Excerpt from the Python docs:

Unfortunately traceback.print_exc(limit=N) trims the wrong end of the
traceback:
.... alpha()
.... except:
.... traceback.print_exc()
....
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
File "<stdin>", line 1, in alpha
File "<stdin>", line 1, in beta
.... alpha()
.... except:
.... traceback.print_exc(limit=1)
....
Traceback (most recent call last):

Peter
 
G

gregpinero

Your assessment is wrong. You only get the extra lines in the traceback if
you don't immediately wrap the exec statement in a try ... except block:

$ cat snip_traceback1.py
import traceback

def alpha():
try:
beta()
except Exception, e:
traceback.print_exc()

def beta():
gamma()

def gamma():
exec s in {}

s = """
def delta():
epsilon()

def epsilon():
1/0
delta()
"""

if __name__ == "__main__":
alpha()

$ python snip_traceback1.py
Traceback (most recent call last):
File "snip_traceback1.py", line 5, in alpha
beta()
File "snip_traceback1.py", line 10, in beta
gamma()
File "snip_traceback1.py", line 13, in gamma
exec s in {}
File "<string>", line 7, in <module>
File "<string>", line 3, in delta
File "<string>", line 6, in epsilon
ZeroDivisionError: integer division or modulo by zero

So the first step is to move the try ... except closer to the exec:

$ cat snip_traceback2.py
import traceback

def alpha():
beta()

def beta():
gamma()

def gamma():
try:
exec s in {}
except Exception, e:
traceback.print_exc()

s = """
def delta():
epsilon()

def epsilon():
1/0
delta()
"""

if __name__ == "__main__":
alpha()

$ python snip_traceback2.py
Traceback (most recent call last):
File "snip_traceback2.py", line 11, in gamma
exec s in {}
File "<string>", line 7, in <module>
File "<string>", line 3, in delta
File "<string>", line 6, in epsilon
ZeroDivisionError: integer division or modulo by zero

You are almost there. Now let's strip off the outermost traceback:

$ cat snip_traceback3.py
import sys
import traceback

def alpha():
beta()

def beta():
gamma()

def gamma():
try:
exec s in {}
except Exception, e:
etype, value, tb = sys.exc_info()
traceback.print_exception(etype, value, tb.tb_next)

s = """
def delta():
epsilon()

def epsilon():
1/0
delta()
"""

if __name__ == "__main__":
alpha()

$ python snip_traceback3.py
Traceback (most recent call last):
File "<string>", line 7, in <module>
File "<string>", line 3, in delta
File "<string>", line 6, in epsilon
ZeroDivisionError: integer division or modulo by zero

Heureka.

Peter

Thanks for the help, Peter. That's exactly what I need. Now could I
use your tb.tb_next trick a couple times and thus avoid moving the try/
except?

-Greg
 
P

Peter Otten

Am Wed, 12 Sep 2007 15:09:02 +0000 schrieb (e-mail address removed):
Thanks for the help, Peter. That's exactly what I need.

Indeed. But not exactly what you want, it seems.
Now could I
use your tb.tb_next trick a couple times and thus avoid moving the try/
except?

Of course you can cut off the traceback at arbitrary positions. Here's an
example that uses the filename:

import traceback
import sys

def tb_filename(tb):
return tb.tb_frame.f_code.co_filename

def tb_iter(tb):
while tb is not None:
yield tb
tb = tb.tb_next

def alpha():
try:
beta()
except Exception, e:
etype, value, tb = sys.exc_info()
filename = tb_filename(tb)
for tb in tb_iter(tb):
if tb_filename(tb) != filename:
break
traceback.print_exception(etype, value, tb)

def beta():
gamma()

def gamma():
exec s in {}

s = """
def delta():
epsilon()

def epsilon():
1/0
delta()
"""

if __name__ == "__main__":
alpha()

Did I mention it already? It's better to move the try ... except.

Peter
 
G

gregpinero

Am Wed, 12 Sep 2007 15:09:02 +0000 schrieb (e-mail address removed):




Indeed. But not exactly what you want, it seems.


Of course you can cut off the traceback at arbitrary positions. Here's an
example that uses the filename:

import traceback
import sys

def tb_filename(tb):
return tb.tb_frame.f_code.co_filename

def tb_iter(tb):
while tb is not None:
yield tb
tb = tb.tb_next

def alpha():
try:
beta()
except Exception, e:
etype, value, tb = sys.exc_info()
filename = tb_filename(tb)
for tb in tb_iter(tb):
if tb_filename(tb) != filename:
break
traceback.print_exception(etype, value, tb)

def beta():
gamma()

def gamma():
exec s in {}

s = """
def delta():
epsilon()

def epsilon():
1/0
delta()
"""

if __name__ == "__main__":
alpha()

Did I mention it already? It's better to move the try ... except.

Peter


This approach is ideal. Indirect, but ideal :) I'll apply it
tonight.

It's complicated but suffice it to say it would be difficult to move
the try/except. Thanks for the help. Way to know about tracebacks,
very impressive!

-Greg
 
G

gregpinero

Am Wed, 12 Sep 2007 15:09:02 +0000 schrieb (e-mail address removed):


Indeed. But not exactly what you want, it seems.


Of course you can cut off the traceback at arbitrary positions. Here's an
example that uses the filename:

import traceback
import sys

def tb_filename(tb):
return tb.tb_frame.f_code.co_filename

def tb_iter(tb):
while tb is not None:
yield tb
tb = tb.tb_next

def alpha():
try:
beta()
except Exception, e:
etype, value, tb = sys.exc_info()
filename = tb_filename(tb)
for tb in tb_iter(tb):
if tb_filename(tb) != filename:
break
traceback.print_exception(etype, value, tb)

def beta():
gamma()

def gamma():
exec s in {}

s = """
def delta():
epsilon()

def epsilon():
1/0
delta()
"""

if __name__ == "__main__":
alpha()

Did I mention it already? It's better to move the try ... except.

Peter

This works great except for syntax errors. Any idea why your solution
doesn't catch those?

Here's the output it gives me, followed by the code I'm using (running
in Python 2.5):

Traceback (most recent call last):
File "traceback_test.py", line 27, in gamma
exec s in {}
File "<string>", line 6
print hi'
^
SyntaxError: EOL while scanning single-quoted string


file traceback_test.py contents:
import traceback
import sys

def tb_filename(tb):
return tb.tb_frame.f_code.co_filename

def tb_iter(tb):
while tb is not None:
yield tb
tb = tb.tb_next

def alpha():
try:
beta()
except Exception, e:
etype, value, tb = sys.exc_info()
filename = tb_filename(tb)
for tb in tb_iter(tb):
if tb_filename(tb) != filename:
break
traceback.print_exception(etype, value, tb)

def beta():
gamma()

def gamma():
exec s in {}

s = """
def delta():
epsilon()

def epsilon():
print hi'
delta()
"""

if __name__ == "__main__":
alpha()

-Greg
 
G

Gabriel Genellina

En Wed, 07 Nov 2007 01:53:31 -0300, (e-mail address removed)
This works great except for syntax errors. Any idea why your solution
doesn't catch those?

Here's the output it gives me, followed by the code I'm using (running
in Python 2.5):

Traceback (most recent call last):
File "traceback_test.py", line 27, in gamma
exec s in {}
File "<string>", line 6
print hi'
^
SyntaxError: EOL while scanning single-quoted string

Which would be your expected output?
Syntax errors happen when the code is *compiled*, before it gets executed.
You may catch syntax errors before even trying to execute the code, if you
do it in two steps:

try:
ccode = compile(s, filename, 'exec')
except SyntaxError:
...show the error appropiately...
...abort execution...
try:
exec ccode in {}
except Exception, e:
...handle exception...
 

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

Forum statistics

Threads
473,968
Messages
2,570,153
Members
46,699
Latest member
AnneRosen

Latest Threads

Top