web-dev-qa-db-ja.com

すぐに再発生しない場合、例外トレースバックは非表示になります

次のようなコードがあります。

import sys

def func1():
    func2()

def func2():
    raise Exception('test error')

def main():
    err = None

    try:
        func1()
    except:
        err = sys.exc_info()[1]
        pass

    # some extra processing, involving checking err details (if err is not None)

    # need to re-raise err so caller can do its own handling
    if err:
        raise err

if __== '__main__':
    main()

いつ func2は、次のトレースバックを受け取る例外を発生させます。

Traceback (most recent call last):
  File "err_test.py", line 25, in <module>
    main()
  File "err_test.py", line 22, in main
    raise err
Exception: test error

ここから、例外がどこから来ているのかわかりません。元のトレースバックは失われます。

元のトレースバックを保存して再レイズするにはどうすればよいですか?私はこれに似たものを見たいです:

Traceback (most recent call last):
  File "err_test.py", line 26, in <module>
    main()
  File "err_test.py", line 13, in main
    func1()
  File "err_test.py", line 4, in func1
    func2()
  File "err_test.py", line 7, in func2
    raise Exception('test error')
Exception: test error
64
parxier

空白のraiseは、最後の例外を発生させます。

# need to re-raise err so caller can do its own handling
if err:
    raise

raise something Pythonを使用すると、somethingが直前にキャッチされた例外であるか、新しいスタックトレースを持つ新しい例外であるかを知る方法がありません。スタックトレースを保持する空白のraiseです。

ここを参照

108
Jochen Ritzel

変更および再スロー 例外が可能です:

式が存在しない場合、raiseは現在のスコープでアクティブだった最後の例外を再発生させます。現在のスコープでアクティブな例外がない場合、これがエラーであることを示すTypeError例外が発生します(IDLEで実行している場合は、Queue.Empty例外が代わりに発生します)。

それ以外の場合、raiseは、省略された式の値としてNoneを使用して、3つのオブジェクトを取得する式を評価します。最初の2つのオブジェクトは、例外のタイプと値を決定するために使用されます。

Noneではなく3番目のオブジェクトが存在する場合、それはトレースバックオブジェクトである必要があり(セクション標準型階層を参照)、例外が発生した場所として現在の場所の代わりに置き換えられます。 3番目のオブジェクトが存在し、トレースバックオブジェクトまたはNoneではない場合、TypeError例外が発生します。

raiseの3つの式の形式は、except句で例外を透過的に再発生させるのに役立ちますが、例外を再実行する場合は、式のないraiseを優先する必要があります。 -raisedは、現在のスコープで最後にアクティブになった例外です。

したがって、例外を変更して再スローする場合は、次の操作を実行できます。

try:
    buggy_code_which_throws_exception()
except Exception as e:
    raise Exception, "The code is buggy: %s" % e, sys.exc_info()[2]
62
qris

traceback モジュールとともにsys.exc_info()を介して、例外に関する多くの情報を取得できます。

コードに次の拡張機能を試してください。

import sys
import traceback

def func1():
    func2()

def func2():
    raise Exception('test error')

def main():

    try:
        func1()
    except:
        exc_type, exc_value, exc_traceback = sys.exc_info()
        # Do your verification using exc_value and exc_traceback

        print "*** print_exception:"
        traceback.print_exception(exc_type, exc_value, exc_traceback,
                                  limit=3, file=sys.stdout)

if __== '__main__':
    main()

これは、あなたが望むものと同様に印刷されます。

*** print_exception:
Traceback (most recent call last):
  File "err_test.py", line 14, in main
    func1()
  File "err_test.py", line 5, in func1
    func2()
  File "err_test.py", line 8, in func2
    raise Exception('test error')
Exception: test error
5
Senthil Kumaran

@Jochenの答えは単純な場合にはうまく機能しますが、より複雑なケースを処理することはできません。新しいコンテキスト(つまり、別のプロセスで処理する必要がある場合)。

この場合、以下を提案します。

  1. 元のexc_infoを取得します
  2. スタックトレースを使用して、元のエラーメッセージをフォーマットする
  3. その完全なエラーメッセージ(スタックトレースを含む)が埋め込まれた新しい例外をスローする

これを行う前に、後で再スローする新しい例外タイプを定義してください...

class ChildTaskException(Exception):
    pass

問題のあるコードでは...

import sys
import traceback

try:
    # do something dangerous
except:
    error_type, error, tb = sys.exc_info()
    error_lines = traceback.format_exception(error_type, error, tb)
    error_msg = ''.join(error_lines)
    # for example, if you are doing multiprocessing, you might want to send this to another process via a pipe
    connection.send(error_msg)

再スロー...

# again, a multiprocessing example of receiving that message through a pipe
error_msg = pcon.recv()
raise ChildTaskException(error_msg)
4
tvt173

メイン関数は次のようにする必要があります。

def main():
    try:
        func1()
    except Exception, err:
        # error processing
        raise

これは、エラーを処理(および再発生)する標準的な方法です。 これはコードパッドのデモです。

2
Gabi Purcaru