简体   繁体   中英

How do you raise an exception inside an except block without exception chaining in a way that's compatible with both Python 2 and 3?

In Python 2.7, the following will only print one traceback, from the exception raised in the except block:

try:
   1/0
except ZeroDivisionError:
   raise Exception("Error!")

Output:

Traceback (most recent call last):
  File "<stdin>", line 4, in <module>
Exception: Error!

However in Python 3 there is exception chaining. Here is the output for the same code:

Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
ZeroDivisionError: division by zero

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "<stdin>", line 4, in <module>
Exception: Error!

I need to write some code that is compatible with both Python 2.7 and Python 3, and I need for there to be no exception chaining when the exception is raised inside that except block. So that the output will be what you see above when that code is executed in Python 2.7.

The one possible solution I've found is raise_from , included in the six module:

from six import raise_from
try:
   1/0
except ZeroDivisionError:
   raise_from(Exception("Error!"), None)

However, it adds an extra line for raise_from to the traceback that I'd like to avoid:

Traceback (most recent call last):
  File "<stdin>", line 4, in <module>
  File "<string>", line 3, in raise_from
Exception: Error!

Is there any way to do this?

The following produces identical output on py2.7.13 and py3.9.2, if you call it with the full path. (If you use a relative path, py3 expands it, but py2 does not.)

try:
    1/0
except ZeroDivisionError:
    internal = Exception("Error!")
    internal.__suppress_context__ = True
    raise internal

Traceback (most recent call last):
File "b:\development\python\so.py", line 6, in
raise internal
Exception: Error!

I was hoping to avoid internal attributes that might change, but __suppress_context__ is documented https://docs.python.org/3/library/exceptions.html

Use a finally block to discard the context after the exception was raised:

try:
   1/0
except ZeroDivisionError:
   # store exception to allow modifying it
   exc = Exception("Error!")
   try:
       raise exc
   finally:
       # context has been set here already – discard it
       exc.__context__ = None

This also works in Python2 for any proper exception, ie those derived from BaseException .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM