追溯显示,直到装饰者
问题描述
这个漂亮的小Python装饰器可以配置地禁用装饰函数:
enabled = get_bool_from_config()
def run_if_enabled(fn):
def wrapped(*args, **kwargs):
try:
return fn(*args, **kwargs) if enabled else None
except Exception:
log.exception('')
return None
return wrapped
唉,如果在fn()
内引发异常,回溯只显示到包装器:
Traceback (most recent call last):
File "C:my_projun.py", line 46, in wrapped
return fn(*args, **kwargs) if enabled else None
File "C:my_projun.py", line 490, in a_decorated_function
some_dict['some_value']
KeyError: 'some_value'
- 为什么?
- 我是否可以解决办法以查看完整的回溯?
解决方案
啊!好了,这是一个有趣的问题!
这里是相同的近似函数,但直接从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 The exception: Traceback (most recent call last): File "x.py", line 21, in wrapped return fn(*args, **kwargs) if enabled else None File "x.py", line 29, in stuff raise Exception("stuff") 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
阿哈!现在,经过反思,这在某种程度上是有意义的:在这一点上,异常只遇到了两个堆栈帧: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
另见:
- http://docs.python.org/2/library/sys.html
- http://docs.python.org/2/library/traceback.html
相关文章