关于python:捕获不可解决的异常并重新提升

Catching unpickleable exceptions and re-raising

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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和unpickles,然后将其抛出,从而产生错误

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

归功于Glenn Maynard对Python中"内部异常"(带回溯)的回答?"
这有重要的东西,即回溯,错误消息和异常类型,所以这可能是最好的。但理想情况下,我喜欢看起来与原始异常完全相同的东西,即

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

或者更一般地说,前面有例外的名称,而不是Exception。这可能吗?

或者,代替BadExc类,可以使用print foo语句代替NameError。但是,此异常不需要特殊处理。


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

1
2
3
4
5
6
7
8
9
10
11
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)):

1
2
3
4
5
6
7
8
9
10
11
12
13
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

然后这个:

1
2
3
4
5
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]

打印:

1
2
3
4
5
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