我想在子进程上使用超时
from subprocess32 import check_output
output = check_output("sleep 30", shell=True, timeout=1)
不幸的是,虽然这会引发超时错误,但它会在30秒后发生。似乎check_output不能中断shell命令。
我可以在Python端做些什么来阻止它? 我怀疑subprocess32无法终止超时过程。
答案 0 :(得分:22)
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()
仅在孙sleep
进程退出后30秒内返回。
要解决问题,请终止整个进程树(属于同一组的所有子进程):
#!/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
答案 1 :(得分:1)
针对Python 3.6的更新。
这仍然在发生,但是我已经测试了check_output
,communicate
和run
方法的许多组合,现在我对错误在哪里以及如何避免使用有了清楚的了解在Python 3.5和Python 3.6上可以轻松实现。
我的结论:当您在shell=True
,PIPE
或stdout
参数上混合使用stderr
和任何stdin
时会发生这种情况(用于Popen
和run
方法中)。
请注意: check_output
在内部使用PIPE
。
如果您在Python 3.6上查看内部代码,则基本上是使用run
对stdout=PIPE
的调用: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
。