web-dev-qa-db-fra.com

Comprendre Popen.communiquer

J'ai un script nommé 1st.py qui crée un REPL (read-eval-print-loop):

print "Something to print"
while True:
    r = raw_input()
    if r == 'n':
        print "exiting"
        break
    else:
        print "continuing"

J'ai ensuite lancé 1st.py avec le code suivant:

p = subprocess.Popen(["python","1st.py"], stdin=PIPE, stdout=PIPE)

Et puis essayé ceci:

print p.communicate()[0]

Il a échoué, fournissant cette traceback:

Traceback (most recent call last):
  File "1st.py", line 3, in <module>
    r = raw_input()
EOFError: EOF when reading a line

Pouvez-vous expliquer ce qui se passe ici s'il vous plaît? Lorsque j'utilise p.stdout.read(), il se bloque pour toujours.

50
Black_Hat

.communicate() écrit les entrées (il n'y a pas d'entrée dans ce cas, il ferme donc simplement le stdin du sous-processus pour indiquer au sous-processus qu'il n'y a plus d'entrées), lit toutes les sorties et attend la fin du sous-processus.

L'exception EOFError est générée dans le processus enfant par raw_input() (les données attendues mais ont reçu EOF (pas de données)).

p.stdout.read() est suspendu pour toujours car il essaie de lire la sortie all de l'enfant en même temps que l'enfant attend l'entrée (raw_input()) qui provoque un blocage.

Pour éviter le blocage, vous devez lire/écrire de manière asynchrone (par exemple, en utilisant des fils ou sélectionnez) ou savoir exactement quand et combien de lecture/écriture, par exemple :

from subprocess import PIPE, Popen

p = Popen(["python", "-u", "1st.py"], stdin=PIPE, stdout=PIPE, bufsize=1)
print p.stdout.readline(), # read the first line
for i in range(10): # repeat several times to show that it works
    print >>p.stdin, i # write input
    p.stdin.flush() # not necessary in this case
    print p.stdout.readline(), # read output

print p.communicate("n\n")[0], # signal the child to exit,
                               # read the rest of the output, 
                               # wait for the child to exit

Remarque: le code est très fragile si les opérations de lecture/écriture ne sont pas synchronisées. ça bloque.

Méfiez-vous des problème de mise en mémoire tampon de blocs (ici, le problème est résolu en utilisant "-u" qui désactive la mise en mémoire tampon pour stdin, stdout dans l'enfant ).

bufsize=1 fait en sorte que les tuyaux soient mis en tampon de lignes du côté parent .

44
jfs

Ne pas utiliser communiquer (input = ""). Il écrit les entrées dans le processus, ferme son stdin et lit ensuite toutes les sorties.

Fais-le comme ça:

p=subprocess.Popen(["python","1st.py"],stdin=PIPE,stdout=PIPE)

# get output from process "Something to print"
one_line_output = p.stdout.readline()

# write 'a line\n' to the process
p.stdin.write('a line\n')

# get output from process "not time to break"
one_line_output = p.stdout.readline() 

# write "n\n" to that process for if r=='n':
p.stdin.write('n\n') 

# read the last output from the process  "Exiting"
one_line_output = p.stdout.readline()

Que feriez-vous pour supprimer l'erreur:

all_the_process_will_tell_you = p.communicate('all you will ever say to this process\nn\n')[0]

Mais puisque communication ferme les variables stdout et stdin et stderr, vous ne pouvez ni lire ni écrire après avoir appelé communiquer.

19
User

Votre deuxième bit de code commence le premier bit de code en tant que sous-processus avec une entrée et une sortie canalisées. Il ferme ensuite son entrée et essaie de lire sa sortie.

Le premier bit de code tente de lire à partir de l'entrée standard, mais le processus qui l'a démarré a fermé son entrée standard, de sorte qu'il atteint immédiatement une fin de fichier, que Python transforme en exception.

0
icktoofay