我写了一个装饰器,但我不想让它出现在堆栈跟踪中。所以在 Python2 中我会这样做:
class SneakyDecorator(object):
def __init__(self, f):
self.f = f
def __call__(self, *args, **kwargs):
# [...]
try:
self.f(*args, **kwargs)
except:
t, v, tb = sys.exc_info()
raise v, None, tb.tb_next # <=== Important line
在Python3中我会这样做:
class SneakyDecorator:
def __init__(self, f):
self.f = f
def __call__(self, *args, **kwargs):
# [...]
try:
self.f(*args, **kwargs)
except:
t, v, tb = sys.exc_info()
raise v.with_traceback(tb.tb_next) # <=== Important line
所以问题是:有没有办法以与 Python2 和 Python3 兼容的方式做到这一点?我更喜欢一个不涉及两个单独的代码库的解决方案。
我尝试使用 six
模块的 reraise
函数,但问题是,该函数随后出现在堆栈跟踪中。
如果您按照此答案执行操作,也会出现同样的问题:Exception with original traceback - 2.6-3.X compatible version .
更新:Python3 代码不起作用!调用方法仍然显示在堆栈跟踪中。所以接下来的问题是:有没有办法在Python3中做到这一点?
最佳答案
实现此目的的唯一方法是保留 __call__
方法的单独版本,因为 exec 本身会创建一个堆栈帧!下面,在 Python 2 中再次显示了该框架:
class SneakyDecorator:
def __init__(self, f):
self.f = f
def __call__(self, *args, **kwargs):
# [...]
try:
self.f(*args, **kwargs)
except:
t, v, tb = sys.exc_info()
exec("raise v, None, tb.tb_next")
结果:
>>> f()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<string>", line 12, in __call__
File "<stdin>", line 3, in f
ValueError: Oi!
所以使用:
def __call__(self, *args, **kwargs):
# [...]
try:
self.f(*args, **kwargs)
except:
t, v, tb = sys.exc_info()
if sys.version_info[0] <= 2:
exec("raise v, None, tb.tb_next")
else:
raise v.with_traceback(tb.tb_next)
实际上不会起作用。
对于 Python 2,以下内容与两个单独的类具有完全相同的功能:
if sys.version_info[0] <= 2:
_call = '''\
def _call(self, *args, **kwargs):
# [...]
try:
self.f(*args, **kwargs)
except:
t, v, tb = sys.exc_info()
raise v, None, tb.tb_next
'''
exec(_call)
else:
def _call(self, *args, **kwargs):
# [...]
try:
self.f(*args, **kwargs)
except:
t, v, tb = sys.exc_info()
raise v.with_traceback(tb.tb_next)
class SneakyDecorator:
def __init__(self, f):
self.f = f
__call__ = _call
# niceties, patch up name and qualified name. Optional.
__call__.__name__ = '__call__'
__call__.__qualname__ = 'SneakyDecorator.__call__'
但是,请注意,对于 Python 3,Exception.with_traceback()
方法可能允许您在删除当前帧的情况下附加新的回溯,但 Python 会将其重新添加到 当你重新引发异常的那一刻!
关于从堆栈跟踪中隐藏装饰器的 Python 2 和 3 兼容方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23765707/