[Recipe] Ejecutar aplicaciones capturando salida en tiempo real
Oswaldo Hernández
listas en soft-com.es
Jue Feb 14 15:31:23 CET 2008
Hola a todos,
Para los archivos de la lista aqui va corregida una funcion para ejecutar aplicaciones utilizando
subprocess.popen que desarrolle hace unos meses con la inestimable ayuda de esta lista.
La funcion ha sido testeado en windows y linux. El modulo incluye unos simples test de utilización.
# =============== runapp.py ===================================
#!/usr/bin/env python
#coding=utf-8
import subprocess
import threading
import re
def runapp(app, cbkStdOut = None, cbkStdErr = None, sh=False):
"""
Ejecuta una aplicacion en segundo plano capturando su salida en tiempo real
Parametros:
app = string con la aplicacion y parametros
cbkStdOut = funcion para captura de salida estandar en tiempo real
cbkStdErr = funcion para captura de salida error en tiempo real,
si no se provee es redirigido a stdout
sh = Ejecutar en shell del sistema,
necesario cuando los parametros de la aplicación incluyen wildcards (*, ?)
Devuelve el codigo de salida de la aplicación o la excepcion producida si no se puede ejecutar
Licencia: MIT
"""
# -- clases auxiliares --
class readpipe(threading.Thread):
""" Thread para lectura del pipe """
lck = threading.Lock()
def __init__(self, pipe, callback=None):
threading.Thread.__init__(self)
self.pipe = pipe
self.callback = callback
def run(self):
while 1:
msg = self.pipe.readline()
if not msg:
break
if self.callback:
# bloquear, ejecutar funcion y desbloquear
readpipe.lck.acquire()
self.callback(msg)
readpipe.lck.release()
# -- inicio --
if not cbkStdErr:
cbkStdErr = cbkStdOut
if not sh:
# la ejecucion CON shell precisa que app sea un str con el comando mas todos sus parametros
# la ejecucion SIN shell precisa que app sea una lista [comando, parametro, parametro, ...]
app = [p for p in re.split(" |(\".*?\")|(\'.*?\')", app) if p]
try:
pr = subprocess.Popen(app,
bufsize = 0,
stdout = subprocess.PIPE,
stderr = subprocess.PIPE,
shell = sh)
except Exception, e:
# fallo al ejecutar, comando incorrecto, ....
raise
# lanzar treads de captura
tout = readpipe(pr.stdout, cbkStdOut)
terr = readpipe(pr.stderr, cbkStdErr)
tout.start()
terr.start()
# esperar que finalice
pr.wait()
# esperar que finalizen los threads,
# en algunos casos se da el proceso por terminado pero todavia no se han cerrado
# los pipes y quedan datos en los buffers, hay que esperar que los threads
# terminen de capturar los datos
while tout.isAlive() or terr.isAlive():
pass
# devolver codigo de salida de la aplicacion
return pr.poll()
##############################
# test, ejemplos
##############################
if __name__ == "__main__":
import sys
def printstdout(msg):
print "Out capturado:%s" % (msg),
sys.stdout.flush()
def printstderr(msg):
print "Err capturado:%s" % (msg),
sys.stdout.flush()
def printlista(lista):
for l in lista:
print "\t%s" % l,
# -- capturar resultado en tiempo real --
cmd = "ping www.google.com"
ret = runapp(cmd, printstdout, printstderr)
if ret == 0:
print "%s OK " % cmd
else:
print "%s Falló con codigo %s" % (cmd, ret)
# -- ejecutar y guardar el resultado en una lista --
cmd = "ping www.google.com"
out = []
ret = runapp(cmd, out.append)
if ret == 0:
print "%s OK" % cmd
else:
print "%s Falló con codigo %s" % (cmd, ret)
print "Detalles:"
printlista(out)
# -- ejecutar en shell --
out = []
cmd = "dir *.*"
ret = runapp(cmd, out.append, sh=True)
if ret == 0:
print "%s OK" % cmd
else:
print "%s falló con codigo %s" % (cmd, ret)
printlista(out)
# -- ejecutar simple, no capturar salida ----
if runapp("ping dominio.que.no.responde.com") == 0:
print "OOPS, dominio.que.no.responde.com ¡¡Si responde!!"
else:
print "dominio.que.no.responde.com, no responde"
# ============== fin runapp.py =================================================
--
*****************************************
Oswaldo Hernández
oswaldo (@) soft-com (.) es
*****************************************
_______________________________________________
Lista de correo Python-es
http://listas.aditel.org/listinfo/python-es
FAQ: http://listas.aditel.org/faqpyes
Más información sobre la lista de distribución Python-es