Search code examples
pythonpython-3.xerror-handlingtry-excepttraceback

Is there a way to access the "another exceptions" that happen in a python3 traceback?


Let's assume you have some simple code that you don't control (eg: it's in a module you're using):

def example():
    try:
        raise TypeError("type")
    except TypeError:
        raise Exception("device busy")

How would I go about accessing the TypeError in this traceback in order to handle it?

Traceback (most recent call last):
  File "/usr/local/google/home/dthor/dev/pyle/pyle/fab/visa_instrument/exception_helpers.py", line 3, in example
    raise TypeError("type")
TypeError: type

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/local/google/home/dthor/dev/pyle/pyle/fab/visa_instrument/exception_helpers.py", line 7, in <module>
    example()
  File "/usr/local/google/home/dthor/dev/pyle/pyle/fab/visa_instrument/exception_helpers.py", line 5, in example
    raise Exception("device busy")
Exception: device busy

I can do the below, but i'm not happy with it because I'm doing string comparison - meaning things would break if the underlying module changes what string they raise (I don't control example()):

try:
    example()
except Exception as err:
    if "device busy" in str(err):
        # do the thing
        pass
    # But raise any other exceptions
    raise err

I'd much rather have:

try:
    example()
except Exception as err:
    if TypeError in err.other_errors:  # magic that doesn't exist
        # do the thing
        pass
    raise err

or even

try:
    example()
except TypeError in exception_stack:  # Magic that doesn't exist
    # do the thing
    pass
except Exception:

I'm investigating the traceback module and sys.exc_info(), but don't have anything concrete yet.

Followup: would things be different if the exception was chained? Eg: raise Exception from the_TypeError_exception


Solution

  • Check the __context__ of the exception:

    >>> try:
    ...     example()
    ... except Exception as e:
    ...     print(f"exception: {e!r}")
    ...     print(f"context: {e.__context__!r}")
    ...
    exception: Exception('device busy')
    context: TypeError('type')
    

    If you use a chained exception, the original exception will also be accessible via the __cause__ attribute.