Есть ли аргумент или параметры для установки тайм-аута для подпроцесса Python.Popen?Тайм-аут подпроцесса Python?
Что-то вроде этого:
subprocess.Popen(['..'], ..., timeout=20)
?
Есть ли аргумент или параметры для установки тайм-аута для подпроцесса Python.Popen?Тайм-аут подпроцесса Python?
Что-то вроде этого:
subprocess.Popen(['..'], ..., timeout=20)
?
Я бы посоветовал посмотреть на Timer class в потолочном модуле. Я использовал его для реализации тайм-аута для 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()
В противном случае программа питона будет продолжать работать, пока таймер не закончит работу.
EDIT: Мне сообщили, что существует условие гонки, что подпроцесс 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
Хотя вы можете очистить обработку специально обрабатывать только конкретное исключение, которое происходит, когда подпроцесс уже завершон исключение.
Этот код имеет состояние гонки. –
Майк, не могли бы вы разработать или изменить исправление выше? Я использовал один и тот же код несколько раз, и если есть проблема, я бы определенно хотел его исправить. – dvntehn00bz
'print 'Ошибка: процесс занимает слишком много времени, чтобы завершить - terminating'' может работать, даже если это ложь, и ваш процесс завершается без вашего убийства (потому что он делает это в моменты между вашими вызовами' poll' и 'kill'). –
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 секунд для того, чтобы закончить.
Это заморозило бы этот процесс в течение 20 секунд. Это приемлемо? –
разве этот вид не побеждает точку использования подпроцесса? – aaronasterling
Кажется! Я думаю, что это небольшое неудобство в использовании подпроцесса. – sultan
К сожалению, такого решения нет. Мне удалось сделать это с помощью потокового таймера, который будет запускаться вместе с процессом, который убьет его после таймаута, но я столкнулся с некоторыми проблемами устаревших файловых дескрипторов из-за процессов зомби или некоторых таких.
+1 Это было бы моим решением. – aaronasterling
У меня тоже возникла такая проблема с файловыми дескрипторами процессов зомби. – sultan
Султан. Должно быть возможно исправить это. Мне удалось окончательно отполировать мое приложение на что-то работоспособное, но оно не было достаточно общим для публикации. –
Нет времени. Я предполагаю, что вы ищете, чтобы убить подпроцесс через некоторое время. Поскольку вы можете сигнализировать о подпроцессе, вы также сможете его убить.
общий подход к отправке сигнала на подпроцесс:
proc = subprocess.Popen([command])
time.sleep(1)
print 'signaling child'
sys.stdout.flush()
os.kill(proc.pid, signal.SIGUSR1)
Вы можете использовать этот механизм, чтобы прекратить после того, как тайм-аут период.
Вы могли бы сделать
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), ...)
с использованием асинхронного процесса API витой в.
Для 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
питона подпроцесс автоматического тайм-аут не встроен, так что вы будете иметь, чтобы построить свой собственный.
Это работает для меня на Ubuntu 12.10 работает Python 2.7.3
Положите это в файл с именем 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 секунды (это не произойдет), процесс прекратится.
[email protected]:~$ python test.py
[email protected]:~$
Существует три секунды между тем, когда процесс выполняется, и он завершается.
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).
Я не тестировал в окнах; но, помимо обновления команды example, я думаю, что она должна работать, поскольку я не нашел в документации ничего, что говорит о том, что thread.join или process.terminate не поддерживается.
С Python 3.3 в модуле подпроцесса также есть аргумент timeout
.
Да, https://pypi.python.org/pypi/python-subprocess2 продлит модуль POPEN с двумя дополнительными функциями,
Popen.waitUpTo(timeout=seconds)
Это будет ждать до acertain количества секунд для завершения процесса, в противном случае возвращает None
также,
Popen.waitOrTerminate
Это будет ждать до точки, а затем вызвать .terminate(), а затем .kill(), один orthe другой или какой-либо комбинации обоих, см документацию для получения полной информации:
связанные: [подпроцесс с таймаутом] (http://stackoverflow.com/q/1191374/4279) – jfs