There is a new feature that was introduced in python3 - exception chaining. For some reasons I need to disable it for certain exceptions in my code.
Here is sample code:
try: print(10/0) except ZeroDivisionError as e: sys.exc_info() raise AssertionError(str(e))
what I see:
Traceback (most recent call last): File "draft.py", line 19, in main print(10/0) ZeroDivisionError: division by zero During handling of the above exception, another exception occurred: Traceback (most recent call last): File "draft.py", line 26, in <module> main() File "draft.py", line 22, in main raise AssertionError(str(e)) AssertionError: division by zero
what I want to see:
Traceback (most recent call last): File "draft.py", line 26, in <module> main() File "draft.py", line 22, in main raise AssertionError(str(e)) AssertionError: division by zero
I tried to use sys.exc_clear()
, but this method is removed from python 3 too. I can use workaround that works
exc = None try: print(10/0) except ZeroDivisionError as e: exc = e if exc: raise AssertionError(str(exc))
but I believe that there is better solution.
An implicit form of chained exceptions occurs when another exception gets raised inside an except block. In the code below, the NameError exception is raised as the result of a programming error, not in direct response to the parsing error.
OSError is a built-in exception in Python and serves as the error class for the os module, which is raised when an os specific system function returns a system-related error, including I/O failures such as “file not found” or “disk full”.
The BaseException is the base class of all other exceptions. User defined classes cannot be directly derived from this class, to derive user defied class, we need to use Exception class. The Python Exception Hierarchy is like below. BaseException.
When an exception is raised, the exception-propagation mechanism takes control. The normal control flow of the program stops, and Python looks for a suitable exception handler. Python's try statement establishes exception handlers via its except clauses.
try: print(10/0) except ZeroDivisionError as e: raise AssertionError(str(e)) from None
However, you probably actually want:
try: print(10/0) except ZeroDivisionError as e: raise AssertionError(str(e)) from e
__cause__
Implicit exception chaining happens through __context__
when there isn't an explicit cause exception set.
Explicit exception chaining works through __cause__
so if you set __cause__
to the exception itself, it should stop the chaining. If __cause__
is set, Python will suppress the implicit message.
try: print(10/0) except ZeroDivisionError as e: exc = AssertionError(str(e)) exc.__cause__ = exc raise exc
We can use "raise from" to do the same thing:
try: print(10/0) except ZeroDivisionError as e: exc = AssertionError(str(e)) raise exc from exc
__cause__
Setting __cause__
to None
actually does the same thing:
try: print(10/0) except ZeroDivisionError as e: exc = AssertionError(str(e)) exc.__cause__ = None raise exc
So that brings us to the most elegant way to do this which is to raise from None
:
try: print(10/0) except ZeroDivisionError as e: raise AssertionError(str(e)) from None
But I would argue that you usually want to explicitly raise your exception from the cause exception so the traceback is preserved:
try: print(10/0) except ZeroDivisionError as e: raise AssertionError(str(e)) from e
This will give us a slightly different message that states that the first exception was the direct cause of the second:
Traceback (most recent call last): File "<stdin>", line 2, in <module> ZeroDivisionError: division by zero The above exception was the direct cause of the following exception: Traceback (most recent call last): File "<stdin>", line 4, in <module> AssertionError: division by zero
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With