392

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
techenthu
  • 158
  • 11
Claudiu
  • 224,032
  • 165
  • 485
  • 680
  • 5
    Use the built-in [traceback](http://docs.python.org/library/traceback.html) module. – Ned Deily Aug 14 '09 at 16:10
  • 1
    It may also be helpful to print line of code, where exception happened: see http://stackoverflow.com/questions/14519177/python-exception-handling-line-number/20264059#20264059 – Apogentus Nov 28 '13 at 10:52
  • None of these solutions find the line of code *where the exception happened*, @Apogentus. Maybe it's because it's Python 2 code or something but these solutions find the line of code much nearer where the exception is caught than where it was raised. – NeilG Jul 12 '23 at 01:47

7 Answers7

519
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)
David Cain
  • 16,484
  • 14
  • 65
  • 75
Ants Aasma
  • 53,288
  • 15
  • 90
  • 97
  • 65
    You should be careful about unpacking sys.exc_info() into local variables, since if you get an exception in the except handler, the local vars could get kept in a circular reference and not GC'd. Best practice is to always just use slices off of sys.exc_info() instead. Or use other modules like traceback, as other posters have suggested. – Daniel Pryden Aug 17 '09 at 23:13
  • 2
    is tb just exc_tb? and os.path.split(blabla)[1] is os.path.basename(balbal) – sunqiang Aug 20 '09 at 01:23
  • 1
    With this code, I get (when `try: 1/0`) : ` integer division or modulo by zero`. How to have `ZeroDivisionError: integer division or modulo by zero` instead ? – Basj Feb 22 '14 at 09:19
  • 5
    @Basj: With sys.exc_info()[0].__name__ you get the plain name of the type. – Johannes Overmann Mar 03 '14 at 17:21
  • 10
    @DanielPryden Python docs are also using the same unpacking method https://docs.python.org/2/library/traceback.html#traceback-examples – user Aug 07 '14 at 03:49
  • 2
    I am importing a Class from a different file, when I use this solution the error points to the line in the current file on which the function from the class is called. The actual error occurs in a class file but this solution only shows the error in a current file. Any way to dig deeper? – Ivan Bilan Feb 19 '16 at 10:31
  • 5
    @RobM: Yes, it's thread-safe. `sys.exc_info()` was introduced to deal with thread-safety problems in the previous API. Its output is specific to both the current thread and the current stack frame. – user2357112 Apr 13 '16 at 23:21
  • 1
    use logging.exception(e) if using logging lib – saurabh Oct 11 '18 at 05:47
  • Is sys.exc_info() thread safe? – kta Dec 09 '22 at 06:59
  • @DanielPryden's comment no longer applies in Python 3, refer to https://stackoverflow.com/questions/11414894/extract-traceback-info-from-an-exception-object#comment19173332_11417308 – user202729 May 20 '23 at 01:03
320

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
nu everest
  • 9,589
  • 12
  • 71
  • 90
55

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
pythonlarry
  • 2,316
  • 2
  • 20
  • 17
51

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')
user
  • 23,260
  • 9
  • 113
  • 101
Stryker
  • 5,732
  • 1
  • 57
  • 70
46

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.

Luc
  • 5,339
  • 2
  • 48
  • 48
43

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
    }
  ]
}
Christian
  • 3,708
  • 3
  • 39
  • 60
1

This is what I used to get the file name.

__file__.__str__

To sum it, I created a page to show errors. Return this on exception.

context={
                'details':'Type of error:{}:Function name:{}:Line number:{}'.format(exc_type, fname, exc_tb.tb_lineno),
                'error_details':str(e),
                'filename':__file__.__str__,
            })

How it looks in case an exception occurs:

enter image description here

Arindam Roychowdhury
  • 5,927
  • 5
  • 55
  • 63
  • 4
    FYI, copying the text instead of making a pixely (jpeg-y) screenshot of text is more searchable and useful. – Luc Nov 09 '21 at 00:25