python - 可以从 Python 异常回溯中删除装饰器吗?

标签 python python-decorators

我有一些装饰器可以包装很多功能。当其中一个函数抛出错误时,我的回溯被许多装饰线污染了。有没有办法修改 python 装饰器,使其不会在异常回溯中打印自己?

下面是一些示例代码:

import functools


def dec(func):
    @functools.wraps(func)
    def wrap(*args, **kwargs):
        return func(*args, **kwargs)
    return wrap


@dec
def spam():
    print('I sure hope nobody throws an Exception')
    eggs()


@dec
def eggs():
    raise Exception('Spanish Inquisition')


if __name__ == '__main__':
    spam()

当我运行它时,我得到:

Traceback (most recent call last):
  File "tmp.py", line 23, in <module>
    spam()
  File "tmp.py", line 7, in wrap
    return func(*args, **kwargs)
  File "tmp.py", line 14, in spam
    eggs()
  File "tmp.py", line 7, in wrap
    return func(*args, **kwargs)
  File "tmp.py", line 19, in eggs
    raise Exception('Spanish Inquisition')
Exception: Spanish Inquisition

但我真正想要的是:

Traceback (most recent call last):
  File "tmp.py", line 23, in <module>
    spam()
  File "tmp.py", line 14, in spam
    eggs()
  File "tmp.py", line 19, in eggs
    raise Exception('Spanish Inquisition')
Exception: Spanish Inquisition

我该怎么做呢?

最佳答案

因此,我制作了一个装饰器 ignores_exc_tb,它似乎可以满足我的需求。我将在这里发布代码以及我正在使用它的真实代码:

import sys
from functools import wraps
from .util_iter import isiterable
from .util_print import Indenter


IGNORE_EXC_TB = not '--noignore-exctb' in sys.argv


def ignores_exc_tb(func):
    """ decorator that removes other decorators from traceback """
    if IGNORE_EXC_TB:
        @wraps(func)
        def wrapper_ignore_exctb(*args, **kwargs):
            try:
                return func(*args, **kwargs)
            except Exception:
                # Code to remove this decorator from traceback
                exc_type, exc_value, exc_traceback = sys.exc_info()
                # Remove two levels to remove this one as well
                raise exc_type, exc_value, exc_traceback.tb_next.tb_next
        return wrapper_ignore_exctb
    else:
        return func


def indent_decor(lbl):
    def indent_decor_outer_wrapper(func):
        @ignores_exc_tb
        @wraps(func)
        def indent_decor_inner_wrapper(*args, **kwargs):
            with Indenter(lbl):
                return func(*args, **kwargs)
        return indent_decor_inner_wrapper
    return indent_decor_outer_wrapper


def indent_func(func):
    @wraps(func)
    @indent_decor('[' + func.func_name + ']')
    @ignores_exc_tb
    def wrapper_indent_func(*args, **kwargs):
        return func(*args, **kwargs)
    return wrapper_indent_func


def accepts_scalar_input(func):
    '''
    accepts_scalar_input is a decorator which expects to be used on class methods.
    It lets the user pass either a vector or a scalar to a function, as long as
    the function treats everything like a vector. Input and output is sanatized
    to the user expected format on return.
    '''
    @ignores_exc_tb
    @wraps(func)
    def wrapper_scalar_input(self, input_, *args, **kwargs):
        is_scalar = not isiterable(input_)
        if is_scalar:
            iter_input = (input_,)
        else:
            iter_input = input_
        result = func(self, iter_input, *args, **kwargs)
        if is_scalar:
            result = result[0]
        return result
    return wrapper_scalar_input


def accepts_scalar_input_vector_output(func):
    '''
    accepts_scalar_input is a decorator which expects to be used on class
    methods.  It lets the user pass either a vector or a scalar to a function,
    as long as the function treats everything like a vector. Input and output is
    sanatized to the user expected format on return.
    '''
    @ignores_exc_tb
    @wraps(func)
    def wrapper_vec_output(self, input_, *args, **kwargs):
        is_scalar = not isiterable(input_)
        if is_scalar:
            iter_input = (input_,)
        else:
            iter_input = input_
        result = func(self, iter_input, *args, **kwargs)
        if is_scalar:
            if len(result) != 0:
                result = result[0]
        return result
    return wrapper_vec_output

编辑: 用于重新引发异常的新 python 3 语法:

exc_type, exc_value, exc_traceback = sys.exc_info() 尝试: exc_traceback = exc_traceback.tb_next exc_traceback = exc_traceback.tb_next 除了异常: 经过 ex = exc_type(exc_value) ex.__traceback__ = exc_traceback 抚养前任

关于python - 可以从 Python 异常回溯中删除装饰器吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22825165/

相关文章:

python - 有没有办法让 python 函数知道它在模块加载时被装饰了?

编译时调用的 Python 装饰器函数

python - 在 Python 中读取非结构化文本文件以使其结构化

python - 如何在 Django 中从 JavaScript 调用 Python 函数?

python - 如何编码 ('ascii' , 'ignore' ) 抛出 UnicodeDecodeError?

python - 如何在Python中向继承方法添加装饰器而不复制整个方法?

python - 如何从表中确定 pytables 文件名

python - 使用 for 循环 python 在两个列表中查找公共(public)项 - 降低时间复杂度

带有参数 : @repeat(n) 的 Python3 'repeat' 装饰器

Python编码技术: class methods that use another method as interface to do system calls