回溯显示在装饰器之前
这个很不错的小工具是用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_proj\run.py", line 46, in wrapped
return fn(*args, **kwargs) if enabled else None
File "C:\my_proj\run.py", line 490, in a_decorated_function
some_dict['some_value']
KeyError: 'some_value'
- 为什么会这样?
- 有没有办法让我们看到完整的错误信息?
1 个回答
8
啊!好的,这个问题挺有意思的!
这里有一个差不多的函数,它直接从 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
另请参见: