Nenhuma das respostas aqui abordou todas as minhas necessidades.
- Sem threads para stdout (sem filas etc.)
- Sem bloqueio, pois preciso verificar se há outras coisas acontecendo
- Use o PIPE conforme necessário para fazer várias coisas, por exemplo, saída de fluxo, gravação em um arquivo de log e retorno de uma cópia em seqüência da saída.
Um pouco de experiência: estou usando um ThreadPoolExecutor para gerenciar um pool de threads, cada um iniciando um subprocesso e executando-os simultaneamente. (No Python2.7, mas isso também deve funcionar na versão 3.x mais recente). Eu não quero usar threads apenas para reunir a saída, pois quero o maior número possível de outras coisas (um pool de 20 processos usaria 40 threads apenas para executar; 1 para o processo e 1 para stdout ... e mais se você quiser stderr eu acho)
Estou retirando muitas exceções e outras aqui, então isso se baseia no código que funciona na produção. Espero que não estraguei tudo na cópia e colar. Além disso, feedback muito bem-vindo!
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
Tenho certeza de que há custos adicionais sendo adicionados aqui, mas isso não é uma preocupação no meu caso. Funcionalmente, ele faz o que eu preciso. A única coisa que não resolvi é por que isso funciona perfeitamente para mensagens de log, mas vejo algumas print
mensagens aparecerem mais tarde e de uma só vez.