回答:
この質問に対する答えは、使用しているPythonのバージョンによって異なります。
それは簡単です。例外に__traceback__
は、トレースバックを含む属性が備わっています。この属性も書き込み可能でありwith_traceback
、例外メソッドを使用して簡単に設定できます。
raise Exception("foo occurred").with_traceback(tracebackobj)
これらの機能は、raise
ドキュメントの一部として最小限に説明されています。
回答のこの部分のすべてのクレジットは、最初にこの情報を投稿したVyctorに送るべきです。ここに含めているのは、この答えが一番上にあり、Python 3がより一般的になっているためです。
厄介なほど複雑です。トレースバックの問題は、それらにスタックフレームへの参照があり、スタックフレームが、参照を持つスタックフレームへの参照を持つトレースバックへの参照を持っていることです。これにより、ガベージコレクタに問題が発生します。(これを最初に指摘してくれたecatmurに感謝します。)
これを解決する良い方法は、条項を出た後に外科的にサイクルを断ち切るexcept
ことです。これはPython 3が行うことです。Python 2ソリューションは非常に醜く、節の中sys.exc_info()
でのみ 機能するアドホック関数が提供されます。例外、例外タイプ、および現在処理されている例外のトレースバックを含むタプルを返します。except
したがって、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
しかし、編集が示すように、例外がすでに処理された後で、例外が処理されなかった場合に出力されるはずのトレースバックを取得しようとしています。それはもっと難しい質問です。残念ながら、例外が処理されていないときにsys.exc_info
戻ります(None, None, None)
。他の関連するsys
属性も役に立ちません。sys.exc_traceback
例外が処理されていない場合は非推奨であり、未定義です。sys.last_traceback
完璧だと思われますが、インタラクティブなセッション中にのみ定義されるようです。
例外の発生方法を制御できる場合はinspect
、カスタム例外を使用して一部の情報を格納できる場合があります。しかし、それがどのように機能するかは完全にはわかりません。
実を言うと、例外をキャッチして返すのは珍しいことです。これは、とにかくリファクタリングする必要がある兆候かもしれません。
sys.exc_info
するコールバックアプローチと組み合わせて使用する私の上記のソリューションを検討してください。
Python 3.0 [PEP 3109]以降、組み込みクラスにException
は(Python 3.2.3で)以下__traceback__
を含む属性がありますtraceback object
。
>>> try:
... raise Exception()
... except Exception as e:
... tb = e.__traceback__
...
>>> tb
<traceback object at 0x00000000022A9208>
問題は、しばらくの間グーグル検索__traceback__
を行った後、私が見つけた記事は少ないのですが、どれを使用すべきか(使用しないべきか)を説明する記事がどれもないということです__traceback__
。
ただし、Python 3のドキュメントにraise
は次のように記載されています。
トレースバックオブジェクトは通常、例外が発生し、
__traceback__
書き込み可能な属性としてそれにアタッチされると、自動的に作成されます。
だから私はそれが使用されることを意図していると思います。
__
名前に含まれているのは、パブリックプロパティではなく、実装の詳細であることを示していますか?
__foo
プライベートメソッドですが__foo__
(末尾のアンダースコアも)、「マジック」メソッドです(プライベートではありません)。
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_tbをご覧ください。
余談ですが、実際にターミナルに出力されるように完全なトレースバックを取得したい場合は、次のようにします。
>>> 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>
etype=type(exc)
今は省略できます:「バージョン3.5で変更:etype引数は無視され、値のタイプから推測されます。」docs.python.org/3.7/library/…Python 3.7.3でテスト済み。
トレースバックが例外に保存されない非常に正当な理由があります。トレースバックはスタックのローカルへの参照を保持しているため、循環GCが起動するまで循環参照と(一時的な)メモリリークが発生します(これが、トレースバックをローカル変数に格納してはならない理由です)。
私が考えることができる唯一のことは、あなたがmonkeypatch stuff
のグローバルをキャッチしException
て、それがキャッチしていると思ったときに実際に特殊なタイプをキャッチし、例外が呼び出し元としてあなたに伝播することです:
module_containing_stuff.Exception = type("BogusException", (Exception,), {})
try:
stuff()
except Exception:
import sys
print sys.exc_info()
e.__traceback__
ます。