Adding information to an exception?
我希望实现这样的目标:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | def foo(): try: raise IOError('Stuff ') except: raise def bar(arg1): try: foo() except Exception as e: e.message = e.message + 'happens at %s' % arg1 raise bar('arg1') |
1 2 | Traceback... IOError('Stuff Happens at arg1') |
但我得到的是:
1 2 | Traceback.. IOError('Stuff') |
关于如何实现这一点的任何线索? 如何在Python 2和3中完成它?
我这样做,所以改变
1 2 3 4 5 6 7 8 9 10 11 12 13 | def foo(): try: raise IOError('Stuff') except: raise def bar(arg1): try: foo() except Exception as e: raise type(e)(e.message + ' happens at %s' % arg1) bar('arg1') |
1 2 3 4 5 6 | Traceback (most recent call last): File"test.py", line 13, in <module> bar('arg1') File"test.py", line 11, in bar raise type(e)(e.message + ' happens at %s' % arg1) IOError: Stuff happens at arg1 |
更新1
这是一个保留原始追溯的略微修改:
1 2 3 4 5 6 7 8 9 10 | ... def bar(arg1): try: foo() except Exception as e: import sys raise type(e), type(e)(e.message + ' happens at %s' % arg1), sys.exc_info()[2] bar('arg1') |
1 2 3 4 5 6 7 8 | Traceback (most recent call last): File"test.py", line 16, in <module> bar('arg1') File"test.py", line 11, in bar foo() File"test.py", line 5, in foo raise IOError('Stuff') IOError: Stuff happens at arg1 |
更新2
对于Python 3.x,我的第一次更新中的代码在语法上是不正确的,加上在
1 | During handling of the above exception, another exception occurred: |
在显示的追溯消息中。
1 2 3 4 5 6 7 8 9 10 11 | # for Python 3.x ... def bar(arg1): try: foo() except Exception as e: import sys raise type(e)(str(e) + ' happens at %s' % arg1).with_traceback(sys.exc_info()[2]) bar('arg1') |
更新3
一位评论者询问是否有一种方法可以在Python 2和3中运行。虽然由于语法差异,答案似乎是"否",但通过使用像
另请注意,由于异常在
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 | import sys if sys.version_info.major < 3: # Python 2? # Using exec avoids a SyntaxError in Python 3. exec("""def reraise(exc_type, exc_value, exc_traceback=None): raise exc_type, exc_value, exc_traceback""") else: def reraise(exc_type, exc_value, exc_traceback=None): if exc_value is None: exc_value = exc_type() if exc_value.__traceback__ is not exc_traceback: raise exc_value.with_traceback(exc_traceback) raise exc_value def foo(): try: raise IOError('Stuff') except: raise def bar(arg1): try: foo() except Exception as e: reraise(type(e), type(e)(str(e) + ' happens at %s' % arg1), sys.exc_info()[2]) bar('arg1') |
如果您来到这里寻找Python 3的解决方案,手册说:
When raising a new exception (rather than using a bare
raise to re-raise the exception currently being handled), the implicit exception context can be supplemented with an explicit cause by using from with raise:
1 | raise new_exc from original_exc |
例:
1 2 3 4 5 6 | try: return [permission() for permission in self.permission_classes] except TypeError as e: raise TypeError("Make sure your view's 'permission_classes' are iterable." "If you use '()' to generate a set with a single element" "make sure that there is a comma behind the one (element,).") from e |
最终看起来像这样:
1 2 3 4 5 6 7 8 9 10 11 12 13 | 2017-09-06 16:50:14,797 [ERROR] django.request: Internal Server Error: /v1/sendEmail/ Traceback (most recent call last): File"venv/lib/python3.4/site-packages/rest_framework/views.py", line 275, in get_permissions return [permission() for permission in self.permission_classes] TypeError: 'type' object is not iterable The above exception was the direct cause of the following exception: Traceback (most recent call last): # Traceback removed... TypeError: Make sure your view's Permission_classes are iterable. If you use parens () to generate a set with a single element make sure that there is a (comma,) behind the one element. |
将一个完全不起眼的
假设您不想或不能修改foo(),您可以这样做:
1 2 3 4 5 6 | try: raise IOError('stuff') except Exception as e: if len(e.args) >= 1: e.args = (e.args[0] + ' happens',) + e.args[1:] raise |
这确实是解决Python 3中的问题的唯一解决方案,没有丑陋和令人困惑的"在处理上述异常时,发生了另一个异常"消息。
如果应该将重新提升行添加到堆栈跟踪中,则写入
我使用的一个方便的方法是使用class属性作为存储细节,
因为class属性可以从类对象和类实例访问:
1 2 | class CustomError(Exception): details = None |
然后在你的代码中:
1 2 3 | exc = CustomError('Some message') exc.details('Details -- add whatever you want') raise exc |
并在捕获错误时:
1 2 3 4 | except CustomError, e: # Do whatever you want with the exception instance print e print e.details |
我将提供一段代码,每当我想要向异常添加额外信息时,我经常使用这些代码。我在Python 2.7和3.6中都工作。
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 | import sys import traceback try: a = 1 b = 1j # The line below raises an exception because # we cannot compare int to complex. m = max(a, b) except Exception as ex: # I create my informational message for debugging: msg ="a=%r, b=%r" % (a, b) # Gather the information from the original exception: exc_type, exc_value, exc_traceback = sys.exc_info() # Format the original exception for a nice printout: traceback_string = ''.join(traceback.format_exception( exc_type, exc_value, exc_traceback)) # Re-raise a new exception of the same class as the original one, # using my custom message and the original traceback: raise type(ex)("%s ORIGINAL TRACEBACK: %s " % (msg, traceback_string)) |
上面的代码产生以下输出:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | --------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-6-09b74752c60d> in <module>() 14 raise type(ex)( 15 "%s ORIGINAL TRACEBACK: %s " % ---> 16 (msg, traceback_string)) TypeError: a=1, b=1j ORIGINAL TRACEBACK: Traceback (most recent call last): File"<ipython-input-6-09b74752c60d>", line 7, in <module> m = max(a, b) # Cannot compare int to complex TypeError: no ordering relation is defined for complex numbers |
我知道这与问题中提供的示例略有不同,但我希望有人发现它有用。
与以前的答案不同,这适用于非常糟糕的
但它会修改类型,以便分解无用的
我仍然希望找到一个不会修改类型的额外改进。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | from contextlib import contextmanager @contextmanager def helpful_info(): try: yield except Exception as e: class CloneException(Exception): pass CloneException.__name__ = type(e).__name__ CloneException.__module___ = type(e).__module__ helpful_message = '%s helpful info!' % e import sys raise CloneException, helpful_message, sys.exc_traceback class BadException(Exception): def __str__(self): return 'wat.' with helpful_info(): raise BadException('fooooo') |
保留原始回溯和类型(名称)。
1 2 3 4 5 6 7 8 9 10 11 12 | Traceback (most recent call last): File"re_raise.py", line 20, in <module> raise BadException('fooooo') File"/usr/lib64/python2.6/contextlib.py", line 34, in __exit__ self.gen.throw(type, value, traceback) File"re_raise.py", line 5, in helpful_info yield File"re_raise.py", line 20, in <module> raise BadException('fooooo') __main__.BadException: wat. helpful info! |
您可以定义从另一个继承的自己的异常,并创建它自己的构造函数来设置值。
例如:
1 2 3 4 5 6 7 | class MyError(Exception): def __init__(self, value): self.value = value Exception.__init__(self) def __str__(self): return repr(self.value) |
也许
1 2 | except Exception as e: raise IOError(e.message + 'happens at %s'%arg1) |