2016-04-15 4 views
2

Wenn mit der alten Schule subprocess.Popen() API zwei Prozesse gestartet wird, kann ich leicht anschließen Standard aus von einem Prozess zur Standard in eines anderen Prozess, eine Pipeline zu schaffen, in die gleiche Weise wie ein UNIX-Shell tun wird, wenn Befehle mit | Verbindungs:Connect zwei Prozesse gestartet mit asyncio.subprocess.create_subprocess_exec()

from subprocess import Popen, PIPE 

process_1 = Popen(['ls'], stdout = PIPE) 
process_2 = Popen(['wc'], stdin = process_1.stdout) 

process_1.wait() 
process_2.wait() 

wie kann ich das gleiche zu erreichen, wenn den asynchronen API von asyncio.subprocess.create_subprocess_exec() (oder ähnlichem) unter Verwendung von? Das ist, was ich versucht:

from asyncio.events import get_event_loop 
from asyncio.subprocess import PIPE, create_subprocess_exec 

async def main(): 
    process_1 = await create_subprocess_exec('ls', stdout = PIPE) 
    process_2 = await create_subprocess_exec('wc', stdin = process_1.stdout) 

    await process_1.wait() 
    await process_2.wait() 

get_event_loop().run_until_complete(main()) 

Aber der zweite Aufruf von create_subprocess_exec() beschwert sich, dass die zu stdin gebene Argument hat keine fileno (was stimmt):

Traceback (most recent call last): 
    File ".../test-async.py", line 11, in <module> 
    get_event_loop().run_until_complete(main()) 
[...] 
    File ".../test-async.py", line 6, in main 
    process_2 = await create_subprocess_exec('wc', stdin = process_1.stdout) 
[...] 
    File "/opt/local/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/subprocess.py", line 1388, in _get_handles 
    p2cread = stdin.fileno() 
AttributeError: 'StreamReader' object has no attribute 'fileno' 

Wie kann ich das gleiche Ergebnis wie im synchronen Beispiel oben?

+1

ohne Bezug: Sie sollten 'process_1.stdout.close()' nach 'Popen (['wc'], ..)' hinzufügen. Siehe [Wie verwende ich subprocess.Popen, um mehrere Prozesse über Pipes zu verbinden?] (Http://stackoverflow.com/q/295459/4279) – jfs

Antwort

5

In asyncio ist process.stdout eigentlich ein StreamReader, kein Dateiobjekt. Auf das Dateiobjekt kann über process._transport._proc.stdout zugegriffen werden. Leider können Sie es nicht verwenden, da es bereits in der Ereignisschleife registriert wurde, um die Stream-Schnittstelle process.stdout bereitzustellen.

Ein Weg, mit dem Problem umzugehen ist, Ihr eigenes Rohr zu erstellen und die Datei-Deskriptoren zum subprocess passieren:

async def main(): 
    read, write = os.pipe() 
    process_1 = await create_subprocess_exec('ls', stdout=write) 
    os.close(write) 
    process_2 = await create_subprocess_exec('wc', stdin=read, stdout=PIPE) 
    os.close(read) 
    return await process_2.stdout.read() 

Beachten Sie, dass die write Dateideskriptors explizit geschlossen werden sollen, sobald der erste Teilprozess gestartet wird (Es wird nicht automatisch geschlossen, es sei denn, Sie verwenden subprocess.PIPE). Der Dateideskriptor read muss ebenfalls geschlossen werden, wie erklärt here.

+0

könnten Sie eine Referenz für die Anweisung angeben: * "Es wird nur automatisch geschlossen, wenn Sie verwenden subprocess.PIPE "*? Wie ich verstehe, ist der Grund, warum Sie 'os.close (write)' im Elternteil haben wollen, so dass 'ls'' SIGPIPE' und 'EPIPE' erhalten würde, wenn' wc' vorzeitig stirbt. Das gleiche gilt für 'subprocess.PIPE' (zumindest für gewöhnliche' Popen() ', wie in [das Codebeispiel in den Dokumenten] demonstriert wird (https://docs.python.org/3/library/subprocess.html#). Replacement-Shell-Pipeline)) – jfs

+0

@JFSebastian Ich bemerkte es, indem ich [den Code] (https://github.com/python/cpython/blob/master/Lib/subprocess.py#L1501), ich denke ' Ich weiß, ob es irgendwo dokumentiert ist. In Ihrem Beispiel geht es jedoch darum, den Reader zu schließen, nicht den Writer. In meinem Beispiel muss der Writer geschlossen werden, um die Pipe richtig einzurichten (siehe [diese Antwort] (http://stackoverflow.com/a/15512202/2846140)). – Vincent

+0

Sie haben Recht mit dem 'write'-Ende der Pipe (es ist geschlossen, weil es im Parent nicht verwendet wird - es würde auch erklären, warum der Code verantwortlich ist, der die' subprocess.PIPE'-Pipe erzeugt, um sie zu schließen- es steht in keinem Zusammenhang mit 'asyncio' und der entsprechende' os.close() 'Aufruf ist im' subprocess' Modul). Das Beispiel, das ich erwähnte, würde "process_1.stdout.close()" oben entsprechen, d. H. "Os.close (read)" sollte aufgerufen werden. – jfs

Verwandte Themen