Existe-t-il des arguments ou des options pour configurer un délai d'attente pour la méthode subprocess.Popen de Python?Délai d'expiration du sous-processus Python?
Quelque chose comme ceci:
subprocess.Popen(['..'], ..., timeout=20)
?
Existe-t-il des arguments ou des options pour configurer un délai d'attente pour la méthode subprocess.Popen de Python?Délai d'expiration du sous-processus Python?
Quelque chose comme ceci:
subprocess.Popen(['..'], ..., timeout=20)
?
Je vous conseille de regarder le Timer class dans le module de filetage. Je l'ai utilisé pour mettre en place un délai d'attente pour un Popen.
Tout d'abord, créez un rappel:
def timeout(p):
if p.poll() is None:
print 'Error: process taking too long to complete--terminating'
p.kill()
Ouvrez ensuite le processus:
proc = Popen(...)
Ensuite, créez une minuterie qui appellera la fonction de rappel qui passe le processus à lui.
t = threading.Timer(10.0, timeout, [proc])
t.start()
t.join()
Quelque part plus tard dans le programme, vous pouvez ajouter la ligne:
t.cancel()
Sinon, le programme Python en cours d'exécution jusqu'à ce que la garder minuterie a fini.
EDIT: J'ai été informé qu'il existe une condition de concurrence dans laquelle le sous-processus p peut se terminer entre les appels p.poll() et p.kill(). Je crois que le code suivant peut résoudre ce problème:
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
Bien que vous pouvez nettoyer l'exception de manutention pour traiter spécifiquement que l'exception particulière qui se produit lorsque le sous-processus est déjà terminé normalement.
Ce code a une condition de concurrence. –
Mike, pourriez-vous élaborer ou modifier un correctif ci-dessus? J'ai utilisé un code similaire plusieurs fois, et s'il y a un problème, je voudrais vraiment le réparer. – dvntehn00bz
'print 'Erreur: le processus prend trop de temps - terminating'' peut s'exécuter même si c'est un mensonge et que votre processus se termine sans que vous le détruisiez (parce qu'il le fait dans les moments entre vos appels' poll' et 'kill'). –
subprocess.Popen ne bloque pas que vous pouvez faire quelque chose comme ceci:
import time
p = subprocess.Popen(['...'])
time.sleep(20)
if p.poll() is None:
p.kill()
print 'timed out'
else:
print p.communicate()
Il a un inconvénient que vous devez toujours attendre au moins 20 secondes à la fin.
Cela gelerait ce processus pendant 20 secondes. Est-ce acceptable? –
cela ne va-t-il pas à l'encontre de l'utilisation d'un sous-processus? – aaronasterling
semble que c'est! Je pense que c'est un petit inconvénient d'utiliser un sous-processus. – sultan
Malheureusement, il n'y a pas une telle solution. J'ai réussi à faire cela en utilisant une minuterie filetée qui se lancerait avec le processus qui le tuerait après le délai d'attente, mais j'ai rencontré des problèmes de descripteur de fichier périmés à cause de processus de zombies ou autres.
+1 Ce serait ma solution. – aaronasterling
J'ai aussi rencontré un tel problème avec les descripteurs de fichiers sur les processus zombies. – sultan
Sultan. Il devrait être possible de les corriger. J'ai réussi à enfin polir mon application en quelque chose de réalisable, mais ce n'était pas assez générique pour être publié. –
Il n'y a pas de temps mort. Je suppose que ce que vous cherchez est de tuer le sous-processus après un certain temps. Puisque vous êtes capable de signaler le sous-processus, vous devriez pouvoir le tuer aussi.
approche générique pour envoyer un signal à sous-processus:
proc = subprocess.Popen([command])
time.sleep(1)
print 'signaling child'
sys.stdout.flush()
os.kill(proc.pid, signal.SIGUSR1)
Vous pouvez utiliser ce mécanisme pour se terminer après une période de temporisation.
Vous pouvez faire
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), ...)
en utilisant l'API de processus asynchrone de Twisted.
Pour Linux, vous pouvez utiliser un signal. Cela dépend de la plateforme, donc une autre solution est requise pour Windows. Cela peut fonctionner avec 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
Un auto-délai subprocess python n'est pas construit, donc vous allez devoir construire votre propre.
Cela fonctionne pour moi sur Ubuntu 12.10 python en cours d'exécution 2.7.3
Mettre cela dans un fichier appelé 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()
Enregistrer, et l'exécuter:
python test.py
La commande sleep 20
prend 20 secondes pour se terminer. S'il ne se termine pas dans 3 secondes (ce ne sera pas le cas), le processus est terminé.
[email protected]:~$ python test.py
[email protected]:~$
Il s'écoule trois secondes entre l'exécution du processus et sa fin.
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)
La sortie de ce devrait être:
Thread started
Process started
Process finished
Thread finished
0
Thread started
Process started
Terminating process
Thread finished
-15
où il peut être vu que, dans la première exécution, le processus terminé correctement (code de retour 0), tandis que le dans le second la le processus a été terminé (code de retour -15).
Je n'ai pas testé dans Windows; mais, mis à part la mise à jour de la commande example, je pense que cela devrait fonctionner puisque je n'ai trouvé dans la documentation rien qui dise que thread.join ou process.terminate n'est pas supporté.
À partir de Python 3.3, il existe également un argument timeout
pour les fonctions d'aide au blocage dans le module de sous-processus.
Oui, https://pypi.python.org/pypi/python-subprocess2 étendra le module Popen avec deux fonctions supplémentaires,
Popen.waitUpTo(timeout=seconds)
Cela attendra jusqu'à acertain nombre de secondes pour que le processus soit terminé, retournez autrement Aucun
aussi,
Popen.waitOrTerminate
Cela attendra jusqu'à un certain point, puis appelez .terminate(), puis .kill(), l'un ou l'autre oula une combinaison des deux, voir docs pour plus de détails:
liés: [subprocess avec timeout] (http://stackoverflow.com/q/1191374/4279) – jfs