15

我编写了一个服务器来处理事件,并且在处理事件期间未捕获的异常不能终止服务器。

服务器是单个非线程 python 进程。

我想终止这些错误类型:

  • 键盘中断
  • 内存错误
  • ...

内置异常列表很长:https ://docs.python.org/2/library/exceptions.html

我不想重新发明这种异常处理,因为我猜它之前已经做过好几次了。

如何进行?

  1. 有一个白名单:一个正常的异常列表,处理下一个事件是正确的选择
  2. 有一个黑名单:一个异常列表,表明终止服务器是正确的选择。

提示:这个问题与在后台运行 unix 守护进程无关。这与双叉无关,也与重定向 stdin/stdout 无关 :-)

4

2 回答 2

4

我会以您正在考虑的类似方式执行此操作,使用“您不应通过” Gandalf异常处理程序except Exception来捕获所有非系统退出异常set,同时创建应通过并结束的异常的黑名单重新-提高。

如果调用堆栈中没有更高的处理程序,则使用Gandalf 处理程序将确保GeneratorExit和(所有系统退出异常)通过并终止程序。在这里,您可以检查捕获的异常中的 a是否确实属于列入黑名单的异常集合,然后重新检查。SystemExitKeyboardInterrupttype(e)__class__eraise

作为一个小示范:

import exceptions  # Py2.x only

# dictionary holding {exception_name: exception_class}
excptDict = vars(exceptions)

exceptionNames = ['MemoryError', 'OSError', 'SystemError'] # and others

# set containing black-listed exceptions
blackSet = {excptDict[exception] for exception in exceptionNames}

现在保存我们不想blackSet = {OSError, SystemError, MemoryError}处理的非系统退出异常的类。

块现在try-except看起来像这样:

try:
    # calls that raise exceptions:
except Exception as e:
    if type(e) in blackSet: raise e # re-raise
    # else just handle it

使用捕获所有异常的示例BaseException可以帮助说明我的意思。(这样做仅用于演示目的,以便了解这种提升将如何最终终止您的程序)。请注意建议您使用BaseException; 我使用它是为了演示什么异常实际上会“通过”并导致终止(即BaseException捕获的所有内容):

for i, j in excptDict.iteritems():
    if i.startswith('__'): continue  # __doc__ and other dunders
    try:
        try:
            raise j
        except Exception as ex:
            # print "Handler 'Exception' caught " + str(i)
            if type(ex) in blackSet:
                raise ex           
    except BaseException:
        print "Handler 'BaseException' caught " + str(i)

# prints exceptions that would cause the system to exit     
Handler 'BaseException' caught GeneratorExit
Handler 'BaseException' caught OSError
Handler 'BaseException' caught SystemExit
Handler 'BaseException' caught SystemError
Handler 'BaseException' caught KeyboardInterrupt
Handler 'BaseException' caught MemoryError
Handler 'BaseException' caught BaseException

最后,为了使这个 Python 2/3 不可知,您可以try并且import exceptions如果失败(在 Python 3 中就是这样),则回退到builtins包含 all的导入Exceptions;我们按名称搜索字典,所以没有区别:

try:
    import exceptions
    excDict = vars(exceptions)
except ImportError:
    import builtins 
    excDict = vars(builtins)

我不知道是否有更聪明的方法来实际执行此操作,另一种解决方案可能不是使用try-except带有 signle的 a ,而是except拥有 2 个处理程序,一个用于黑名单异常,另一个用于一般情况:

try:
    # calls that raise exceptions:
except tuple(blackSet) as be:  # Must go first, of course.
    raise be
except Exception as e:
    # handle the rest
于 2016-01-25T12:06:18.640 回答
2

最上面的例外是BaseException. 其下有两组:

  • Exception衍生的
  • 其他一切

Stopiteration, ValueError,等TypeError都是 的例子Exception

之类的东西GeneratorExitSystemExitKeyboardInterrupt没有后裔Execption

所以第一步是捕捉Exception而不是BaseException让你轻松终止程序。我还建议将其捕获GeneratorExit为 1) 除非手动提升它,否则它永远不会被实际看到;2)您可以记录它并重新启动循环;3)它的目的是表示生成器已经退出并且可以清理,而不是程序应该退出。

下一步是用足够的细节记录每个异常,以便您有可能找出问题所在(当您稍后开始调试时)。

最后,您必须自己决定Exception要终止哪些派生异常(如果有):我建议RuntimeErrorand MemoryError,尽管您可以通过简单地停止和重新启动服务器循环来解决这些问题。

所以,真的,这取决于你。

如果有一些其他严重到足以退出的错误(例如IOError尝试加载配置文件时),那么负责加载配置文件的代码应该足够聪明,可以捕获IOError并引发错误SystemExit

至于白名单/黑名单 - 使用黑名单,因为应该只有少数(如果有的话)Exception基于您需要实际终止服务器的异常。

于 2016-01-28T05:57:12.993 回答