没有捕获Python 2.7自定义异常

时间:2018-09-10 23:04:56

标签: python

我正在使用自定义超时异常来解决iter(subprocess.Popen.stdout.readline,'')在没有更多要读取的输出时阻塞,但是无法正确捕获异常的情况。这是一个既有主流程又有独立流程(由multiprocessing.Process实现)的代码,在这两者中都可能发生超时。相关部分为:

class Timeout(Exception):
    def __init__(self, message):
        self.message = message

def handle_timeout(signal, frame):
    raise Timeout("Timed out")

在主进程中可以很好地捕获此自定义异常,但是在子进程中,只要提高了超时时间,就不会使用(我相信)适当的标准语法来捕获该异常:

from subprocess import Popen, PIPE
subProc = Popen(('tail', '-f', fileName), stdout=PIPE, stderr=PIPE, shell=False, close_fds=True)
lines = iter(subProc.stdout.readline,'')
for line in lines:
    try:
        process_line(line)
    except Timeout as time_out:
        print(time_out.message)
        subProc.terminate()
        break

我得到以下输出,而不是打印超时消息并终止subProc:

Traceback (most recent call last):
  File "/home/username/anaconda2/envs/Py2.7/lib/python2.7/multiprocessing/process.py", line 267, in _bootstrap
    self.run()
  File "reader.py", line 50, in run
    for line in lines:
  File "reader.py", line 13, in handle_timeout
    raise Timeout("Timed out")
Timeout

handle_timeout似乎工作得很好,因为发生了超时,但是异常处理被忽略或跳过。我在语法方面做错了吗,还是需要在子进程中定义一个单独的自定义异常?

编辑:

之前的第二个代码块不完整。这就是当前的状态(包括chepner对iter(stdout.readline,'')的不相关性的建议):

from subprocess import Popen, PIPE
signal.signal(signal.SIGALRM, handle_timeout)
subProc = Popen(('tail', '-f', fileName), stdout=PIPE, stderr=PIPE, shell=False, close_fds=True)
for line in subProc.stdout:
    signal.alarm(CHILD_TIMEOUT)
    try:
        process_line(line)
    except Timeout as time_out:
        print(time_out.message)
        subProc.terminate()
        break

在父进程中(超时异常完全按照期望的方式工作),格式为:

# signal masking as in last block
while True:
    try:
        signal.alarm(MASTER_TIMEOUT) # different from CHILD_TIMEOUT
        other_processing()
    except Timeout:
        shutDown(children) # method to shut down child processes
        break

已解决:

我找到了解决方法。

subProc = Popen(('tail', '-f', fileName), stdout=PIPE, stderr=PIPE, shell=False, close_fds=True)
while not exit.is_set(): # exit is defined by multiprocessing.Event()
    signal.alarm(3)
    try:
        for line in subProc.stdout:
            process_line(line)
    except Timeout:
        print("Process timed out while waiting for log output")
        subProc.terminate()
        exit.set()

现在,当警报响起时,将引发超时异常并按原应的方式捕获该异常,在触发退出条件之前结束子进程,此后子进程将正常关闭。

1 个答案:

答案 0 :(得分:1)

您实际上无法在处理代码的子过程中 内捕获错误。您认为是使用事件捕获来进行错误处理,或者实际上不是正在引发的子流程,执行代码并管理响应。由于您使用popopen手动控制子流程,因此需要手动处理其响应。

当子进程结束时,它应该返回0。如果它返回-1或1,则意味着发生了错误,您需要从stderr读取以捕获错误。

编辑1

我明白了你的问题。您编写处理程序handle_timeout的方式将捕获错误并每次重新引发。您不能在多个地方处理异常。因为它有两个单独的函数试图同时处理同一错误。这将始终产生冲突,并且第一个捕获错误的冲突将导致您的主进程退出。您可以在此处执行几项不同的操作,但请允许我恳请您-不要无缘无故地吃掉错误。

修复1:    删除您的错误处理程序

def handle_timeout(signal, frame):
    raise Timeout("Timed out")

修复2:

try:
    process_line(line)
finally: 
    subProc.terminate()

以上内容将确保子过程终止而不会产生错误。另外,使用诸如handle_timeout处理程序之类的自定义句柄捕获错误是一种几乎专门用于在重新引发错误之前解构复杂运行或对象的技术。它是最后一个解决方案,主要用于在发生特定错误后进行大量清理的情况。如果要这样做,请不要使用except块。