python subprocess.call不能正确处理信号

时间:2015-12-24 22:54:03

标签: python python-3.x subprocess signals

(我使用Python 3.4.2) 我有一个脚本test.py,它处理SIGTERM等。但是,当它被其他一些脚本调用时,sig处理是不正确的。

这是test.py:

Optional

如果我只是打电话给" test.py"并执行" Ctrl + C"然后将0,1,...,9打印到控制台。但是,如果我使用subprocess.call在另一个脚本中调用test.py,则只会打印0。例如,这是另一个调用test.py:

的脚本
#! /path/to/python3
import time
import signal
import sys

def handleSIG(signal, frame):
    for i in range(10):
        print(i)
    sys.exit()

for sig in [signal.SIGTERM, signal.SIGINT, signal.SIGQUIT, signal.SIGHUP]:
    signal.signal(sig, handleSIG)

time.sleep(30)

奇怪的是,使用subproces.Popen()会使这个错误消失。

2 个答案:

答案 0 :(得分:3)

如果subprocess.callwait中断,那么python 3.3 def call(*popenargs, timeout=None, **kwargs): with Popen(*popenargs, **kwargs) as p: try: return p.wait(timeout=timeout) except: p.kill() p.wait() raise 实现会向其子项发送一个SIGKILL,它是由你的 Ctrl - C (SIGINT - > KeyboardInterrupt异常)。

因此,您会看到处理终端的SIGINT(发送到整个进程组)的子进程与父进程的SIGKILL之间存在竞争。

从python 3.3源代码,为简洁起见编辑:

def call(*popenargs, **kwargs):
    return Popen(*popenargs, **kwargs).wait()

将其与python 2实现进行对比:

wait

多么令人不快的惊喜。当扩展call和{{1}}接口以适应超时时,似乎在3.3中引入了这种行为。我发现这不正确,我filed a bug.

答案 1 :(得分:1)

更新:此Python-3回归将在Python 3.7中通过 PR #5026 修复。有关其他背景和讨论,请参阅bpo-25942和(已拒绝)PR #4283

我最近自己遇到过这个问题。 @pilcrow给出的解释是正确的。

仅仅使用Python 2实现(Popen(*popenargs, **kwargs).wait())的OP解决方案(在评论中)对我来说是不够的,因为我不能100%确定孩子会回复{{1} } 在所有情况下。我仍然希望它能够在超时后被杀死。

我决定只是等待孩子(超时)。

SIGINT

从技术上讲,这意味着我可能会将孩子的生命延长到原来的def nice_call(*popenargs, timeout=None, **kwargs): """ Like subprocess.call(), but give the child process time to clean up and communicate if a KeyboardInterrupt is raised. """ with Popen(*popenargs, **kwargs) as p: try: return p.wait(timeout=timeout) except KeyboardInterrupt: if not timeout: timeout = 0.5 # Wait again, now that the child has received SIGINT, too. p.wait(timeout=timeout) raise except: p.kill() p.wait() raise 之外,但这比不正确的清理行为要好。