子进程超时失败
Subprocess timeout failure
我想在子进程上使用超时
from subprocess32 import check_output
output = check_output("sleep 30", shell=True, timeout=1)
不幸的是,虽然这会引发超时错误,但它会在 30 秒后发生。 check_output好像不能打断shell命令。
Python 方面我可以做些什么来阻止这种情况?
我怀疑 subprocess32 未能杀死超时进程。
check_output()
with timeout is essentially:
with Popen(*popenargs, stdout=PIPE, **kwargs) as process:
try:
output, unused_err = process.communicate(inputdata, timeout=timeout)
except TimeoutExpired:
process.kill()
output, unused_err = process.communicate()
raise TimeoutExpired(process.args, timeout, output=output)
有两个问题:
- [第二个]
.communicate()
可能会等待后代进程,而不仅仅是直接子进程,请参阅 Python subprocess .check_call vs
.check_output
process.kill()
可能不会杀死整个进程树,请参阅 How to terminate a python subprocess launched with shell=True
它导致您观察到的行为:TimeoutExpired
在一秒钟内发生,shell 被杀死,但 check_output()
returns 仅在 30 秒后发生孙 sleep
进程退出。
要解决这些问题,请终止整个进程树(属于同一组的所有子进程):
#!/usr/bin/env python3
import os
import signal
from subprocess import Popen, PIPE, TimeoutExpired
from time import monotonic as timer
start = timer()
with Popen('sleep 30', shell=True, stdout=PIPE, preexec_fn=os.setsid) as process:
try:
output = process.communicate(timeout=1)[0]
except TimeoutExpired:
os.killpg(process.pid, signal.SIGINT) # send signal to the process group
output = process.communicate()[0]
print('Elapsed seconds: {:.2f}'.format(timer() - start))
输出
Elapsed seconds: 1.00
更新 Python 3.6.
这仍然在发生,但我已经测试了很多 check_output
、communicate
和 run
方法的组合,现在我清楚地知道错误在哪里以及如何发生在 Python 3.5 和 Python 3.6.
上以简单的方式避免它
我的结论:当您在 stdout
、stderr
或 stdin
上混合使用 shell=True
和任何 PIPE
时,就会发生这种情况参数(用于Popen
和run
方法)。
注意:check_output
在中使用了PIPE
。
如果您查看 Python 3.6 中的代码,它基本上是使用 stdout=PIPE
调用 run
:https://github.com/python/cpython/blob/ae011e00189d9083dd84c357718264e24fe77314/Lib/subprocess.py#L335
因此,要解决 Python 3.5 或 3.6 上的@innisfree 问题,只需执行以下操作:
check_output(['sleep', '30'], timeout=1)
对于其他情况,只需避免混合使用 shell=True
和 PIPE
,记住 check_output
使用 PIPE
.
我想在子进程上使用超时
from subprocess32 import check_output
output = check_output("sleep 30", shell=True, timeout=1)
不幸的是,虽然这会引发超时错误,但它会在 30 秒后发生。 check_output好像不能打断shell命令。
Python 方面我可以做些什么来阻止这种情况? 我怀疑 subprocess32 未能杀死超时进程。
check_output()
with timeout is essentially:
with Popen(*popenargs, stdout=PIPE, **kwargs) as process:
try:
output, unused_err = process.communicate(inputdata, timeout=timeout)
except TimeoutExpired:
process.kill()
output, unused_err = process.communicate()
raise TimeoutExpired(process.args, timeout, output=output)
有两个问题:
- [第二个]
.communicate()
可能会等待后代进程,而不仅仅是直接子进程,请参阅 Python subprocess .check_call vs .check_output process.kill()
可能不会杀死整个进程树,请参阅 How to terminate a python subprocess launched with shell=True
它导致您观察到的行为:TimeoutExpired
在一秒钟内发生,shell 被杀死,但 check_output()
returns 仅在 30 秒后发生孙 sleep
进程退出。
要解决这些问题,请终止整个进程树(属于同一组的所有子进程):
#!/usr/bin/env python3
import os
import signal
from subprocess import Popen, PIPE, TimeoutExpired
from time import monotonic as timer
start = timer()
with Popen('sleep 30', shell=True, stdout=PIPE, preexec_fn=os.setsid) as process:
try:
output = process.communicate(timeout=1)[0]
except TimeoutExpired:
os.killpg(process.pid, signal.SIGINT) # send signal to the process group
output = process.communicate()[0]
print('Elapsed seconds: {:.2f}'.format(timer() - start))
输出
Elapsed seconds: 1.00
更新 Python 3.6.
这仍然在发生,但我已经测试了很多 check_output
、communicate
和 run
方法的组合,现在我清楚地知道错误在哪里以及如何发生在 Python 3.5 和 Python 3.6.
我的结论:当您在 stdout
、stderr
或 stdin
上混合使用 shell=True
和任何 PIPE
时,就会发生这种情况参数(用于Popen
和run
方法)。
注意:check_output
在中使用了PIPE
。
如果您查看 Python 3.6 中的代码,它基本上是使用 stdout=PIPE
调用 run
:https://github.com/python/cpython/blob/ae011e00189d9083dd84c357718264e24fe77314/Lib/subprocess.py#L335
因此,要解决 Python 3.5 或 3.6 上的@innisfree 问题,只需执行以下操作:
check_output(['sleep', '30'], timeout=1)
对于其他情况,只需避免混合使用 shell=True
和 PIPE
,记住 check_output
使用 PIPE
.