3

tl;博士

在 Python 中读取管道时,我是否应该处理 EINTR“系统调用中断”错误,如果是,我该如何测试这样的代码?

描述

在下面的回溯中,self._dataq是一个multiprocessing.Queue(从技术上讲,我正在使用该billiard库,但我认为它们基本上是相同的代码)。Python 子进程偶尔会写入队列的另一端。我认为发生的事情是一个系统调用正在读取为队列提供数据的管道,并且一个信号到达——可能是来自第二个 Ctrl+C 事件的 SIGINT(第一个 SIGINT 发生在您^C在第二行看到用户的位置记录输出,并且我的信号处理程序捕获了该 SIGINT,正如您在日志中的 WARNING 消息中看到的那样)。

[INFO     2014-03-05 14:16:06,000] Doing some work, la-dee-da
^C[WARNING 2014-03-05 14:16:07,344] Commencing shutdown. (Signal SIGINT, process 2469.). Press Ctrl+C again to exit immediately.
[DEBUG    2014-03-05 14:16:07,347] Terminating subprocess
Traceback (most recent call last):
[... a bunch of stuff omitted]
  File "mycode.py", line 97, in __next__
    result = self._dataq.get(timeout=0.1)
  File "/usr/local/lib/python2.7/site-packages/billiard/queues.py", line 103, in get
    if timeout < 0 or not self._poll(timeout):
IOError: [Errno 4] Interrupted system call

上面回溯中的语句result = self._dataq.get(timeout=0.1)位于循环的中间,如下所示。循环的主要目的是让我可以放弃尝试从self._dataq何时self.timedout()开始返回True

import queue
while True:
    try:
        result = self._dataq.get(timeout=0.1)
    except queue.Empty:
        if self.timedout():
            self.close()
            raise MyTimedoutError()
    else:
        break

问题

如果我关于为什么IOError发生的理论是正确的,那么当它们是由中断的系统调用引起的时,上面的try...except块应该捕获并忽略s 。IOError如果它是导致 EINTR 错误的信号,那么仅仅回到 Python 中运行该except IOError:语句的行为将允许 Python 级别的信号处理程序运行。

那是对的吗?如果是这样,是否可以在我的代码中测试此更改?对我来说,如何编写不包含严重竞争条件的单元测试并不明显。

4

2 回答 2

1

Python 3.5 通过将处理责任EINTR放在 Python 运行时而不是应用程序代码上来解决这个问题。请参阅PEP 475Python 3.5 变更日志

于 2016-07-08T04:07:59.423 回答
0

我将把它称为 Python 中的错误。我找不到任何multiprocessing.Queue允许引发IOError的文档EINTR(尽管引发其他 I/O 问题是有意义的,这就是为什么您应该在忽略引发异常之前检查它的 errno属性;请参阅errno),但它没有t 直接映射到任何提供此类行为的低级 C 函数。有一些关于在 C 级别处理所有EINTRs 的讨论,但这并没有进入 3.4(我怀疑它是否会进入 2.x),所以这可能仍然是公平的报告。

于 2014-12-25T16:45:12.190 回答