Czy jest jakiś argument lub opcje do ustawienia limitu czasu dla podprocesu Pythona?Timeout podprocesu w języku Python?
coś takiego:
subprocess.Popen(['..'], ..., timeout=20)
?
Czy jest jakiś argument lub opcje do ustawienia limitu czasu dla podprocesu Pythona?Timeout podprocesu w języku Python?
coś takiego:
subprocess.Popen(['..'], ..., timeout=20)
?
Radzę rzucić okiem na Timer class w module wątków. Użyłem go do wprowadzenia limitu czasu dla Popen.
Najpierw utwórz callback:
def timeout(p):
if p.poll() is None:
print 'Error: process taking too long to complete--terminating'
p.kill()
następnie otworzyć proces:
proc = Popen(...)
następnie utworzyć zegar, który nazywamy zwrotnego przechodzącego proces do niego.
t = threading.Timer(10.0, timeout, [proc])
t.start()
t.join()
Gdzieś później w programie, może chcesz dodać linię:
t.cancel()
W przeciwnym razie program python będzie biec aż timer zakończy działanie.
EDYCJA: Zostałem poinformowany, że istnieje warunek wyścigu, że podproces p może zakończyć się między wywołaniami p.poll() i p.kill(). Wierzę, że następujący kod można ustalić, że:
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
Choć może chcesz wyczyścić wyjątek obsługi specjalnie obsługiwać tylko szczególny wyjątek, który występuje, gdy podproces został już zakończony normalnie.
Ten kod ma stan wyścigu. –
Mike, czy mógłbyś rozwinąć lub poprawić powyższą poprawkę? Kilka razy użyłem podobnego kodu, a jeśli jest jakiś problem, zdecydowanie chciałbym go naprawić. – dvntehn00bz
"print" Błąd: proces trwa zbyt długo, aby zakończyć - zakończenie "" może zostać uruchomione, nawet jeśli jest to kłamstwo, a proces kończy się bez zabicia go (ponieważ robi to w momentach między połączeniami "poll" i "kill"). –
subprocess.Popen nie blokuje więc można zrobić coś takiego:
import time
p = subprocess.Popen(['...'])
time.sleep(20)
if p.poll() is None:
p.kill()
print 'timed out'
else:
print p.communicate()
Ma wadę, że należy zawsze odczekać co najmniej 20 sekund na to, aby zakończyć.
To spowodowałoby zatrzymanie tego procesu przez 20 sekund. Czy to jest do przyjęcia? –
nie pokonuje tego rodzaju podprocesu? – aaronasterling
wydaje się, że jest! Myślę, że to jest mała niedogodność w używaniu podprocesu. – sultan
Niestety, nie ma takiego rozwiązania. Udało mi się to zrobić za pomocą nagwintowanego timera, który uruchomiłbym wraz z procesem, który zabiłby go po przekroczeniu limitu czasu, ale wpadłem na kilka starych problemów deskryptorów plików z powodu procesów zombie lub niektórych takich.
+1 To byłoby moje rozwiązanie. – aaronasterling
Doświadczyłem też takiego problemu z deskryptorami plików w procesach zombie. – sultan
Sultan. Powinno być możliwe ich skorygowanie. Udało mi się wreszcie wypolerować moją aplikację w coś praktycznego, ale nie była ona wystarczająco ogólna, aby ją opublikować. –
Nie ma wolnego czasu. Sądzę, że to, czego szukasz, to zabicie podprocesora po pewnym czasie. Ponieważ jesteś w stanie sygnalizować podproces, powinieneś być w stanie go zabić.
ogólne podejście do wysyłania sygnału do podproces:
proc = subprocess.Popen([command])
time.sleep(1)
print 'signaling child'
sys.stdout.flush()
os.kill(proc.pid, signal.SIGUSR1)
Można użyć tego mechanizmu, aby zakończyć po pewnym czasie poza okres.
Można zrobić
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), ...)
użyciu skręconych jest procesem asynchronicznym API.
W systemie Linux można użyć sygnału. Jest to zależne od platformy, więc inne rozwiązanie jest wymagane w systemie Windows. Może działać z komputerem 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
pytona podproces auto-timeout nie jest wbudowane, więc masz zamiar zbudować własną.
Działa to na mnie działa na Ubuntu 12.10 Python 2.7.3
umieścić to w pliku o nazwie 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()
zapisz go i uruchom go:
python test.py
Komenda sleep 20
trwa 20 sekund. Jeśli nie zakończy się w ciągu 3 sekund (nie nastąpi), proces zostanie zakończony.
[email protected]:~$ python test.py
[email protected]:~$
Między uruchomieniem procesu i trzema sekundami jest przerwany.
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)
Wyjście to powinno być:
Thread started
Process started
Process finished
Thread finished
0
Thread started
Process started
Terminating process
Thread finished
-15
, gdzie można zauważyć, że w pierwszym wykonaniu, proces zakończeniu prawidłowo (kod powrotu 0), natomiast w drugim z proces został zakończony (kod powrotu -15).
Nie testowałem w systemie Windows; ale oprócz aktualizacji przykładowego polecenia, myślę, że powinno działać, ponieważ nie znalazłem w dokumentacji niczego, co mówi, że thread.join lub process.terminate nie jest obsługiwane.
Od wersji 3.3 Pythona istnieje również argument o blokujących funkcjach pomocniczych w module podprocesu.
Tak https://pypi.python.org/pypi/python-subprocess2 przedłuży moduł POPEN z dwoma dodatkowymi funkcjami,
Popen.waitUpTo(timeout=seconds)
To będzie czekać aż do acertain liczbę sekund na zakończenie procesu, w przeciwnym razie zwraca None
także,
Popen.waitOrTerminate
To będzie czekać aż do punktu, a następnie zadzwonić .terminate(), a następnie .kill(), jeden lub kilka innych Orthe połączenie obu, patrz Dokumentacja dla pełnych szczegółów:
pokrewnych: [podproces z limitem czasu] (http://stackoverflow.com/q/1191374/4279) – jfs