Ninguna de las respuestas aquí abordó todas mis necesidades.
- Sin hilos para stdout (sin colas, etc.)
- Sin bloqueo ya que necesito verificar si hay otras cosas sucediendo
- Use PIPE como necesitaba para hacer varias cosas, por ejemplo, salida de flujo, escribir en un archivo de registro y devolver una copia de cadena de la salida.
Un poco de historia: estoy usando un ThreadPoolExecutor para administrar un grupo de subprocesos, cada uno de los cuales ejecuta un subproceso y los ejecuta simultáneamente. (En Python2.7, pero esto también debería funcionar en la versión 3.x más reciente). No quiero usar subprocesos solo para la recopilación de salida, ya que quiero tantos disponibles como sea posible para otras cosas (un grupo de 20 procesos usaría 40 subprocesos solo para ejecutarse; 1 para el subproceso de proceso y 1 para stdout ... y más si quieres stderr supongo)
Estoy eliminando muchas excepciones y tal aquí, así que esto se basa en el código que funciona en la producción. Espero no haberlo arruinado en copiar y pegar. Además, ¡los comentarios son bienvenidos!
import time
import fcntl
import subprocess
import time
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
# Make stdout non-blocking when using read/readline
proc_stdout = proc.stdout
fl = fcntl.fcntl(proc_stdout, fcntl.F_GETFL)
fcntl.fcntl(proc_stdout, fcntl.F_SETFL, fl | os.O_NONBLOCK)
def handle_stdout(proc_stream, my_buffer, echo_streams=True, log_file=None):
"""A little inline function to handle the stdout business. """
# fcntl makes readline non-blocking so it raises an IOError when empty
try:
for s in iter(proc_stream.readline, ''): # replace '' with b'' for Python 3
my_buffer.append(s)
if echo_streams:
sys.stdout.write(s)
if log_file:
log_file.write(s)
except IOError:
pass
# The main loop while subprocess is running
stdout_parts = []
while proc.poll() is None:
handle_stdout(proc_stdout, stdout_parts)
# ...Check for other things here...
# For example, check a multiprocessor.Value('b') to proc.kill()
time.sleep(0.01)
# Not sure if this is needed, but run it again just to be sure we got it all?
handle_stdout(proc_stdout, stdout_parts)
stdout_str = "".join(stdout_parts) # Just to demo
Estoy seguro de que aquí se agrega una sobrecarga, pero no es una preocupación en mi caso. Funcionalmente hace lo que necesito. Lo único que no he resuelto es por qué esto funciona perfectamente para los mensajes de registro, pero veo que algunos print
mensajes aparecen más tarde y de una vez.