(我使用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()会使这个错误消失。
答案 0 :(得分:3)
如果subprocess.call
被wait
中断,那么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
之外,但这比不正确的清理行为要好。