Aucune des réponses ici ne répond à tous mes besoins.
- Pas de threads pour stdout (pas de files d'attente, etc.)
- Non bloquant car je dois vérifier que d'autres choses se passent
- Utilisez PIPE car j'avais besoin de faire plusieurs choses, par exemple la sortie de flux, écrire dans un fichier journal et renvoyer une copie de chaîne de la sortie.
Un peu de contexte: j'utilise un ThreadPoolExecutor pour gérer un pool de threads, chacun lançant un sous-processus et les exécutant simultanément. (En Python2.7, mais cela devrait également fonctionner dans la version 3.x plus récente). Je ne veux pas utiliser de threads uniquement pour la collecte de sortie car je veux autant de disponibles que possible pour d'autres choses (un pool de 20 processus utiliserait 40 threads juste pour s'exécuter; 1 pour le thread de processus et 1 pour stdout ... et plus si vous voulez stderr je suppose)
Je retire beaucoup d'exceptions et telles ici, donc cela est basé sur du code qui fonctionne en production. J'espère que je ne l'ai pas gâché dans le copier-coller. De plus, les commentaires sont les bienvenus!
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
Je suis sûr que des frais généraux sont ajoutés ici, mais ce n'est pas un problème dans mon cas. Fonctionnellement, il fait ce dont j'ai besoin. La seule chose que je n'ai pas résolue est la raison pour laquelle cela fonctionne parfaitement pour les messages de journal, mais je vois certains print
messages apparaître plus tard et en même temps.