ああ!わかりました、これは興味深い質問です。
これは同じ近似関数ですが、から直接例外を取得しますsys.exc_info()
:
import sys
import traceback
def save_if_allowed(fn):
def wrapped(*args, **kwargs):
try:
return fn(*args, **kwargs) if enabled else None
except Exception:
print "The exception:"
print "".join(traceback.format_exception(*sys.exc_info()))
return None
return wrapped
@save_if_allowed
def stuff():
raise Exception("stuff")
def foo():
stuff()
foo()
そして、それは本当です: 出力されるトレースバックには、より高いスタック フレームは含まれません:
$ python test.py
例外:
トレースバック (最新の呼び出しが最後):
ファイル「x.py」、21 行目、ラップ
有効な場合は fn(*args, **kwargs) を返し、それ以外の場合はなし
ファイル「x.py」、29行目、stuff
raise Exception("stuff")
例外: もの
ここで、これを少し絞り込むために、スタック フレームには最新のtry/except
ブロックまでのスタック情報しか含まれていないために発生していると思われます…したがって、デコレータなしでこれを再作成できるはずです。
$ cat test.py
def inner():
raise Exception("inner")
def outer():
try:
inner()
except Exception:
print "".join(traceback.format_exception(*sys.exc_info()))
def caller():
outer()
caller()
$ python test.py
Traceback (most recent call last):
File "x.py", line 42, in outer
inner()
File "x.py", line 38, in inner
raise Exception("inner")
Exception: inner
あはは!振り返ってみると、これはある意味で理にかなっています。この時点で、例外は 2 つのスタック フレームに遭遇しただけです。それは、そのフレームinner()
とそのフレームです。例外outer()
は、どこから呼び出されたかをまだ知りません。outer()
したがって、完全なスタックを取得するには、現在のスタックと例外のスタックを組み合わせる必要があります。
$ cat test.py
def inner():
raise Exception("inner")
def outer():
try:
inner()
except Exception:
exc_info = sys.exc_info()
stack = traceback.extract_stack()
tb = traceback.extract_tb(exc_info[2])
full_tb = stack[:-1] + tb
exc_line = traceback.format_exception_only(*exc_info[:2])
print "Traceback (most recent call last):"
print "".join(traceback.format_list(full_tb)),
print "".join(exc_line)
def caller():
outer()
caller()
$ python test.py
Traceback (most recent call last):
File "test.py", line 56, in <module>
caller()
File "test.py", line 54, in caller
outer()
File "test.py", line 42, in outer
inner()
File "test.py", line 38, in inner
raise Exception("inner")
Exception: inner
以下も参照してください。