如何重新引发一个异常在嵌套的尝试/除了块?

我知道,如果我想重新引发异常,我只需在相应的 except块中使用没有参数的 raise。但是如果给出一个嵌套的表达式

try:
something()
except SomeError as e:
try:
plan_B()
except AlsoFailsError:
raise e  # I'd like to raise the SomeError as if plan_B()
# didn't raise the AlsoFailsError

如何在不破坏堆栈跟踪的情况下重新引发 SomeError?在这种情况下,单独的 raise将重新提高更近的 AlsoFailsError。或者我如何重构我的代码以避免这个问题?

167991 次浏览

在 Python 3中,回溯被存储在异常中,所以一个简单的 raise e(大多数情况下)会做正确的事情:

try:
something()
except SomeError as e:
try:
plan_B()
except AlsoFailsError:
raise e  # or raise e from None - see below

产生的回溯将包括在处理 AlsoFailsError时发生 SomeError的额外通知(因为 raise e位于 except AlsoFailsError内部)。这是一种误导,因为实际发生的情况恰恰相反——我们在尝试从 SomeError恢复时遇到了 AlsoFailsError并处理了它。要获得不包括 AlsoFailsError的回溯,请将 raise e替换为 raise e from None


在 Python 2中,将异常类型、值和回溯存储在局部变量中,并使用 raise的三参数形式:

try:
something()
except SomeError:
t, v, tb = sys.exc_info()
try:
plan_B()
except AlsoFailsError:
raise t, v, tb

根据 Drew McGowen 的建议,但考虑到一般情况(其中存在返回值 s) ,这里有一个 User4815162342的答案的替代方案:

try:
s = something()
except SomeError as e:
def wrapped_plan_B():
try:
return False, plan_B()
except:
return True, None
failed, s = wrapped_plan_B()
if failed:
raise

Python 3.5 + 将回溯信息附加到错误,因此不再需要单独保存它。

>>> def f():
...   try:
...     raise SyntaxError
...   except Exception as e:
...     err = e
...     try:
...       raise AttributeError
...     except Exception as e1:
...       raise err from None
>>> f()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 9, in f
File "<stdin>", line 3, in f
SyntaxError: None
>>>

即使 接受的解决方案是正确的,最好指向使用 six.reraise的具有 Python2 + 3解决方案的 六个库。

6. 重新起立(Exc _ typeExc _ value追踪 = 无)

重新引发异常,可能使用不同的回溯。 [...]

所以,你可以写:

import six




try:
something()
except SomeError:
t, v, tb = sys.exc_info()
try:
plan_B()
except AlsoFailsError:
six.reraise(t, v, tb)