xagg
xagg

Reputation: 546

Display source code causing an exception in Python

How to retrieve the line of code (not the line number) that causes an exception?

This is an attempt.

import sys, inspect
try:
    1 / 0
except Exception as e:
    exc_type, exc_obj, exc_tb = sys.exc_info()
    code = str(inspect.getsourcelines(exc_tb.tb_frame.f_code))        

print(code)

It returns the first line of the script, not the line that causes the exception.

(['import sys, inspect\n'], 1)

Upvotes: 4

Views: 373

Answers (2)

Tarick Welling
Tarick Welling

Reputation: 3255

To add to spinx's answer, there are attributes in every FrameInfo object

So we can make the code more readable by using them:

import sys, inspect

try:
    y = 2
    a = 1 / 0
except Exception as e:
    exception_occr_at_file = inspect.trace()[0].filename
    line_no = inspect.trace()[0].lineno
    code = inspect.trace()[0].code_context

print(exception_occr_at_file)
print(line_no)
print(code)

I also wasn't too happy with the random indexing at 0 for the frames. Reading the description of inspect.info, it doesn't sound like the correct position is being indexed.

inspect.trace(context=1) Return a list of FrameInfo objects for the stack between the current frame and the frame in which an exception currently being handled was raised in. The first entry in the list represents the caller; the last entry represents where the exception was raised.

0 will give the line of the caller, not the thrower. A nice example is found here. (I'm copying it here for easy of use.)

import sys
import inspect
from pprint import pprint


def errorer():
    raise Exception('foo')

def syser():
    try:
        errorer()
    except Exception, e:
        tb = sys.exc_info()[2]
        print tb.tb_frame
        print tb.tb_lasti
        print tb.tb_lineno
        print tb.tb_next

def inspecter():
    try:
        errorer()
    except Exception, e:
        pprint(inspect.trace())

With the following output:

>>> syser()
<frame object at 0x1441240>
6
10
<traceback object at 0x13eb3b0>
>>> inspecter()
[(<frame object at 0x14a5590>,
  '/tmp/errors.py',
  22,
  'inspecter',
  None,
  None),
 (<frame object at 0x14a21b0>,
  '/tmp/errors.py',
  8,
  'errorer',
  None,
  None)]

We can clearly see that indexing at 0 gives us the line the function errorer is called in inspecter and -1 gives us the point where we throw the exception.

So if we want to know which line throws the exception, -1 is correct, if we however want to know the root of our call tree of a failing function/object, 0 is correct.

Upvotes: 0

Sphinx
Sphinx

Reputation: 10729

Below codes works, but it is inflexible. Someone else may have better solution.

import sys, inspect
try:
    y = 2
    a = 1 / 0
except Exception as e:
    exception_occr_at_file = inspect.trace()[0][1]
    line_no = inspect.trace()[0][2]
    code = inspect.trace()[0][4]

print(exception_occr_at_file)
print(line_no)
print(code)

#Ouput:
C:\Users\jianc\Desktop\test\test_print.py
4
['    a = 1 / 0\n']

Upvotes: 2

Related Questions