Tiempo de espera de subproceso de Python?

¿Hay algún argumento u opción para configurar un tiempo de espera para el método de subproceso.Popen de Python?

Algo como esto:

subprocess.Popen(['..'], ..., timeout=20) ?

Recomendaría echar un vistazo a la clase Timer en el módulo de subprocesos. Lo usé para implementar un tiempo de espera para un Popen.

Primero, crea una callback:

  def timeout( p ): if p.poll() is None: print 'Error: process taking too long to complete--terminating' p.kill() 

Luego abre el proceso:

  proc = Popen( ... ) 

Luego crea un temporizador que llamará a la callback y le pasará el proceso.

  t = threading.Timer( 10.0, timeout, [proc] ) t.start() t.join() 

En algún lugar más adelante en el progtwig, es posible que desee agregar la línea:

  t.cancel() 

De lo contrario, el progtwig python continuará ejecutándose hasta que el temporizador haya terminado de ejecutarse.

EDITAR: Me informaron que existe una condición de carrera para que el subproceso p pueda terminar entre las llamadas p.poll () y p.kill (). Creo que el siguiente código puede arreglar eso:

  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 

Aunque es posible que desee limpiar el control de excepciones para tratar específicamente solo la excepción particular que se produce cuando el subproceso ya ha finalizado normalmente.

subprocess.Popen no se bloquea, por lo que puedes hacer algo como esto:

 import time p = subprocess.Popen(['...']) time.sleep(20) if p.poll() is None: p.kill() print 'timed out' else: print p.communicate() 

Tiene el inconveniente de que siempre debe esperar al menos 20 segundos para que finalice.

 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 salida de esto debería ser:

 Thread started Process started Process finished Thread finished 0 Thread started Process started Terminating process Thread finished -15 

donde se puede ver que, en la primera ejecución, el proceso finalizó correctamente (código de retorno 0), mientras que en la segunda se terminó el proceso (código de retorno -15).

No he probado en windows; pero, aparte de actualizar el comando de ejemplo, creo que debería funcionar ya que no he encontrado en la documentación nada que diga que thread.join o process.terminate no sea compatible.

Podrías hacerlo

 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), ...) 

utilizando la API del proceso asíncrono de Twisted.

El auto-tiempo de espera de un subproceso de Python no está incorporado, por lo que tendrá que crear el suyo propio.

Esto me funciona en Ubuntu 12.10 ejecutando python 2.7.3

Pon esto en un archivo llamado 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() 

Guárdalo y ejecútalo.

 python test.py 

El comando sleep 20 tarda 20 segundos en completarse. Si no finaliza en 3 segundos (no lo hará), el proceso finaliza.

 el@apollo:~$ python test.py el@apollo:~$ 

Hay tres segundos entre el momento en que se ejecuta el proceso y se termina.

Desafortunadamente, no existe tal solución. Logré hacer esto usando un temporizador de hilos que se iniciaría junto con el proceso que lo mataría después del tiempo de espera, pero me encontré con algunos problemas de descriptor de archivos obsoletos debido a procesos zombie o algo así.

No, no hay tiempo fuera. Supongo que lo que estás buscando es matar el subproceso después de un tiempo. Ya que puede señalar el subproceso, también debería poder eliminarlo.

enfoque genérico para enviar una señal a subproceso:

 proc = subprocess.Popen([command]) time.sleep(1) print 'signaling child' sys.stdout.flush() os.kill(proc.pid, signal.SIGUSR1) 

Podría usar este mecanismo para terminar después de un período de tiempo de espera.

A partir de Python 3.3, también hay un argumento de timeout para las funciones de ayuda de locking en el módulo de subproceso.

https://docs.python.org/3/library/subprocess.html

Sí, https://pypi.python.org/pypi/python-subprocess2 extenderá el módulo Popen con dos funciones adicionales,

 Popen.waitUpTo(timeout=seconds) 

Esto esperará hasta un cierto número de segundos para que el proceso se complete, de lo contrario, devuelva Ninguno

además,

 Popen.waitOrTerminate 

Esto esperará hasta cierto punto y luego llamará a .terminate (), luego a .kill (), a uno u otro o a alguna combinación de ambos, consulte la documentación para obtener detalles completos:

http://htmlpreview.github.io/?https://github.com/kata198/python-subprocess2/blob/master/doc/subprocess2.html

Para Linux, puedes usar una señal. Esto depende de la plataforma, por lo que se requiere otra solución para Windows. Aunque puede funcionar con 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