ネストされた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戻る別の関数を入れてみましたか?その後、外側のブロックはちょうど可能性がありますTrueFalseexceptif not try_plan_B(): raise
Drew McGowen 2013

@DrewMcGowen残念ながら、より現実的なケースは、これが任意のオブジェクトargを受け入れる関数の内部にあるということです。プランを提供していarg.plan_B()ないために発生する可能性のある呼び出しを試みますBAttributeErrorarg
Tobias Kienzler 2013

トレースバックモジュールをご覧ください
Paco

@Pacoおかげで、私は(答えはすでにより簡単な方法を示していますが)します
Tobias Kienzler 2013

@DrewMcGowen私はあなたのコメント基づいて回答を書きましたがuser4815162342の回答よりもPython的に見えません。しかし、それは、戻り値も必要とし、plan_B例外を発生させることを許可したいためです
Tobias Kienzler

回答:


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処理中に発生した追加の通知が含まれますAlsoFailsErrorraise e内部にあるためexcept AlsoFailsError)。実際に何が起こったのは周りの他の方法であるため、これは誤解を招くです-私たちは遭遇しAlsoFailsError、そしてから回復しようとしているときに、それを取り扱いますSomeError。含まれていませんトレースバックを得るためにはAlsoFailsError、交換してくださいraise eraise e from None

Python 2では、例外のタイプ、値、トレースバックをローカル変数に格納し、次の3つの引数形式を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 2013

3
@TobiasKienzler Python 3では、それをとして表現することも可能raise v.with_traceback(tb)です。(あなたのコメントは、それが値を再インスタンス化することを提案していることを除いて、多くのことを言っています。)
user4815162342

2
また、sys.exc_info()ローカル変数に保存しないという赤い警告は、Python 2.0(13年前にリリースされた)よりも前に理にかなっていますが、今日はばかげています。すべての重要なPythonライブラリは一時停止なしでサイクルを作成し、正しいクリーンアップに依存しているため、現代のPythonはサイクルコレクターがないとほとんど役に立ちません。
user4815162342 2013

1
@ user4815162342「raise e from None」と書くことで、「別のエラーが発生しました」というネストされたエラーを強制終了できます。
Matthias Urlichs

19

承認されたソリューションが正しい場合でも、Python 2 + 3ソリューションが含まれているSixライブラリをに指定することをお勧めしsix.reraiseます。

6。reraiseexc_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と言えば、失敗したsix.raise_from情報を含めたい場合にも使用できますplan_B()
Tobias Kienzler 2017

1
@TobiasKienzler:別の使い方だと思います:six.raise_from以前の例外にリンクされた新しい例外を作成すると、再発生せず、トレースバックが異なります。
Laurent LAPORTE 2017

1
私の要点は、あなたreraisesomething()投げただけの印象をあなたが得たSomeError場合raise_from、これplan_B()が実行されたがスローしたことも知っていた場合AlsoFailsError。したがって、ユースケースによって異なります。raise_fromデバッグが容易になると思います
Tobias Kienzler、2017

9

あたりとしてドリュー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 2017

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;)
トビアスキエンツラー

わかりました。それで、例を変更して別の例外を発生させました。最初の問題を再度発生させると、(元の質問で要求されたように)例外が無視されます。
Matthias Urlichs 2017

3
@TobiasKienzler 3.5+(変更後)は、世界的に認められているフォーマットのようです。denkst duでしたか?;)
linusg

@linusg同意:)
トビアスキエンツラー
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.