Gibt es ein Argument oder Optionen, um ein Timeout für Pythons subprocess.Popen-Methode einzurichten?Python-Subprozess-Timeout?
Etwas wie folgt aus:
subprocess.Popen(['..'], ..., timeout=20)
?
Gibt es ein Argument oder Optionen, um ein Timeout für Pythons subprocess.Popen-Methode einzurichten?Python-Subprozess-Timeout?
Etwas wie folgt aus:
subprocess.Popen(['..'], ..., timeout=20)
?
Ich würde empfehlen, einen Blick auf die Timer class im Threading-Modul. Ich benutzte es, um ein Timeout für einen Popen zu implementieren.
Erstellen Sie zunächst einen Rückruf:
def timeout(p):
if p.poll() is None:
print 'Error: process taking too long to complete--terminating'
p.kill()
dann den Prozess öffnen:
proc = Popen(...)
Dann einen Timer erstellen, die den Rückruf Passieren der Prozess wird es nennen.
t = threading.Timer(10.0, timeout, [proc])
t.start()
t.join()
Irgendwo später im Programm, können Sie die Zeile hinzufügen möchten:
t.cancel()
Andernfalls wird das Python-Programm läuft weiter, bis der Timer läuft beendet.
EDIT: Ich wurde darauf hingewiesen, dass es eine Race-Bedingung gibt, dass der Subprozess p zwischen den p.poll() und p.kill() Aufrufen beendet werden kann. Ich glaube, der folgende Code das in Ordnung bringen kann:
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
Obwohl Sie die Ausnahme reinigen möchten speziell behandeln Umgang nur die besondere Ausnahme, die auftritt, wenn der Teilprozess bereits normal beendet hat.
Dieser Code hat eine Racebedingung. –
Mike, könnten Sie einen Fix oben erarbeiten oder bearbeiten? Ich habe einen ähnlichen Code mehrmals verwendet, und wenn es ein Problem gibt, möchte ich es definitiv beheben. – dvntehn00bz
'print 'Fehler: Prozess dauert zu lange, um abzuschließen - termining'' kann auch dann ausgeführt werden, wenn es eine Lüge ist und Ihr Prozess endet, ohne dass Sie ihn töten (weil es in den Momenten zwischen Ihren' Poll'- und' Kill'-Aufrufen geschieht). –
subprocess.Popen nicht blockiert, so dass Sie etwas tun können:
import time
p = subprocess.Popen(['...'])
time.sleep(20)
if p.poll() is None:
p.kill()
print 'timed out'
else:
print p.communicate()
Es hat den Nachteil, dass Sie immer mindestens 20 Sekunden warten muss, bis es beendet wird.
Dies würde diesen Prozess für 20 Sekunden einfrieren. Ist das akzeptabel? –
besiegt das irgendwie den Punkt der Verwendung eines Unterprozesses nicht? – aaronasterling
scheint es ist! Ich denke, das ist eine kleine Unannehmlichkeit der Verwendung von Subprozess. – sultan
Leider gibt es keine solche Lösung. Ich schaffte es, dies mit einem Thread-Timer zu tun, der zusammen mit dem Prozess starten würde, der ihn nach dem Timeout beenden würde, aber ich stieß auf einige veraltete Datei-Deskriptor-Probleme aufgrund von Zombie-Prozessen oder ähnlichem.
+1 Das wäre meine Lösung. – aaronasterling
Ich habe ein solches Problem auch mit Dateideskriptoren bei Zombie-Prozessen erlebt. – sultan
Sultan. Es sollte möglich sein, diese zu korrigieren. Es gelang mir schließlich, meine Bewerbung in etwas Machbares zu verwandeln, aber es war nicht allgemein genug, um es zu veröffentlichen. –
Nein, es gibt keine Auszeit. Ich denke, was Sie suchen, ist, den Unterprozess nach einiger Zeit zu beenden. Da Sie den Unterprozess signalisieren können, sollten Sie ihn auch töten können.
generischer Ansatz ein Signal an subprocess senden an:
proc = subprocess.Popen([command])
time.sleep(1)
print 'signaling child'
sys.stdout.flush()
os.kill(proc.pid, signal.SIGUSR1)
Sie können diesen Mechanismus verwenden, nach einer Auszeit Frist zu kündigen.
Sie könnten
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), ...)
mit Verdrehte der asynchronen Prozess API tun.
Für Linux können Sie ein Signal verwenden. Dies ist plattformabhängig, daher ist eine andere Lösung für Windows erforderlich. Es kann jedoch mit Mac funktionieren.
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
Ein Python subprocess Auto-Timeout wird nicht gebaut, so dass Sie Ihre eigenen haben werden zu bauen.
Dies funktioniert für mich auf Ubuntu 12.10 Python 2.7.3
Setzen Sie diese in einer Datei 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()
Speichern nannte es ausgeführt wird, und führen Sie es:
python test.py
Der Befehl sleep 20
dauert 20 Sekunden. Wenn es nicht in 3 Sekunden endet (wird es nicht), dann wird der Prozess beendet.
Zwischen dem Ausführen des Prozesses und dem Beenden des Prozesses liegen drei Sekunden.
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)
Der Ausgang dieses sollte sein:
Thread started
Process started
Process finished
Thread finished
0
Thread started
Process started
Terminating process
Thread finished
-15
wo es, dass in der ersten Ausführung zu sehen ist, während der Prozess korrekt beendet (Rückkehrcode 0), die in der zweiten der Prozess wurde beendet (Rückgabecode -15).
Ich habe nicht in Windows getestet; Aber abgesehen von der Aktualisierung des Beispielbefehls sollte es funktionieren, da ich in der Dokumentation nichts gefunden habe, was besagt, dass thread.join oder process.terminate nicht unterstützt wird.
Ab Python 3.3 gibt es auch ein timeout
Argument für die blockierenden Hilfsfunktionen im Subprozessmodul.
Ja, https://pypi.python.org/pypi/python-subprocess2 den Popen Modul mit zwei zusätzlichen Funktionen erweitern,
Popen.waitUpTo(timeout=seconds)
Diese wartet bis werden Anzahl der Sekunden für den Prozess acertain abzuschließen, sonst return None
auch
Popen.waitOrTerminate
Dies wird zu einem Punkt warten, und dann rufen .terminate(), dann .kill(), eine oderdie andere oder eine Kombination von beiden, siehe Dokumentation für weitere Informationen:
bezogen werden: [subprocess mit Timeout] (http://stackoverflow.com/q/1191374/4279) – jfs