46 votes

Comment obtenir des informations en temps réel d'un sous-processus ? Popen en python (2.5)

J'aimerais utiliser le module de sous-processus de la manière suivante :

  1. créer un nouveau processus dont l'exécution peut prendre beaucoup de temps.
  2. capture stdout (ou stderr ou potentiellement les deux, ensemble ou séparément)
  3. Traiter les données du sous-processus à mesure qu'il entre, peut-être en déclenchant des événements pour chaque ligne reçue (en wxPython par exemple) ou simplement en les imprimant pour le moment.

J'ai créé des processus avec Popen, mais si j'utilise communicate(), les données me parviennent en une seule fois, une fois le processus terminé.

Si je crée un thread séparé qui fait un blocage readline() de myprocess.stdout (en utilisant stdout = subprocess.PIPE ) Je n'obtiens pas non plus de lignes avec cette méthode, jusqu'à ce que le processus se termine. (peu importe ce que j'ai défini comme bufsize)

Existe-t-il un moyen de gérer ce problème qui ne soit pas épouvantable et qui fonctionne bien sur plusieurs plateformes ?

8voto

Ryan Points 1041

Mise à jour avec un code qui semble ne pas fonctionner (sous Windows en tout cas)

class ThreadWorker(threading.Thread):
    def __init__(self, callable, *args, **kwargs):
        super(ThreadWorker, self).__init__()
        self.callable = callable
        self.args = args
        self.kwargs = kwargs
        self.setDaemon(True)

    def run(self):
        try:
            self.callable(*self.args, **self.kwargs)
        except wx.PyDeadObjectError:
            pass
        except Exception, e:
            print e

if __name__ == "__main__":
    import os
    from subprocess import Popen, PIPE

    def worker(pipe):
        while True:
            line = pipe.readline()
            if line == '': break
            else: print line

    proc = Popen("python subprocess_test.py", shell=True, stdin=PIPE, stdout=PIPE, stderr=PIPE)

    stdout_worker = ThreadWorker(worker, proc.stdout)
    stderr_worker = ThreadWorker(worker, proc.stderr)
    stdout_worker.start()
    stderr_worker.start()
    while True: pass

7voto

Douglas Leeder Points 29986

Stdout sera mis en mémoire tampon - donc vous n'obtiendrez rien jusqu'à ce que ce tampon soit rempli, ou que le sous-processus se termine.

Vous pouvez essayer de tirer la chasse stdout à partir du sous-processus, ou en utilisant stderr, ou en changeant stdout en mode non tamponné.

2voto

Lance Richardson Points 3566

Il semble que le problème pourrait être l'utilisation de la sortie en mémoire tampon par le sous-processus - si une quantité relativement faible de sortie est créée, elle pourrait être mise en mémoire tampon jusqu'à la sortie du sous-processus. Vous pouvez trouver quelques informations sur le contexte aquí :

2voto

exhuma Points 3619

Voici ce qui a marché pour moi :

cmd = ["./tester_script.bash"]
p = subprocess.Popen( cmd, shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE )
while p.poll() is None:
    out = p.stdout.readline()
    do_something_with( out, err )

Dans votre cas, vous pourriez essayer de passer une référence au sous-processus à votre fil de travail et effectuer l'interrogation dans ce fil. Je ne sais pas comment cela se passera lorsque deux threads interrogeront (et interagiront avec) le même sous-processus, mais cela pourrait fonctionner.

Notez également que le while p.poll() is None: est prévu tel quel. Faites pas remplacez-le par while not p.poll() dans le mot python 0 (le code de retour pour une terminaison réussie) est également pris en compte False .

1voto

Andres Buritica Points 86

Lisez un personnage à la fois : http://blog.thelinuxkid.com/2013/06/get-python-subprocess-output-without.html

import contextlib
import subprocess

# Unix, Windows and old Macintosh end-of-line
newlines = ['\n', '\r\n', '\r']
def unbuffered(proc, stream='stdout'):
    stream = getattr(proc, stream)
    with contextlib.closing(stream):
        while True:
            out = []
            last = stream.read(1)
            # Don't loop forever
            if last == '' and proc.poll() is not None:
                break
            while last not in newlines:
                # Don't loop forever
                if last == '' and proc.poll() is not None:
                    break
                out.append(last)
                last = stream.read(1)
            out = ''.join(out)
            yield out

def example():
    cmd = ['ls', '-l', '/']
    proc = subprocess.Popen(
        cmd,
        stdout=subprocess.PIPE,
        stderr=subprocess.STDOUT,
        # Make all end-of-lines '\n'
        universal_newlines=True,
    )
    for line in unbuffered(proc):
        print line

example()

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X