例外オブジェクトからトレースバック情報を抽出する


111

(不明な起​​源の)Exceptionオブジェクトが与えられた場合、そのトレースバックを取得する方法はありますか?私はこのようなコードを持っています:

def stuff():
   try:
       .....
       return useful
   except Exception as e:
       return e

result = stuff()
if isinstance(result, Exception):
    result.traceback <-- How?

トレースバックを取得したら、Exceptionオブジェクトからどのように抽出できますか?

回答:


92

この質問に対する答えは、使用しているPythonのバージョンによって異なります。

Python 3では

それは簡単です。例外に__traceback__は、トレースバックを含む属性が備わっています。この属性も書き込み可能でありwith_traceback、例外メソッドを使用して簡単に設定できます。

raise Exception("foo occurred").with_traceback(tracebackobj)

これらの機能は、raiseドキュメントの一部として最小限に説明されています。

回答のこの部分のすべてのクレジットは、最初にこの情報を投稿したVyctorに送るべきです。ここに含めているのは、この答えが一番上にあり、Python 3がより一般的になっているためです。

Python 2では

厄介なほど複雑です。トレースバックの問題は、それらにスタックフレームへの参照があり、スタックフレームが、参照を持つスタックフレームへの参照を持つトレースバックへの参照を持っていることです。これにより、ガベージコレクタに問題が発生します。(これを最初に指摘してくれた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カスタム例外を使用して一部の情報を格納できる場合があります。しかし、それがどのように機能するかは完全にはわかりません。

実を言うと、例外をキャッチして返すのは珍しいことです。これは、とにかくリファクタリングする必要がある兆候かもしれません。


例外を返すことはどういうわけか型破りであることに同意しますが、これの背後にある理論的根拠については私の別の質問を参照しください。
georg

@ thg435、そう、これはその時より意味があります。私の他の質問で提案sys.exc_infoするコールバックアプローチと組み合わせて使用する私の上記のソリューションを検討してください。
sendle

トレースバックオブジェクトの詳細(ほとんどありません):docs.python.org/3/library/types.html#types.TracebackType docs.python.org/3/reference/datamodel.html#traceback-objects
0xfede7c8

69

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__書き込み可能な属性としてそれにアタッチされると、自動的に作成されます。

だから私はそれが使用されることを意図していると思います。


4
はい、使用するためのものです。以下からの新機能ではPython 3.0 「PEP 3134:例外は今のように自分のトレースバックを保存するオブジェクトのトレースバック属性この手段を例外オブジェクトは、現在の例外に関連するすべての情報が含まれており、使用sys.exc_infoに少ない理由は()(があること。後者は削除されませんが)。」
Maciej Szpakowski 2015

この答えがなぜためらいがちで曖昧なのか、私にはよくわかりません。これは文書化されたプロパティです。なぜ考えていない「で使用されることを意味する」こと?
Mark Amery 2017

2
@MarkAmeryおそらく、__名前に含まれているのは、パブリックプロパティではなく、実装の詳細であることを示していますか?
基本的な

4
@Basicは、ここでは示していません。通常、Pythonでは__fooプライベートメソッドですが__foo__(末尾のアンダースコアも)、「マジック」メソッドです(プライベートではありません)。
Mark Amery 2017

1
参考までに、この__traceback__属性は、GCに影響を与えることなく、好きな方法で100%安全に使用できます。ドキュメントからそれを伝えるのは難しいですが、ecatmurは確固とした証拠を見つけました。
sendle


21

余談ですが、実際にターミナルに出力されるように完全なトレースバックを取得したい場合は、次のようにします。

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

4
最後に!これが一番の答えになるはずです。ダニエル、ありがとう!
Dany

3
ああ、私はこれを見つける前にこれを理解しようと最後の20分間を費やしていました:-) etype=type(exc)今は省略できます:「バージョン3.5で変更:etype引数は無視され、値のタイプから推測されます。」docs.python.org/3.7/library/…Python 3.7.3でテスト済み。
Ciro Santilli郝海东冠状病六四事件法轮功

8

トレースバックが例外に保存されない非常に正当な理由があります。トレースバックはスタックのローカルへの参照を保持しているため、循環GCが起動するまで循環参照と(一時的な)メモリリークが発生します(これが、トレースバックをローカル変数に格納してはならない理由です)。

私が考えることができる唯一のことは、あなたがmonkeypatch stuffのグローバルをキャッチしExceptionて、それがキャッチしていると思ったときに実際に特殊なタイプをキャッチし、例外が呼び出し元としてあなたに伝播することです:

module_containing_stuff.Exception = type("BogusException", (Exception,), {})
try:
    stuff()
except Exception:
    import sys
    print sys.exc_info()

7
これは間違っています。Python 3は、トレースバックオブジェクトを例外として配置しe.__traceback__ます。
Glenn Maynard

6
@GlennMaynard Python 3は、PEP 3110exceptに従って、ブロックの終了時に例外ターゲットを削除することで問題を解決します。
ecatmur 2012
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.