Question

Existe-t-il un moyen de rendre un appel de sous-processus en python « persistant » ?J'appelle un programme qui met du temps à se charger plusieurs fois.Ce serait donc formidable si je pouvais simplement laisser ce programme ouvert et communiquer avec lui sans le tuer.

La version dessin animé de mon script python ressemble à ceci :

for text in textcollection:
    myprocess = subprocess.Popen(["myexecutable"],
                stdin = subprocess.PIPE, stdout = subprocess.PIPE,
                stderr = None)
    myoutputtext, err = myprocess.communicate(input=text)

Je dois traiter chaque texte séparément, donc le regrouper dans un seul gros fichier texte et le traiter une seule fois n'est pas une option.

De préférence, s'il existe une option comme celle-ci

myprocess = subprocess.Popen(["myexecutable"],
            stdin = subprocess.PIPE, stdout = subprocess.PIPE,
            stderr = None)    for text in textcollection:
for text in textcollection:
    myoutputtext, err = myprocess.communicate(input=text)

où je peux laisser le processus ouvert, je l'apprécierais vraiment.

Était-ce utile?

La solution

Vous pouvez utiliser myprocess.stdin.write() et myprocess.stdout.read() pour communiquer avec votre sous-processus, il vous suffit de faire attention à vous assurer que vous gérez correctement la mise en mémoire tampon pour éviter que vos appels ne se bloquent.

Si la sortie de votre sous-processus est bien définie, vous devriez pouvoir communiquer de manière fiable avec elle en utilisant la mise en mémoire tampon de ligne et myprocess.stdout.readline().

Voici un exemple:

>>> p = subprocess.Popen(['cat'], bufsize=1, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
>>> p.stdin.write('hello world\n')
>>> p.stdout.readline()
'hello world\n'
>>> p.stdout.readline()        # THIS CALL WILL BLOCK

Une alternative à cette méthode pour Unix est de mettre le handle de fichier en mode non bloquant, ce qui vous permettra d'appeler des fonctions comme myprocess.stdout.read() et demandez-lui de renvoyer des données s'il y en a, ou de lever un IOError s'il n'y a pas de données :

>>> p = subprocess.Popen(['cat'], stdin=subprocess.PIPE, stdout=subprocess.PIPE)
>>> import fcntl, os
>>> fcntl.fcntl(p.stdout.fileno(), fcntl.F_SETFL, os.O_NONBLOCK)
0
>>> p.stdout.read()         # raises an exception instead of blocking
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IOError: [Errno 11] Resource temporarily unavailable

Cela vous permettrait de faire quelque chose comme ceci :

fcntl.fcntl(p.stdout.fileno(), fcntl.F_SETFL, os.O_NONBLOCK)
for text in textcollection:
    myprocess.stdin.write(text + '\n')
    while True:
        myoutputtext = ''
        try:
            myoutputtext += myprocess.stdout.read()
        except IOError:
            pass
        if validate_output(myoutputtext):
            break
        time.sleep(.1)    # short sleep before attempting another read

Dans cet exemple, validate_output() est une fonction que vous auriez besoin d'écrire et qui renvoie True si les données que vous avez reçues jusqu'à présent correspondent à la totalité des résultats que vous espérez obtenir.

Autres conseils

C'est l'appel à communicate() cela tue votre sous-processus.Selon le documentation des sous-processus le communicate() la méthode :

Interagissez avec le processus :Envoyez les données à stdin.Lit les données depuis stdout et stderr jusqu'à ce que la fin du fichier soit atteinte.Attendez la fin du processus.

Ce que vous voulez faire, c'est interagir directement avec le POpen objets stdin et stdout propriétés directement pour communiquer avec le sous-processus.Cependant, la documentation déconseille ce dicton :

Avertissement:Utilisez communicate() plutôt que .stdin.write, .stdout.read ou .stderr.read pour éviter les blocages dus au remplissage et au blocage du processus enfant par l'un des autres tampons de canal du système d'exploitation.

Vous devez donc soit mettre en œuvre vos propres solutions de contournement pour les blocages potentiels, soit espérer que quelqu'un ait écrit un module de sous-processus asynchrone pour toi.

Modifier: Voici un exemple rapide de la façon dont le module de sous-processus asynchrone pourrait être utilisé :

import asyncsubprocess

textcollection = ['to', 'be', 'or', 'not', 'to be', 'that is the', 'question']

myprocess = asyncsubprocess.Popen(["cat"],
     stdin = asyncsubprocess.PIPE,
     stdout = asyncsubprocess.PIPE,
     stderr = None)

for text in textcollection:
    bytes_sent, myoutput, err = myprocess.listen(text)
    print text, bytes_sent, myoutput, err

Quand je lance ceci, il affiche :

to 2 to 
be 2 be 
or 2 or 
not 3 not 
to be 5 to be 
that is the 11 that is the 
question 8 question 

Je pense que vous recherchez

myprocess.stdin.write(text)

Vous pouvez créer une liste de popens puis appeler la communication sur chaque élément d'une autre boucle. quelque chose comme ça

processes=[]
for text in textcollection:
    myprocess = subprocess.Popen(["myexecutable"],
                stdin = subprocess.PIPE, stdout = subprocess.PIPE,
                stderr = None)
    myprocess.stdin.write(text)
    processes.append(myprocess)

for proc in processes:
    myoutput, err=proc.communicate()
    #do something with the output here

De cette façon, il n'aura pas besoin d'attendre après que tout le monde popel ait commencé

if os.name == 'nt':
 startupinfo = subprocess.STARTUPINFO()
 startupinfo.dwFlags |= subprocess._subprocess.STARTF_USESHOWWINDOW
 subprocess.call(os.popen(tempFileName), shell=True)
 os.remove(tempFileName)
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top