297
votes

Catching an exception that would print like this:

Traceback (most recent call last):
  File "c:/tmp.py", line 1, in <module>
    4 / 0
ZeroDivisionError: integer division or modulo by zero

I want to format it into:

ZeroDivisonError, tmp.py, 1
6
Use the built-in traceback module.Ned Deily
It may also be helpful to print line of code, where exception happened: see stackoverflow.com/questions/14519177/…Apogentus

6 Answers

432
votes
import sys, os

try:
    raise NotImplementedError("No error")
except Exception as e:
    exc_type, exc_obj, exc_tb = sys.exc_info()
    fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1]
    print(exc_type, fname, exc_tb.tb_lineno)
203
votes

Simplest form that worked for me.

import traceback

try:
    print(4/0)
except ZeroDivisionError:
    print(traceback.format_exc())

Output

Traceback (most recent call last):
  File "/path/to/file.py", line 51, in <module>
    print(4/0)
ZeroDivisionError: division by zero

Process finished with exit code 0
52
votes

Source (Py v2.7.3) for traceback.format_exception() and called/related functions helps greatly. Embarrassingly, I always forget to Read the Source. I only did so for this after searching for similar details in vain. A simple question, "How to recreate the same output as Python for an exception, with all the same details?" This would get anybody 90+% to whatever they're looking for. Frustrated, I came up with this example. I hope it helps others. (It sure helped me! ;-)

import sys, traceback

traceback_template = '''Traceback (most recent call last):
  File "%(filename)s", line %(lineno)s, in %(name)s
%(type)s: %(message)s\n''' # Skipping the "actual line" item

# Also note: we don't walk all the way through the frame stack in this example
# see hg.python.org/cpython/file/8dffb76faacc/Lib/traceback.py#l280
# (Imagine if the 1/0, below, were replaced by a call to test() which did 1/0.)

try:
    1/0
except:
    # http://docs.python.org/2/library/sys.html#sys.exc_info
    exc_type, exc_value, exc_traceback = sys.exc_info() # most recent (if any) by default

    '''
    Reason this _can_ be bad: If an (unhandled) exception happens AFTER this,
    or if we do not delete the labels on (not much) older versions of Py, the
    reference we created can linger.

    traceback.format_exc/print_exc do this very thing, BUT note this creates a
    temp scope within the function.
    '''

    traceback_details = {
                         'filename': exc_traceback.tb_frame.f_code.co_filename,
                         'lineno'  : exc_traceback.tb_lineno,
                         'name'    : exc_traceback.tb_frame.f_code.co_name,
                         'type'    : exc_type.__name__,
                         'message' : exc_value.message, # or see traceback._some_str()
                        }

    del(exc_type, exc_value, exc_traceback) # So we don't leave our local labels/objects dangling
    # This still isn't "completely safe", though!
    # "Best (recommended) practice: replace all exc_type, exc_value, exc_traceback
    # with sys.exc_info()[0], sys.exc_info()[1], sys.exc_info()[2]

    print
    print traceback.format_exc()
    print
    print traceback_template % traceback_details
    print

In specific answer to this query:

sys.exc_info()[0].__name__, os.path.basename(sys.exc_info()[2].tb_frame.f_code.co_filename), sys.exc_info()[2].tb_lineno
37
votes

Here is an example of showing the line number of where exception takes place.

import sys
try:
    print(5/0)
except Exception as e:
    print('Error on line {}'.format(sys.exc_info()[-1].tb_lineno), type(e).__name__, e)

print('And the rest of program continues')
13
votes

You could achieve this without having to import traceback:

try:
    func1()
except Exception as ex:
    trace = []
    tb = ex.__traceback__
    while tb is not None:
        trace.append({
            "filename": tb.tb_frame.f_code.co_filename,
            "name": tb.tb_frame.f_code.co_name,
            "lineno": tb.tb_lineno
        })
        tb = tb.tb_next
    print(str({
        'type': type(ex).__name__,
        'message': str(ex),
        'trace': trace
    }))

Output:

{

  'type': 'ZeroDivisionError',
  'message': 'division by zero',
  'trace': [
    {
      'filename': '/var/playground/main.py',
      'name': '<module>',
      'lineno': 16
    },
    {
      'filename': '/var/playground/main.py',
      'name': 'func1',
      'lineno': 11
    },
    {
      'filename': '/var/playground/main.py',
      'name': 'func2',
      'lineno': 7
    },
    {
      'filename': '/var/playground/my.py',
      'name': 'test',
      'lineno': 2
    }
  ]
}
8
votes

Without any imports, but also incompatible with imported modules:

try:
    raise TypeError("Hello, World!")  # line 2
except Exception as e:
    print(
        type(e).__name__,          # TypeError
        __file__,                  # /tmp/example.py
        e.__traceback__.tb_lineno  # 2
    )

$ python3 /tmp/example.py
TypeError /tmp/example.py 2

To reiterate, this does not work across imports or modules, so if you do import X; try: X.example(); then the filename and line number will point to the line containing X.example() instead of the line where it went wrong within X.example(). If anyone knows how to easily get the file name and line number from the last stack trace line (I expected something like e[-1].filename, but no such luck), please improve this answer.