捕捉无法解决的异常和重新提升 [英] Catching unpickleable exceptions and re-raising

查看:146
本文介绍了捕捉无法解决的异常和重新提升的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这是对我的问题的追踪使用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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆