Ho un programma Python che avvia i sottoprocessi usando Popen
e consuma il loro output quasi in tempo reale mentre viene prodotto. Il codice del loop relativo è:Rilevamento della fine del flusso su popen.stdout.readline
def run(self, output_consumer):
self.prepare_to_run()
popen_args = self.get_popen_args()
logging.debug("Calling popen with arguments %s" % popen_args)
self.popen = subprocess.Popen(**popen_args)
while True:
outdata = self.popen.stdout.readline()
if not outdata and self.popen.returncode is not None:
# Terminate when we've read all the output and the returncode is set
break
output_consumer.process_output(outdata)
self.popen.poll() # updates returncode so we can exit the loop
output_consumer.finish(self.popen.returncode)
self.post_run()
def get_popen_args(self):
return {
'args': self.command,
'shell': False, # Just being explicit for security's sake
'bufsize': 0, # More likely to see what's being printed as it happens
# Not guarantted since the process itself might buffer its output
# run `python -u` to unbuffer output of a python processes
'cwd': self.get_cwd(),
'env': self.get_environment(),
'stdout': subprocess.PIPE,
'stderr': subprocess.STDOUT,
'close_fds': True, # Doesn't seem to matter
}
Questa grande opera sulle macchine di produzione, ma sulla mia macchina dev, la chiamata a .readline()
si blocca quando alcuni sottoprocessi completa. Cioè, elaborerà con successo tutto l'output, compresa la riga di output finale che dice "processo completato", ma poi eseguirà nuovamente il sondaggio readline
e non tornerà mai più. Questo metodo si chiude correttamente sulla macchina di sviluppo per la maggior parte dei sottoprocessi che chiamo, ma non riesce costantemente ad uscire per uno script bash complesso che a sua volta chiama molti sottoprocessi.
Vale la pena notare che popen.returncode
viene impostato su un valore non None
(in genere 0
) molte righe prima della fine dell'output. Quindi non posso interrompere il ciclo quando è impostato, altrimenti perdo tutto ciò che viene sputato alla fine del processo e viene ancora memorizzato nel buffer in attesa di lettura. Il problema è che quando sto scaricando il buffer a quel punto, non posso dire quando sono alla fine perché l'ultima chiamata a readline()
si blocca. Chiama anche read()
. Chiamando lo read(1)
mi viene estratto l'ultimo carattere, ma si blocca anche dopo la linea finale. popen.stdout.closed
è sempre False
. Come posso dire quando sono alla fine?
Tutti i sistemi eseguono python 2.7.3 su Ubuntu 12.04LTS. FWIW, stderr
viene unito a stdout
utilizzando stderr=subprocess.STDOUT
.
Perché la differenza? Non riesce a chiudere stdout
per qualche motivo? I sottoprocessi potrebbero fare qualcosa per tenerlo aperto in qualche modo? Potrebbe essere perché sto avviando il processo da un terminale sulla mia scatola di sviluppo, ma in produzione è lanciato come demone tramite supervisord
? Questo cambierebbe il modo in cui i tubi vengono lavorati e, in caso affermativo, come li normalizzo?
non è il problema che stai leggendo una linea da un processo che non esiste più? –
Non credo. Se l'errore fosse così semplice, fallirebbe ovunque, sempre. – Leopd
Perché non puoi rompere semplicemente con '' not outdata'' – sotapme