从堆栈跟踪中隐藏装饰器的 Python 2 和 3 兼容方法

标签 python python-2.7 python-3.x decorator stack-trace

我写了一个装饰器,但我不想让它出现在堆栈跟踪中。所以在 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/

相关文章:

python - Flask-SocketIO 未使用 Gevent/Gevent-websocket

python - 重新排列字典的输出 (Python)

python - 如何使用 pyplot 绘制 2*2 图像网格?

python - 如何使用 Python 2.7 安装 aggdraw

python - 如何基于python shebang从powershell运行python程序?

python - pandas如何识别具有特定模式的字符串

python - 如何将相关列表转换为协方差矩阵?

python - 如何使用python bottle框架获取客户端IP地址

城市 map 上的 Python 绘图点

Python - 多项式除法 GF(2) 域