捕获不可推翻的异常并重新提升

时间:2012-01-18 19:20:09

标签: python exception-handling multiprocessing

这是我的问题Hang in Python script using SQLAlchemy and multiprocessing的后续内容。正如在该问题中所讨论的,在Python中,腌制异常是有问题的。这通常不是问题,但有一种情况,就是在python多处理模块中发生错误的时候。由于多处理通过酸洗移动对象,如果在多处理过程中发生错误,整个过程可能会挂起,如该问题所示。

一种可能的方法是修复所有有问题的异常,如该问题中所讨论的那样。这并不容易,因为事先不能容易地知道可以调用哪些例外。另一种方法是suggested by lbolla in an answer to the question,用于捕获异常,构造等效的无害异常,然后重新抛出。 但是,我不确定如何做到这一点。请考虑以下代码。

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]

此代码对异常进行pickle和unpickle,然后将其抛出,发出错误

raising error
Traceback (most recent call last):
  File "<stdin>", line 11, in <module>
Exception: BadExc: bad exception error message

Glenn Maynard's answer to "“Inner exception” (with traceback) in Python?"的积分。 这有重要的东西,即回溯,错误消息和异常类型,所以这可能是最好的。但理想情况下,我希望看起来与原始异常完全相同,即

Traceback (most recent call last):
  File "<stdin>", line 11, in <module>
__main__.BadExc: bad exception error message

或更一般地说,前面是异常的名称,而不是Exception。这可能吗?

或者,可以使用BadExc语句代替print foo类,而不是NameError语句,这会产生{{1}}。但是,此异常不需要特殊处理。

1 个答案:

答案 0 :(得分:2)

您可以覆盖sys.excepthook以达到您想要的效果。它至少适用于这个例子,但它非常hacky所以请测试,没有承诺: - )

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的长度来确定异常。)

然后使用两个参数创建Exception,名称(__module__.__class__)和Exception消息(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