用 Python 编写的服务器的强大无限循环
Robust endless loop for server written in Python
我写了一个处理事件的服务器,处理事件期间未捕获的异常不能终止服务器。
服务器是单个非线程 python 进程。
我想终止这些错误类型:
- 键盘中断
- 内存错误
- ...
内置异常列表很长:https://docs.python.org/2/library/exceptions.html
我不想重新发明这个异常处理,因为我猜它以前做过几次。
如何进行?
- 有一个白名单:一个正常的异常列表,处理下一个事件是正确的选择
- 有一个黑名单:一个异常列表,表明终止服务器是正确的选择。
提示:这个问题不是关于 运行 后台的 unix 守护进程。这与双叉无关,也与重定向无关 stdin/stdout :-)
我会以与您所想的类似的方式执行此操作,使用 'you shall not pass' Gandalf exception handler except Exception
to catch all non-system-exiting exceptions 创建 black-listed set
应该通过并结束的异常是 re-raised.
使用 Gandalf 处理程序 将确保 GeneratorExit
, SystemExit
and KeyboardInterrupt
(all system-exiting exceptions) pass and terminate the program if no other handlers are present higher in the call stack. Here is where you can check with type(e)
that a __class__
of a caught exception e
actually belongs in the set of black-listed exceptions and re-raise
它。
作为一个小演示:
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}
持有我们想要 不 处理的 non-system-exiting 异常的 类。
一个 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
;我使用它是为了 演示 什么异常实际上 'pass through' 并导致终止 (即 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 ), fall-back 导入包含所有 Exceptions
的 builtins
;我们按名称搜索字典,因此没有区别:
try:
import exceptions
excDict = vars(exceptions)
except ImportError:
import builtins
excDict = vars(builtins)
我不知道是否有更聪明的方法来真正做到这一点,另一种解决方案可能不是 try-except
和 signle except
,有 2 个处理程序,一个用于 black-listed 例外情况和其他一般情况:
try:
# calls that raise exceptions:
except tuple(blackSet) as be: # Must go first, of course.
raise be
except Exception as e:
# handle the rest
top-most 例外是 BaseException
。下面有两组:
Exception
派生
- 其他一切
Stopiteration
、ValueError
、TypeError
等都是Exception
的例子。
GeneratorExit
、SystemExit
和 KeyboardInterrupt
之类的东西不是 Execption
的后代。
所以第一步是捕获 Exception
而不是 BaseException
这将使您可以轻松地终止程序。我还建议将 GeneratorExit
捕获为 1) 除非手动引发,否则不应实际看到它; 2)您可以记录它并重新启动循环;和 3) 它旨在表示生成器已经退出并且可以被清理,而不是程序应该退出。
下一步是记录每个异常的详细信息,以便您有可能找出问题所在(当您稍后开始调试时)。
最后,您必须自己决定要终止哪些 Exception
派生异常:我建议 RuntimeError
和 MemoryError
,尽管您可以通过简单地停止并重新启动服务器循环来解决这些问题。
所以,真的,这取决于你。
如果有其他错误(例如尝试加载配置文件时的 IOError
)严重到足以退出,那么负责加载配置文件的代码应该足够聪明以捕捉IOError
并改为加注 SystemExit
。
至于 whitelist/blacklist -- 使用黑名单,因为只有少数(如果有的话)基于 Exception
的异常需要实际终止服务器。
我写了一个处理事件的服务器,处理事件期间未捕获的异常不能终止服务器。
服务器是单个非线程 python 进程。
我想终止这些错误类型:
- 键盘中断
- 内存错误
- ...
内置异常列表很长:https://docs.python.org/2/library/exceptions.html
我不想重新发明这个异常处理,因为我猜它以前做过几次。
如何进行?
- 有一个白名单:一个正常的异常列表,处理下一个事件是正确的选择
- 有一个黑名单:一个异常列表,表明终止服务器是正确的选择。
提示:这个问题不是关于 运行 后台的 unix 守护进程。这与双叉无关,也与重定向无关 stdin/stdout :-)
我会以与您所想的类似的方式执行此操作,使用 'you shall not pass' Gandalf exception handler except Exception
to catch all non-system-exiting exceptions 创建 black-listed set
应该通过并结束的异常是 re-raised.
使用 Gandalf 处理程序 将确保 GeneratorExit
, SystemExit
and KeyboardInterrupt
(all system-exiting exceptions) pass and terminate the program if no other handlers are present higher in the call stack. Here is where you can check with type(e)
that a __class__
of a caught exception e
actually belongs in the set of black-listed exceptions and re-raise
它。
作为一个小演示:
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}
持有我们想要 不 处理的 non-system-exiting 异常的 类。
一个 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
;我使用它是为了 演示 什么异常实际上 'pass through' 并导致终止 (即 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 ), fall-back 导入包含所有 Exceptions
的 builtins
;我们按名称搜索字典,因此没有区别:
try:
import exceptions
excDict = vars(exceptions)
except ImportError:
import builtins
excDict = vars(builtins)
我不知道是否有更聪明的方法来真正做到这一点,另一种解决方案可能不是 try-except
和 signle except
,有 2 个处理程序,一个用于 black-listed 例外情况和其他一般情况:
try:
# calls that raise exceptions:
except tuple(blackSet) as be: # Must go first, of course.
raise be
except Exception as e:
# handle the rest
top-most 例外是 BaseException
。下面有两组:
Exception
派生- 其他一切
Stopiteration
、ValueError
、TypeError
等都是Exception
的例子。
GeneratorExit
、SystemExit
和 KeyboardInterrupt
之类的东西不是 Execption
的后代。
所以第一步是捕获 Exception
而不是 BaseException
这将使您可以轻松地终止程序。我还建议将 GeneratorExit
捕获为 1) 除非手动引发,否则不应实际看到它; 2)您可以记录它并重新启动循环;和 3) 它旨在表示生成器已经退出并且可以被清理,而不是程序应该退出。
下一步是记录每个异常的详细信息,以便您有可能找出问题所在(当您稍后开始调试时)。
最后,您必须自己决定要终止哪些 Exception
派生异常:我建议 RuntimeError
和 MemoryError
,尽管您可以通过简单地停止并重新启动服务器循环来解决这些问题。
所以,真的,这取决于你。
如果有其他错误(例如尝试加载配置文件时的 IOError
)严重到足以退出,那么负责加载配置文件的代码应该足够聪明以捕捉IOError
并改为加注 SystemExit
。
至于 whitelist/blacklist -- 使用黑名单,因为只有少数(如果有的话)基于 Exception
的异常需要实际终止服务器。