Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

"Inner exception" (with traceback) in Python?

People also ask

How do I fix Python traceback error?

If there is no variable defined with that name you will get a NameError exception. To fix the problem, in Python 2, you can use raw_input() . This returns the string entered by the user and does not attempt to evaluate it. Note that if you were using Python 3, input() behaves the same as raw_input() does in Python 2.

How do I print traceback of exception in Python?

Method 2: By using print_exception() method. This method prints exception information and stack trace entries from traceback object tb to file. Parameters: This method accepts the following parameters: if tb argument is not None, it prints a header Traceback (most recent call last):

Is traceback an error in Python?

In Python, A traceback is a report containing the function calls made in your code at a specific point i.e when you get an error it is recommended that you should trace it backward(traceback). Whenever the code gets an exception, the traceback will give the information about what went wrong in the code.

Can you have two excepts in Python?

By handling multiple exceptions, a program can respond to different exceptions without terminating it. In Python, try-except blocks can be used to catch and respond to one or multiple exceptions. In cases where a process raises more than one possible exception, they can all be handled using a single except clause.


Python 3

In python 3 you can do the following:

try:
    raise MyExceptionToBeWrapped("I have twisted my ankle")

except MyExceptionToBeWrapped as e:

    raise MyWrapperException("I'm not in a good shape") from e

This will produce something like this:

   Traceback (most recent call last):
   ...
   MyExceptionToBeWrapped: ("I have twisted my ankle")

The above exception was the direct cause of the following exception:

   Traceback (most recent call last):
   ...
   MyWrapperException: ("I'm not in a good shape")

Python 2

It's simple; pass the traceback as the third argument to raise.

import sys
class MyException(Exception): pass

try:
    raise TypeError("test")
except TypeError, e:
    raise MyException(), None, sys.exc_info()[2]

Always do this when catching one exception and re-raising another.


Python 3 has the raise ... from clause to chain exceptions. Glenn's answer is great for Python 2.7, but it only uses the original exception's traceback and throws away the error message and other details. Here are some examples in Python 2.7 that add context information from the current scope into the original exception's error message, but keep other details intact.

Known Exception Type

try:
    sock_common = xmlrpclib.ServerProxy(rpc_url+'/common')
    self.user_id = sock_common.login(self.dbname, username, self.pwd)
except IOError:
    _, ex, traceback = sys.exc_info()
    message = "Connecting to '%s': %s." % (config['connection'],
                                           ex.strerror)
    raise IOError, (ex.errno, message), traceback

That flavour of raise statement takes the exception type as the first expression, the exception class constructor arguments in a tuple as the second expression, and the traceback as the third expression. If you're running earlier than Python 2.2, see the warnings on sys.exc_info().

Any Exception Type

Here's another example that's more general purpose if you don't know what kind of exceptions your code might have to catch. The downside is that it loses the exception type and just raises a RuntimeError. You have to import the traceback module.

except Exception:
    extype, ex, tb = sys.exc_info()
    formatted = traceback.format_exception_only(extype, ex)[-1]
    message = "Importing row %d, %s" % (rownum, formatted)
    raise RuntimeError, message, tb

Modify the Message

Here's another option if the exception type will let you add context to it. You can modify the exception's message and then reraise it.

import subprocess

try:
    final_args = ['lsx', '/home']
    s = subprocess.check_output(final_args)
except OSError as ex:
    ex.strerror += ' for command {}'.format(final_args)
    raise

That generates the following stack trace:

Traceback (most recent call last):
  File "/mnt/data/don/workspace/scratch/scratch.py", line 5, in <module>
    s = subprocess.check_output(final_args)
  File "/usr/lib/python2.7/subprocess.py", line 566, in check_output
    process = Popen(stdout=PIPE, *popenargs, **kwargs)
  File "/usr/lib/python2.7/subprocess.py", line 710, in __init__
    errread, errwrite)
  File "/usr/lib/python2.7/subprocess.py", line 1327, in _execute_child
    raise child_exception
OSError: [Errno 2] No such file or directory for command ['lsx', '/home']

You can see that it shows the line where check_output() was called, but the exception message now includes the command line.


In Python 3.x:

raise Exception('Failed to process file ' + filePath).with_traceback(e.__traceback__)

or simply

except Exception:
    raise MyException()

which will propagate MyException but print both exceptions if it will not be handled.

In Python 2.x:

raise Exception, 'Failed to process file ' + filePath, e

You can prevent printing both exceptions by killing the __context__ attribute. Here I write a context manager using that to catch and change your exception on the fly: (see http://docs.python.org/3.1/library/stdtypes.html for expanation of how they work)

try: # Wrap the whole program into the block that will kill __context__.

    class Catcher(Exception):
        '''This context manager reraises an exception under a different name.'''

        def __init__(self, name):
            super().__init__('Failed to process code in {!r}'.format(name))

        def __enter__(self):
            return self

        def __exit__(self, exc_type, exc_val, exc_tb):
            if exc_type is not None:
                self.__traceback__ = exc_tb
                raise self

    ...


    with Catcher('class definition'):
        class a:
            def spam(self):
                # not really pass, but you get the idea
                pass

            lut = [1,
                   3,
                   17,
                   [12,34],
                   5,
                   _spam]


        assert a().lut[-1] == a.spam

    ...


except Catcher as e:
    e.__context__ = None
    raise

I don't think you can do this in Python 2.x, but something similar to this functionality is part of Python 3. From PEP 3134:

In today's Python implementation, exceptions are composed of three parts: the type, the value, and the traceback. The 'sys' module, exposes the current exception in three parallel variables, exc_type, exc_value, and exc_traceback, the sys.exc_info() function returns a tuple of these three parts, and the 'raise' statement has a three-argument form accepting these three parts. Manipulating exceptions often requires passing these three things in parallel, which can be tedious and error-prone. Additionally, the 'except' statement can only provide access to the value, not the traceback. Adding the 'traceback' attribute to exception values makes all the exception information accessible from a single place.

Comparison to C#:

Exceptions in C# contain a read-only 'InnerException' property that may point to another exception. Its documentation [10] says that "When an exception X is thrown as a direct result of a previous exception Y, the InnerException property of X should contain a reference to Y." This property is not set by the VM automatically; rather, all exception constructors take an optional 'innerException' argument to set it explicitly. The 'cause' attribute fulfills the same purpose as InnerException, but this PEP proposes a new form of 'raise' rather than extending the constructors of all exceptions. C# also provides a GetBaseException method that jumps directly to the end of the InnerException chain; this PEP proposes no analog.

Note also that Java, Ruby and Perl 5 don't support this type of thing either. Quoting again:

As for other languages, Java and Ruby both discard the original exception when another exception occurs in a 'catch'/'rescue' or 'finally'/'ensure' clause. Perl 5 lacks built-in structured exception handling. For Perl 6, RFC number 88 [9] proposes an exception mechanism that implicitly retains chained exceptions in an array named @@.


For maximum compatibility between Python 2 and 3, you can use raise_from in the six library. https://six.readthedocs.io/#six.raise_from . Here is your example (slightly modified for clarity):

import six

try:
  ProcessFile(filePath)
except Exception as e:
  six.raise_from(IOError('Failed to process file ' + repr(filePath)), e)