(起源不明の)Exceptionオブジェクトが与えられた場合、そのトレースバックを取得する方法はありますか?このようなコードがあります:
def stuff():
try:
.....
return useful
except Exception as e:
return e
result = stuff()
if isinstance(result, Exception):
result.traceback <-- How?
例外オブジェクトを取得したら、どのようにして例外オブジェクトからトレースバックを抽出できますか?
この質問に対する答えは、使用しているPythonのバージョンによって異なります。
簡単です。例外には、トレースバックを含む__traceback__
属性が装備されています。この属性も書き込み可能であり、例外のwith_traceback
メソッドを使用して簡単に設定できます。
raise Exception("foo occurred").with_traceback(tracebackobj)
これらの機能は、 raise
ドキュメントの一部として最小限説明されています。
回答のこの部分のすべての功績は、Vyctorに委ねるべきです。Vyctorは 最初にこの情報を投稿しました です。この答えが一番上に留まっており、Python 3がより一般的になっているため、ここに含めています。
煩わしいほど複雑です。トレースバックの問題は、スタックフレームへの参照があり、スタックフレームにスタックフレームへの参照があるトレースバックへの参照があることです ...への参照がある アイデアが得られます。これにより、ガベージコレクターで問題が発生します。 (最初にこれを指摘してくれた ecatmur に感謝します。)
これを解決する良い方法は、Python 3が行うexcept
句を残した後、外科的に サイクルを壊す にすることです。 Python 2ソリューションははるかにいです:アドホック関数、 sys.exc_info()
が提供されます。これは内部でのみ動作しますexcept
clause。例外、例外タイプ、および現在処理中の例外のトレースバックを含むTupleを返します。
したがって、except
句の内部にいる場合は、sys.exc_info()
の出力を traceback
モジュールとともに使用して、さまざまな便利なことを実行できます。
>>> import sys, traceback
>>> def raise_exception():
... try:
... raise Exception
... except Exception:
... ex_type, ex, tb = sys.exc_info()
... traceback.print_tb(tb)
... finally:
... del tb
...
>>> raise_exception()
File "<stdin>", line 3, in raise_exception
しかし、編集が示すように、例外が処理されていない場合、alreadyが処理された後、wouldが出力されたというトレースバックを取得しようとしています。それははるかに難しい質問です。残念ながら、例外が処理されていない場合、sys.exc_info
は(None, None, None)
を返します。他の関連sys
属性も役に立たない。 sys.exc_traceback
は非推奨であり、例外が処理されていない場合は未定義です。 sys.last_traceback
は完璧に思えますが、インタラクティブセッション中にのみ定義されるようです。
例外の発生方法を制御できる場合は、 inspect
および カスタム例外 を使用して、一部の情報を保存できます。しかし、それがどのように機能するかは完全にはわかりません。
実を言うと、例外をキャッチして返すことは、やるべきことの一種です。これは、とにかくリファクタリングする必要がある兆候かもしれません。
以来 Python 3.0[PEP 3109] 組み込みクラス Exception
には、__traceback__
(Python 3.2.3を含む)を含むtraceback object
属性があります。
>>> try:
... raise Exception()
... except Exception as e:
... tb = e.__traceback__
...
>>> tb
<traceback object at 0x00000000022A9208>
問題は、 グーグル__traceback__
の後しばらくの間、数件の記事しか見つかりませんでしたが、__traceback__
を使用する(しない)かどうか、またはその理由を説明していないことです。
ただし、 raise
のPython 3ドキュメント には次のように書かれています:
トレースバックオブジェクトは通常、例外が発生したときに自動的に作成され、書き込み可能な
__traceback__
属性としてオブジェクトにアタッチされます。
だから私はそれが使用されることを意図していると思います。
Python 3:の例外オブジェクトから文字列としてトレースバックを取得する方法
_import traceback
# `e` is an exception object that you get from somewhere
traceback_str = ''.join(traceback.format_tb(e.__traceback__))
_
traceback.format_tb(...)
は文字列のリストを返します。 ''.join(...)
はそれらを結合します。詳細については、以下をご覧ください。 https://docs.python.org/3/library/traceback.html#traceback.format_exc
余談ですが、実際に端末に出力されるのと同じように完全なトレースバックを取得したい場合は、次のようにします。
>>> try:
... print(1/0)
... except Exception as e:
... exc = e
...
>>> exc
ZeroDivisionError('division by zero')
>>> tb_str = traceback.format_exception(etype=type(exc), value=exc, tb=exc.__traceback__)
>>> tb_str
['Traceback (most recent call last):\n', ' File "<stdin>", line 2, in <module>\n', 'ZeroDivisionError: division by zero\n']
>>> print("".join(tb_str))
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
ZeroDivisionError: division by zero
上記の回答でformat_tb
を使用すると、情報が少なくなります。
>>> tb_str = "".join(traceback.format_tb(exc.__traceback__))
>>> print("".join(tb_str))
File "<stdin>", line 2, in <module>
トレースバックが例外に保存されない非常に良い理由があります。トレースバックはそのスタックのローカルへの参照を保持するため、循環GCが起動するまで循環参照と(一時的な)メモリリークが発生します(これが、トレースバックを保存しない ローカル変数 。)
私が考えることができる唯一のものは、stuff
のグローバルをmonkeypatchし、Exception
をキャッチしていると思うと、実際に特殊な型をキャッチし、例外が呼び出し元としてあなたに伝播することです:
module_containing_stuff.Exception = type("BogusException", (Exception,), {})
try:
stuff()
except Exception:
import sys
print sys.exc_info()