捕捉无法解决的异常和重新提升 [英] Catching unpickleable exceptions and re-raising
问题描述
这是对我的问题的追踪使用SQLAlchemy挂入Python脚本和多处理。正如在这个问题中讨论的,腌制异常在Python中是有问题的。这通常不是一个问题,但有一种情况是,当python多处理模块出现错误时。由于多处理通过酸洗来移动对象,如果多处理过程中出现错误,整个过程可能会挂起,如该问题所示。
This is a followup to my question Hang in Python script using SQLAlchemy and multiprocessing. As discussed in that question, pickling exceptions is problematic in Python. This is usually not a issue, but one case when it is, is when errors occur in the python multiprocessing module. Since multiprocessing moves objects around by pickling, if an error occurs inside a multiprocessing process, the entire process may hang, as demonstrated in that question.
一种可能的方法是修复所有有问题的例外,正如那个问题所讨论的。这并不容易,因为人们不能容易地知道哪些例外可能被称为。 由lbolla在问题答案中提出的替代方法是捕获异常,构建等效的无害例外,然后再推翻。
但是,我不知道如何做到这一点。考虑以下代码。
One possible approach is to fix all the problematic exceptions, as discussed in that question. This is not easy, since one cannot easily know in advance which exceptions may be called. An alternative approach, which was suggested by lbolla in an answer to the question, is to catch the exception, construct an equivalent harmless exception, and then rethrow. However, I'm not sure of exactly how to do this. Consider the following code.
class BadExc(Exception):
def __init__(self, message, a):
'''Non-optional param in the constructor.'''
Exception.__init__(self, message)
self.a = a
import sys
try:
try:
#print foo
raise BadExc("bad exception error message", "a")
except Exception, e:
raise Exception(e.__class__.__name__ + ": " +str(e)), None, sys.exc_info()[2]
except Exception, f:
pass
import cPickle
a = cPickle.dumps(f)
l = cPickle.loads(a)
print "raising error"
raise sys.exc_info()[0], sys.exc_info()[1], sys.exc_info()[2]
此代码腌制并解开异常,然后抛出它,给出错误
This code pickles and unpickles the exception, and then throws it, giving the error
raising error
Traceback (most recent call last):
File "<stdin>", line 11, in <module>
Exception: BadExc: bad exception error message
信用到 Glenn Maynard对内部异常(带回溯)在Python中的回答?。
这有重要的东西,即回溯,错误消息和异常类型,所以这可能是最好的。但最理想的情况是,我想要的东西看起来就像原来的例外,即
Credits to Glenn Maynard's answer to ""Inner exception" (with traceback) in Python?". This has the important stuff, namely the traceback, the error message, and the exception type, so this might be the best one can do. But ideally I'd like something that looks exactly like the original exception, namely
Traceback (most recent call last):
File "<stdin>", line 11, in <module>
__main__.BadExc: bad exception error message
或更一般地,例外在前面,而不是异常
。这是可能的吗?
or more generally, with the name of the exception in the front, rather than Exception
. Is this possible?
或者,而不是 BadExc
类,可以使用打印foo
语句,它给出了一个 NameError
。但是,此异常不需要特殊处理。
Alternatively, instead of the BadExc
class, one can use the print foo
statement instead, which gives a NameError
. However, this exception does not require special handling.
推荐答案
您可以覆盖 sys.excepthook
来实现你想要的。它至少适用于这个例子,但它非常麻烦,所以请测试和没有承诺: - )
You can override sys.excepthook
to achieve what you want. It at least works for this example, but it's pretty hacky so please test and no promises :-)
import sys
def excepthook_wrapper(type, value, traceback):
if len(value.args) == 2:
name, msg = value.args
value.args = (msg,)
sys.__excepthook__(name, value, traceback)
else:
sys.__excepthook__(type, value, traceback)
sys.excepthook = excepthook_wrapper
(编辑:我不是因为现在'正常'有两个参数的异常会得到不同的处理。可能的解决方案是通过将PICKLED作为第一个参数标记你的特殊异常,然后检查它,而不是检查 args
。)
然后创建异常
有两个参数,名称( __ module __.__ class __
)和异常
年龄( str(e)
):
And then create the Exception
with two arguments, the name (__module__.__class__
) and the Exception
message (str(e)
):
try:
try:
#print foo
raise BadExc("bad exception error message", "a")
except Exception, e:
cls = e.__class__
if hasattr(cls, '__module__'):
name = '{0}.{1}'.format(cls.__module__, cls.__name__)
else:
name = cls.__name__
raise Exception(name, str(e)), None, sys.exc_info()[2]
except Exception, f:
pass
然后这个:
import cPickle
a = cPickle.dumps(f)
l = cPickle.loads(a)
print "raising error"
raise sys.exc_info()[0], sys.exc_info()[1], sys.exc_info()[2]
打印:
raising error
Traceback (most recent call last):
File "test.py", line 18, in <module>
raise BadExc("bad exception error message", "a")
__main__.BadExc: bad exception error message
这篇关于捕捉无法解决的异常和重新提升的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!