[英]Python subprocess timeout?
是否有任何参数或选项可以为 Python 的 subprocess.Popen 方法设置超时?
像这样的东西:
subprocess.Popen(['..'], ..., timeout=20)
?
我建议查看threading
模块中的Timer
类。 我用它来实现Popen
的超时。
首先,创建一个回调:
def timeout( p ):
if p.poll() is None:
print 'Error: process taking too long to complete--terminating'
p.kill()
然后打开进程:
proc = Popen( ... )
然后创建一个将调用回调的计时器,将进程传递给它。
t = threading.Timer( 10.0, timeout, [proc] )
t.start()
t.join()
在程序后面的某个地方,您可能需要添加以下行:
t.cancel()
否则,python 程序将继续运行,直到计时器完成运行。
编辑:我被告知存在subprocess
p.poll()
p
可能在p.poll()
和p.kill()
调用之间终止的竞争条件。 我相信以下代码可以解决这个问题:
import errno
def timeout( p ):
if p.poll() is None:
try:
p.kill()
print 'Error: process taking too long to complete--terminating'
except OSError as e:
if e.errno != errno.ESRCH:
raise
尽管您可能希望清除异常处理以专门处理子进程已正常终止时发生的特定异常。
subprocess.Popen 不会阻塞,因此您可以执行以下操作:
import time
p = subprocess.Popen(['...'])
time.sleep(20)
if p.poll() is None:
p.kill()
print 'timed out'
else:
print p.communicate()
它有一个缺点,您必须始终等待至少 20 秒才能完成。
import subprocess, threading
class Command(object):
def __init__(self, cmd):
self.cmd = cmd
self.process = None
def run(self, timeout):
def target():
print 'Thread started'
self.process = subprocess.Popen(self.cmd, shell=True)
self.process.communicate()
print 'Thread finished'
thread = threading.Thread(target=target)
thread.start()
thread.join(timeout)
if thread.is_alive():
print 'Terminating process'
self.process.terminate()
thread.join()
print self.process.returncode
command = Command("echo 'Process started'; sleep 2; echo 'Process finished'")
command.run(timeout=3)
command.run(timeout=1)
这个输出应该是:
Thread started
Process started
Process finished
Thread finished
0
Thread started
Process started
Terminating process
Thread finished
-15
可以看出,在第一次执行中,进程正确完成(返回代码 0),而在第二次执行中,进程被终止(返回代码 -15)。
我还没有在 Windows 中测试过; 但是,除了更新示例命令之外,我认为它应该可以工作,因为我没有在文档中找到任何说明不支持 thread.join 或 process.terminate 的内容。
你可以做
from twisted.internet import reactor, protocol, error, defer
class DyingProcessProtocol(protocol.ProcessProtocol):
def __init__(self, timeout):
self.timeout = timeout
def connectionMade(self):
@defer.inlineCallbacks
def killIfAlive():
try:
yield self.transport.signalProcess('KILL')
except error.ProcessExitedAlready:
pass
d = reactor.callLater(self.timeout, killIfAlive)
reactor.spawnProcess(DyingProcessProtocol(20), ...)
使用 Twisted 的异步进程 API。
没有内置python子进程自动超时,因此您将不得不构建自己的。
这在运行 python 2.7.3 的 Ubuntu 12.10 上对我有用
把它放在一个名为 test.py 的文件中
#!/usr/bin/python
import subprocess
import threading
class RunMyCmd(threading.Thread):
def __init__(self, cmd, timeout):
threading.Thread.__init__(self)
self.cmd = cmd
self.timeout = timeout
def run(self):
self.p = subprocess.Popen(self.cmd)
self.p.wait()
def run_the_process(self):
self.start()
self.join(self.timeout)
if self.is_alive():
self.p.terminate() #if your process needs a kill -9 to make
#it go away, use self.p.kill() here instead.
self.join()
RunMyCmd(["sleep", "20"], 3).run_the_process()
保存并运行它:
python test.py
sleep 20
命令需要 20 秒才能完成。 如果它没有在 3 秒内终止(它不会),则该过程终止。
el@apollo:~$ python test.py
el@apollo:~$
进程运行和终止之间有三秒钟的时间。
不幸的是,没有这样的解决方案。 我设法使用一个线程计时器来做到这一点,该计时器将与在超时后杀死它的进程一起启动,但由于僵尸进程或类似问题,我确实遇到了一些陈旧的文件描述符问题。
不,没有时间。 我想,您正在寻找的是在一段时间后终止子进程。 由于您能够向子进程发出信号,因此您也应该能够杀死它。
向子进程发送信号的通用方法:
proc = subprocess.Popen([command])
time.sleep(1)
print 'signaling child'
sys.stdout.flush()
os.kill(proc.pid, signal.SIGUSR1)
您可以使用此机制在超时后终止。
从 Python 3.3 开始, subprocess 模块中的阻塞辅助函数也有一个timeout
参数。
是的, https: //pypi.python.org/pypi/python-subprocess2 将通过两个附加功能扩展 Popen 模块,
Popen.waitUpTo(timeout=seconds)
这将等待进程完成特定秒数,否则返回 None
还有,
Popen.waitOrTerminate
这将等待一个点,然后调用 .terminate(),然后调用 .kill(),一个或另一个或两者的某种组合,有关完整详细信息,请参阅文档:
对于 Linux,您可以使用信号。 这是平台相关的,因此 Windows 需要另一个解决方案。 不过它可能适用于 Mac。
def launch_cmd(cmd, timeout=0):
'''Launch an external command
It launchs the program redirecting the program's STDIO
to a communication pipe, and appends those responses to
a list. Waits for the program to exit, then returns the
ouput lines.
Args:
cmd: command Line of the external program to launch
time: time to wait for the command to complete, 0 for indefinitely
Returns:
A list of the response lines from the program
'''
import subprocess
import signal
class Alarm(Exception):
pass
def alarm_handler(signum, frame):
raise Alarm
lines = []
if not launch_cmd.init:
launch_cmd.init = True
signal.signal(signal.SIGALRM, alarm_handler)
p = subprocess.Popen(cmd, stdout=subprocess.PIPE)
signal.alarm(timeout) # timeout sec
try:
for line in p.stdout:
lines.append(line.rstrip())
p.wait()
signal.alarm(0) # disable alarm
except:
print "launch_cmd taking too long!"
p.kill()
return lines
launch_cmd.init = False
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.