Reputation: 1233
Given the file errors.py
:
from traceback import format_tb, extract_tb
class MyError(Exception):
def __init__(self, message):
self.message = message
class MySecondError(Exception):
def __init__(self, message):
self.message = message
try:
try:
raise MyError("Something specific has happened")
except Exception as error:
raise MySecondError("Something general has happened") from error
except Exception as error:
print("".join(format_tb(error.__traceback__)))
When running python errors.py
the output is:
File "errors.py", line 15, in <module>
raise MySecondError("Something general has happened") from error
The main problem with this is that only the traceback of the 'highest' error in the 'chain' (MySecondError
) and there is no info on the wrapped error (MyError
)
If I remove the final try/except
wrapper so that the chained error is not caught, I get a much better output:
Traceback (most recent call last):
File "exceptions.py", line 14, in <module>
raise MyError("Something specific has happened")
__main__.MyError: Something specific has happened
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "exceptions.py", line 16, in <module>
raise MySecondError("Something general has happened") from error
__main__.MySecondError: Something general has happened
Which has the traceback of the full error chain and conjoining lines (Traceback (most recent call last):
, The above exception was the direct cause of the following exception:
) and string representation of each error
Ideally I would like to capture these lines of output to direct them elsewhere (e.g. a logger)
One solution I have is to iterate over error.__context__
and manually add the conjoining phrases:
except Exception as error:
inner_error = error
while inner_error:
if inner_error is not error:
print("\nThe above exception was the direct cause of the following exception:\n")
print("Traceback (most recent call last):")
print("".join(format_tb(inner_error.__traceback__) + [ str(error) ]))
inner_error = inner_error.__context__
Which works, but it is hacky and I would prefer to use some standard-library module which already handles this.
Upvotes: 0
Views: 668
Reputation: 101979
You want to use the format_exception
function:
from traceback import format_tb, format_exception
class MyError(Exception):
def __init__(self, message):
self.message = message
class MySecondError(Exception):
def __init__(self, message):
self.message = message
try:
try:
raise MyError("Something specific has happened")
except Exception as error:
raise MySecondError("Something general has happened") from error
except Exception as error:
print("".join(format_exception(error.__class__, error, error.__traceback__)))
Gives:
$ python3 /tmp/a.py
Traceback (most recent call last):
File "/tmp/a.py", line 13, in <module>
raise MyError("Something specific has happened")
MyError: Something specific has happened
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "/tmp/a.py", line 15, in <module>
raise MySecondError("Something general has happened") from error
MySecondError: Something general has happened
If you specify chain=False
this function will not print conjoining exceptions but only the last one.
Upvotes: 1