Disable exception chaining in python 3

五迷三道 提交于 2019-12-18 19:08:10

问题


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 mehtod 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.


回答1:


Simple Answer

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

Explanation

__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

raise from

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

None __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

raise from None

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


来源:https://stackoverflow.com/questions/33809864/disable-exception-chaining-in-python-3

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!