When I catch an exception, how do I get the type, file, and line number?
Asked Answered
N

8

404

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
Norword answered 14/8, 2009 at 16:2 Comment(3)
Use the built-in traceback module.Verine
It may also be helpful to print line of code, where exception happened: see #14519677Audiology
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.Sherris
I
535
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)
Insociable answered 14/8, 2009 at 16:9 Comment(10)
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.Equiponderance
is tb just exc_tb? and os.path.split(blabla)[1] is os.path.basename(balbal)Condorcet
With this code, I get (when try: 1/0) : <type 'exceptions.ZeroDivisionError'> integer division or modulo by zero. How to have ZeroDivisionError: integer division or modulo by zero instead ?Briefs
@Basj: With sys.exc_info()[0].__name__ you get the plain name of the type.Armindaarming
@DanielPryden Python docs are also using the same unpacking method docs.python.org/2/library/traceback.html#traceback-examplesSphenoid
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?Okubo
@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.Calyptrogen
use logging.exception(e) if using logging libToscanini
Is sys.exc_info() thread safe?Piggish
@DanielPryden's comment no longer applies in Python 3, refer to #11415394Sirloin
S
338

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
Scopophilia answered 5/12, 2017 at 17:10 Comment(4)
while not exactly the format the op wanted, this is the simplest and most robust solutionBarbate
what is robust about it?Sheepwalk
It just more robust. High rate answer can't print the deepest stack in my program.Janiculum
you can also use traceback.print_exc(), without print(). save some typing :)Dissimilate
T
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
Thallic answered 16/4, 2013 at 20:55 Comment(2)
change 'message' : exc_value.message to 'message' : str(exc_value) for py3Latrell
What's unsafe about letting Python handle those variables like any other variables?Borate
I
52

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')
Irreformable answered 13/1, 2017 at 19:29 Comment(0)
H
48

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.

Hendershot answered 18/12, 2020 at 15:46 Comment(1)
I think possibly e.__traceback__.tb_frame.f_code.co_filename would do the trick for the filename.Egg
S
46

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
    }
  ]
}
Strander answered 5/10, 2020 at 16:24 Comment(0)
W
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

Wishywashy answered 29/10, 2021 at 17:6 Comment(1)
FYI, copying the text instead of making a pixely (jpeg-y) screenshot of text is more searchable and useful.Hendershot
K
1
try:
    4/0
except Exception as exc:
    print("error: ",exc)
    print("error file info: ",exc.__traceback__.tb_frame)
    print("error line#: ",exc.__traceback__.tb_lineno)
Kristofor answered 23/3 at 11:14 Comment(1)
Thank you for your interest in contributing to the Stack Overflow community. This question already has quite a few answers—including one that has been extensively validated by the community. Are you certain your approach hasn’t been given previously? If so, it would be useful to explain how your approach is different, under what circumstances your approach might be preferred, and/or why you think the previous answers aren’t sufficient. Can you kindly edit your answer to offer an explanation?Karee

© 2022 - 2024 — McMap. All rights reserved.