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

python subprocess.call不';不能正确处理信号,python,python-3.x,subprocess,signals,Python,Python 3.x,Subprocess,Signals,(我正在使用Python 3.4.2) 我有一个script test.py,它处理SIGTERM等。但是,当它被其他脚本调用时,sig处理是不正确的 这是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

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

这是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)
import subprocess

cmd = '/path/to/test.py'
subprocess.call(cmd)
如果我只调用“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)
import subprocess

cmd = '/path/to/test.py'
subprocess.call(cmd)

奇怪的是,使用subprocess.Popen()可以消除这个错误。

如果它的
等待被中断,python 3.3
subprocess.call
实现会向它的子进程发送一个SIGKILL,这是由Ctrl-C(SIGINT->KeyboardInterrupt异常)引起的

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

来自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
与python 2实现相比:

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

多么令人不快的惊喜啊。在3.3中,当扩展
wait
call
接口以适应超时时,似乎引入了这种行为。我不认为这是正确的,我已经更新了:

这个Python-3回归将通过在Python 3.7中修复。有关其他背景和讨论,请参阅和(已拒绝)


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

OP仅仅使用Python 2实现(
Popen(*popenargs,**kwargs).wait()
)的解决方案(在评论中)对我来说是不够的,因为我不能100%确定孩子在所有情况下都会响应
SIGINT
。我还是希望它在暂停后被杀死

我决定简单地重新等待孩子(超时)


从技术上讲,这意味着我有可能延长孩子的寿命,使其超出原来的
超时时间
,但这比不正确的清理行为要好。

Popen也不会等待流程完成,因此不确定这在什么情况下会起作用all@PadraicCunningham我从命令行运行test2.py,并使用Ctrl-C终止它。对于Popen,我使用“subprocess.Popen(cmd).wait()”,因此它会等待cmd完成。顺便说一句,从subprocess.call()的实现中,可以看出子进程的信号处理功能将被阻止。如果没有打印任何内容,我会理解,但奇怪的是,它打印了一些东西(在我的例子中,0),但不是全部。您使用的是什么操作系统?linux(在windows下通过PuTTy登录)您可以提到一个解决方法:在父级中为
signal.SIGINT
定义自己的处理程序(以避免
键盘中断
异常)或者显式调用
Popen().wait()
(如果不需要超时)。是。我认为OP已经意识到直接使用
Popen
。对于自定义SIGINT处理程序,您希望它做什么?大概OP希望使用Ctrl-C向整个流程组发送一个SIGINT,重要的是,中断
等待
@pilcrow非常感谢您的解释。我用
subprocess.Popen(cmd).wait()替换了
subprocess.call(cmd)
,解决了这个问题。是的,我的目标是使用Ctrl-C终止父进程并使所有子进程正常退出。@pilcrow:nothing(
signal(SIGINT,lambda*a:None)
),如果子进程在SIGINT上退出(SIGINT已经发送到整个进程组),则不需要中断
。wait()
。但是如果您可以消除所有
.call()
调用(包括在第三方代码中),那么
Popen().wait()
可能更可取。也许
os.\u exit()
在信号处理程序中会更好。这将产生预期的行为——我们可能也希望父对象终止。无论哪种方式,都是一种非常特殊的解决不愉快情况的方法。