including column information when an exception occurs
Tim Peters
tim_one at email.msn.com
Sat Dec 4 17:12:21 EST 1999
[Michael McCandless]
> When a Python exception occurs, the interpreter shows you the stack
> traceback to the line where the error occurred. Why, when it gets down
> to the actual line, does it not also report, eg, the column or
> subexpression in which the error occurs?
It does, but only if the exception is a Syntaxrror. Column information is
available while the program is being parsed (the only time you can get a
SyntaxError), but is not available at runtime (which is when almost all
other exceptions get raised). The interpreter incurs a good deal of pain to
keep the line number available at runtime, but even that's limited (e.g., if
an exception occurs in an expression spanning multiple lines, you only get
the number of the line that begins the stmt that contains the expression).
The easiest way to get more context is probably to disassemble the byte code
near the point of failure. For example, putting the attached in module
tbrev.py and running your test case like so:
def test0(tup, i0, i1):
test1(tup, i0, i1)
def test1(tup, i0, i1):
return tup[i0] + tup[i1]
try:
test0((1, 2, 3), 2, 4)
except:
from misc import tbrev # import from where you store it
tbrev.show_context()
prints this new line before the usual traceback:
Probably near a reference to local variable i1,
That wouldn't do you a lot of good if the failing code looked like
tup1[i] + tup2[i]
instead! Disassembling more context gets messier. This is a job for
Michael Hudson <0.9 wink>.
if-python-were-written-in-python-it-would-be-easier-ly y'rs - tim
import dis
LOAD_GLOBAL = dis.opname.index('LOAD_GLOBAL')
LOAD_FAST = dis.opname.index('LOAD_FAST')
LOAD_ATTR = dis.opname.index('LOAD_ATTR')
LOADOPS = (LOAD_GLOBAL, LOAD_FAST, LOAD_ATTR)
HAVE_ARGUMENT = dis.HAVE_ARGUMENT
del dis
def show_context():
import sys
tb = sys.exc_info()[2]
if tb is None:
return
while tb.tb_next:
tb = tb.tb_next
lasti = tb.tb_lasti
code = tb.tb_frame.f_code
bytecode = code.co_code
i = 0
limit = min(lasti, len(bytecode))
loads = []
while i < limit:
c = bytecode[i]
op = ord(c)
i = i+1
if op >= HAVE_ARGUMENT:
oparg = ord(bytecode[i]) + ord(bytecode[i+1])*256
i = i+2
if op in LOADOPS:
loads.append((op, oparg))
if loads:
op, arg = loads[-1]
if op == LOAD_GLOBAL:
msg = "global variable " + code.co_names[arg]
elif op == LOAD_FAST:
msg = "local variable " + code.co_varnames[arg]
elif op == LOAD_ATTR:
msg = "attribute " + code.co_names[arg]
else:
msg = "<OOPS! I got confused>"
print "Probably near a reference to", msg + ","
raise
More information about the Python-list
mailing list