Domanda

C'è un modo per fare una chiamata del sottoprocessa in Python "persistente"? Sto chiamando un programma che richiede un po 'di tempo per caricare più volte. Quindi sarebbe bello se potessi semplicemente lasciare quel programma aperto e comunicare con esso senza ucciderlo.

La versione dei cartoni animati della mia sceneggiatura Python sembra così:

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

Devo elaborare ogni testo separatamente, quindi unire tutto in un file di testo di grandi dimensioni ed elaborarlo una volta non è un'opzione.

Preferibilmente, se c'è un'opzione come questa

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)

Dove posso lasciare il processo aperto, lo apprezzerei davvero.

È stato utile?

Soluzione

Puoi usare myprocess.stdin.write() e myprocess.stdout.read() Per comunicare con il tuo sottoprocesso, devi solo stare attento a assicurarti di gestire il buffering correttamente per evitare che le chiamate bloccino.

Se l'output del tuo sottoprocesso è ben definito, dovresti essere in grado di comunicare in modo affidabile con esso usando il buffering di linea e myprocess.stdout.readline().

Ecco un esempio:

>>> 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

Un'alternativa a questo metodo per UNIX è mettere la maniglia del file in modalità non bloccante, che consentirà di chiamare funzioni come myprocess.stdout.read() e falli restituire i dati se presenti sono disponibili o sollevare un IOError Se non ci sono dati:

>>> 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

Questo ti permetterebbe di fare qualcosa del genere:

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

In questo esempio, validate_output() è una funzione che dovresti scrivere che ritorna True Se i dati che hai ricevuto finora sono tutti output che ti aspetti di ottenere.

Altri suggerimenti

È la chiamata a communicate() Questo sta uccidendo il tuo sottoprocesso. Secondo il Documentazione del sottoprocesso il communicate() Metodo lo farà:

Interagisci con il processo: inviare dati a stdin. Leggi i dati di StDout e Stderr, fino a raggiungere la fine del file. Attendere il processo di risoluzione.

Quello che vuoi fare è interagire direttamente con il POpen oggetto stdin e stdout Proprietà direttamente per comunicare con il sottoprocesso. Tuttavia, la documentazione consiglia di questo detto:

ATTENZIONE: Usa comunicare () anziché .stdin.Write, .stdout.read o .stderr.read per evitare i deadlock a causa di uno qualsiasi degli altri tubi del sistema operativo che riempiono e bloccano il processo del bambino.

Quindi devi implementare le tue soluzioni alternative per potenziali deadlock o sperare che qualcuno abbia scritto un Modulo di sottoprocesso asincrono per te.

Modificare: Ecco un rapido esempio di come si potrebbe usare il modulo di sottoprocesso asincrono:

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

Quando eseguo questo, stampe:

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 

Penso che tu stia cercando

myprocess.stdin.write(text)

È possibile creare un elenco di pipia e quindi chiamare comunicare su ciascun elemento in un altro ciclo. qualcosa come questo

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

In questo modo non dovrà aspettare fino a quando tutte le sorte sono iniziate

if os.name == 'nt':
 startupinfo = subprocess.STARTUPINFO()
 startupinfo.dwFlags |= subprocess._subprocess.STARTF_USESHOWWINDOW
 subprocess.call(os.popen(tempFileName), shell=True)
 os.remove(tempFileName)
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top