getoutput español python timeout pipe subprocess popen

español - subprocess python windows



Python subprocess timeout? (10)

¿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) ?



Desafortunadamente, no hay 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 similares.


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

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.


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


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.


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

Primero, crea una devolución de llamada:

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 cree un temporizador que llamará a la devolución de llamada 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 programa, es posible que desee agregar la línea:

t.cancel()

De lo contrario, el programa 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 manejo de excepciones para tratar específicamente solo la excepción particular que se produce cuando el subproceso ya ha finalizado normalmente.


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 un 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


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.


Un auto-tiempo de espera de subproceso de Python no está integrado, 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.


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.