如何在嵌套的try / except块中重新引发异常?


106

我知道如果我想重新引发异常,我会raise在相应的except块中简单地使用不带参数的形式。但是给定一个嵌套的表达式

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。或者我该如何重构我的代码来避免此问题?


2
您是否尝试plan_B过添加另一个True在成功和False异常情况下返回的函数?那么外面的except障碍可能就是if not try_plan_B(): raise
Drew McGowen

@DrewMcGowen不幸的是,更现实的情况是该函数位于接受任意对象的函数内部,arg并且我将尝试调用arg.plan_B()该函数,这可能是AttributeError由于arg未提供计划B 而引起的
Tobias Kienzler 2013年


@Paco谢谢,我会(尽管答案已经显示出一种简单的方法)
Tobias Kienzler 2013年

@DrewMcGowen我根据您的评论写了一个答案,但是看起来比user4815162342的答案pythony。但这是由于我希望也具有返回值并允许plan_B引发异常
Tobias Kienzler 2013年

Answers:


127

从Python 3开始,回溯存储在异常中,因此raise e(大多数)正确的事情很简单:

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

产生的追溯将包括SomeError在处理过程中发生的其他通知AlsoFailsError(由于位于raise e内部except AlsoFailsError)。这具有误导性,因为实际发生的是相反的情况-我们AlsoFailsError在尝试从恢复时遇到并处理了它SomeError。要获取不包含的回溯AlsoFailsError,请替换raise eraise 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

太好了,这就是我也在这里找到的,谢谢!虽然有建议是raise self.exc_info[1], None, self.exc_info[2]self.exc_info = sys.exc_info()-把[1]到第一位置,出于某种原因
托比亚斯Kienzler

3
@TobiasKienzler raise t, None, tb将丢失该异常的值,并将强制raise从类型重新实例化该异常,从而为您提供了一个不太具体(或根本不正确)的异常值。例如,如果引发的异常是KeyError("some-key"),它将重新引发KeyError()并忽略回溯中确切丢失的键。
user4815162342

3
@TobiasKienzler仍然应该可以在Python 3中将其表示为raise v.with_traceback(tb)。(您的评论甚至说了很多,除了它提议重新实例化该值。)
user4815162342 2013年

2
另外,不要sys.exc_info()在局部变量中存储红色警告在Python 2.0(13年前发布)之前是有意义的,但在今天荒谬可笑。如果没有周期收集器,现代Python将几乎毫无用处,因为每个不重要的Python库都会创建没有暂停的周期,并取决于它们的正确清理。
user4815162342

1
@ user4815162342您可以通过编写“从None提高e”来消除“发生另一个错误”嵌套错误。
Matthias Urlichs'Apr 26'17

19

即使接受的解决方案正确,也最好使用指向具有Python 2 + 3解决方案的Sixsix.reraise

六。重新提高exc_typeexc_valueexc_traceback = None)

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

因此,您可以编写:

import six


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

1
好点-六发言你也可以使用six.raise_from,如果你想包含的信息plan_B()也失败了。
Tobias Kienzler

1
@TobiasKienzler:我认为这是一种不同的用法:six.raise_from创建与上一个异常相关联的新异常时,您无需重新引发,因此回溯是不同的。
洛朗·拉波特

1
我的观点完全相同-如果reraise你得到的印象只有something()SomeError,如果raise_from你也知道,这引起了plan_B()要执行,但扔AlsoFailsError。因此,这取决于用例。我认为raise_from这将使调试更加容易
Tobias Kienzler

9

根据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

1
这种方法的好处是,它在Python 2和3的工作不变
user4815162342

2
@ user4815162342的要点:)尽管同时考虑了Python3 raise from,所以堆栈跟踪也使我认为计划B失败了。这可以在Python 2中模拟的方式。
Tobias Kienzler

5

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

2
问题是有关期间发生的另一个异常except。但是您是对的,当我替换err = e为时,raise AttributeError您首先得到SyntaxError堆栈跟踪,然后是a During handling of the above exception, another exception occurred:AttributeError堆栈跟踪。很高兴知道,尽管不幸的是,人们不能依赖于3.5+的安装。PS:ff verstehen nicht-Deutsche vermutlich nicht;)
Tobias Kienzler

好的,因此我更改了示例以引发另一个异常,当我重新引发第一个异常时,该异常(作为原始问题的要求)被忽略。
Matthias Urlichs '17

3
@TobiasKienzler 3.5+(我将其更改为)似乎是一种全球认可的格式。是denkst du吗?;)
linusg

@linusg Agreed :)
Tobias Kienzler
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.