Python: Exception decorator. How to preserve stacktrace
我正在写一个装饰器来应用于一个函数。 它应该捕获任何异常,然后根据原始异常消息引发自定义异常。 (这是因为suds抛出一个通用的WebFault异常,我从它的消息中解析Web服务抛出的异常并引发Python异常来镜像它。)
但是,当我在包装器中引发自定义异常时,我希望堆栈跟踪指向引发原始WebFault异常的函数。 到目前为止,我提出了正确的异常(它动态地解析消息并实例化异常类)。 我的问题:如何保持堆栈跟踪指向引发WebFault异常的原始函数?
1 2 3 4 5 6 7 8 9 10 11 12 | from functools import wraps def try_except(fn): def wrapped(*args, **kwargs): try: fn(*args, **kwargs) except Exception, e: parser = exceptions.ExceptionParser() raised_exception = parser.get_raised_exception_class_name(e) exception = getattr(exceptions, raised_exception) raise exception(parser.get_message(e)) return wraps(fn)(wrapped) |
在Python 2.x中,
(将异常类型和异常实例视为两个单独的参数的原因是异常类之前几天的工件。)
所以:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | import sys class MyError(Exception): pass def try_except(fn): def wrapped(*args, **kwargs): try: return fn(*args, **kwargs) except Exception, e: et, ei, tb = sys.exc_info() raise MyError, MyError(e), tb return wrapped def bottom(): 1 / 0 @try_except def middle(): bottom() def top(): middle() >>> top() Traceback (most recent call last): File"<stdin>", line 1, in <module> File"tmp.py", line 24, in top middle() File"tmp.py", line 10, in wrapped return fn(*args, **kwargs) File"tmp.py", line 21, in middle bottom() File"tmp.py", line 17, in bottom 1 / 0 __main__.MyError: integer division or modulo by zero |
在Python 3中,这改变了一点。在那里,回溯附加到异常实例,它们有一个
1 | raise MyError(e).with_traceback(tb) |
另一方面,Python 3也有异常链接,在许多情况下更有意义;要使用它,你只需使用:
1 | raise MyError(e) from e |
我用自定义装饰器装饰的测试遇到了这个问题。
我在装饰器主体中使用了以下构造来保留在unittests输出中打印的原始轨迹:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | try: result = func(self, *args, **kwargs) except Exception: exc_type, exc_instance, exc_traceback = sys.exc_info() formatted_traceback = ''.join(traceback.format_tb( exc_traceback)) message = ' {0} {1}: {2}'.format( formatted_traceback, exc_type.__name__, exc_instance.message ) raise exc_type(message) |