Subprozesse

Quellcode: Lib/asyncio/subprocess.py, Lib/asyncio/base_subprocess.py


Dieser Abschnitt beschreibt High-Level-APIs mit async/await für asyncio, um Subprozesse zu erstellen und zu verwalten.

Hier ist ein Beispiel dafür, wie asyncio einen Shell-Befehl ausführen und sein Ergebnis abrufen kann

import asyncio

async def run(cmd):
    proc = await asyncio.create_subprocess_shell(
        cmd,
        stdout=asyncio.subprocess.PIPE,
        stderr=asyncio.subprocess.PIPE)

    stdout, stderr = await proc.communicate()

    print(f'[{cmd!r} exited with {proc.returncode}]')
    if stdout:
        print(f'[stdout]\n{stdout.decode()}')
    if stderr:
        print(f'[stderr]\n{stderr.decode()}')

asyncio.run(run('ls /zzz'))

wird ausgeben

['ls /zzz' exited with 1]
[stderr]
ls: /zzz: No such file or directory

Da alle Subprozessfunktionen von asyncio asynchron sind und asyncio viele Werkzeuge für die Arbeit mit solchen Funktionen bietet, ist es einfach, mehrere Subprozesse parallel auszuführen und zu überwachen. Es ist tatsächlich trivial, das obige Beispiel so zu modifizieren, dass mehrere Befehle gleichzeitig ausgeführt werden

async def main():
    await asyncio.gather(
        run('ls /zzz'),
        run('sleep 1; echo "hello"'))

asyncio.run(main())

Siehe auch die Untersektion Beispiele.

Subprozesse erstellen

async asyncio.create_subprocess_exec(program, *args, stdin=None, stdout=None, stderr=None, limit=None, **kwds)

Erstellt einen Subprozess.

Das Argument limit legt das Pufferlimit für StreamReader-Wrapper für stdout und stderr fest (wenn subprocess.PIPE an die Argumente stdout und stderr übergeben wird).

Gibt eine Process-Instanz zurück.

Weitere Parameter finden Sie in der Dokumentation von loop.subprocess_exec().

Geändert in Version 3.10: Das Argument loop wurde entfernt.

async asyncio.create_subprocess_shell(cmd, stdin=None, stdout=None, stderr=None, limit=None, **kwds)

Führt den Shell-Befehl cmd aus.

Das Argument limit legt das Pufferlimit für StreamReader-Wrapper für stdout und stderr fest (wenn subprocess.PIPE an die Argumente stdout und stderr übergeben wird).

Gibt eine Process-Instanz zurück.

Weitere Parameter finden Sie in der Dokumentation von loop.subprocess_shell().

Wichtig

Es liegt in der Verantwortung der Anwendung sicherzustellen, dass alle Leerzeichen und Sonderzeichen ordnungsgemäß maskiert werden, um Shell-Injection-Schwachstellen zu vermeiden. Die Funktion shlex.quote() kann verwendet werden, um Leerzeichen und spezielle Shell-Zeichen in Zeichenfolgen, die zur Konstruktion von Shell-Befehlen verwendet werden, ordnungsgemäß zu maskieren.

Geändert in Version 3.10: Das Argument loop wurde entfernt.

Hinweis

Subprozesse sind unter Windows verfügbar, wenn eine ProactorEventLoop verwendet wird. Details finden Sie unter Subprocess Support unter Windows.

Siehe auch

asyncio bietet auch die folgenden Low-Level-APIs für die Arbeit mit Subprozessen: loop.subprocess_exec(), loop.subprocess_shell(), loop.connect_read_pipe(), loop.connect_write_pipe() sowie die Subprocess Transports und Subprocess Protocols.

Konstanten

asyncio.subprocess.PIPE

Kann an die Argumente stdin, stdout oder stderr übergeben werden.

Wenn PIPE an das Argument stdin übergeben wird, zeigt das Attribut Process.stdin auf eine StreamWriter-Instanz.

Wenn PIPE an die Argumente stdout oder stderr übergeben wird, zeigen die Attribute Process.stdout und Process.stderr auf StreamReader-Instanzen.

asyncio.subprocess.STDOUT

Spezieller Wert, der als Argument stderr verwendet werden kann und angibt, dass der Standardfehler in die Standardausgabe umgeleitet werden soll.

asyncio.subprocess.DEVNULL

Spezieller Wert, der als Argument stdin, stdout oder stderr für Funktionen zur Prozessverwaltung verwendet werden kann. Er gibt an, dass die spezielle Datei os.devnull für den entsprechenden Subprozessstream verwendet wird.

Interaktion mit Subprozessen

Sowohl die Funktionen create_subprocess_exec() als auch create_subprocess_shell() geben Instanzen der Process-Klasse zurück. Process ist ein High-Level-Wrapper, der die Kommunikation mit Subprozessen und die Überwachung ihres Abschlusses ermöglicht.

class asyncio.subprocess.Process

Ein Objekt, das OS-Prozesse umschließt, die von den Funktionen create_subprocess_exec() und create_subprocess_shell() erstellt wurden.

Diese Klasse ist so konzipiert, dass sie eine ähnliche API wie die Klasse subprocess.Popen aufweist, es gibt jedoch einige bemerkenswerte Unterschiede

  • im Gegensatz zu Popen haben Process-Instanzen keine Entsprechung zur Methode poll();

  • die Methoden communicate() und wait() haben keinen Parameter timeout: verwenden Sie stattdessen die Funktion wait_for();

  • die Methode Process.wait() ist asynchron, während die Methode subprocess.Popen.wait() als blockierende Busy-Loop implementiert ist;

  • der Parameter universal_newlines wird nicht unterstützt.

Diese Klasse ist nicht threadsicher.

Siehe auch den Abschnitt Subprozesse und Threads.

async wait()

Wartet auf die Beendigung des Kindprozesses.

Setzt und gibt das Attribut returncode.

Hinweis

Diese Methode kann zu einem Deadlock führen, wenn stdout=PIPE oder stderr=PIPE verwendet wird und der Kindprozess so viele Ausgaben erzeugt, dass er darauf wartet, dass der OS-Pipe-Puffer mehr Daten aufnehmen kann. Verwenden Sie die Methode communicate(), wenn Pipes verwendet werden, um diese Bedingung zu vermeiden.

async communicate(input=None)

Interaktion mit dem Prozess

  1. sendet Daten an stdin (wenn input nicht None ist);

  2. schließt stdin;

  3. liest Daten von stdout und stderr, bis EOF erreicht ist;

  4. wartet auf die Beendigung des Prozesses.

Das optionale Argument input sind die Daten (bytes-Objekt), die an den Kindprozess gesendet werden.

Gibt ein Tupel (stdout_data, stderr_data) zurück.

Wenn beim Schreiben von input in stdin eine Ausnahme BrokenPipeError oder ConnectionResetError ausgelöst wird, wird die Ausnahme ignoriert. Diese Bedingung tritt auf, wenn der Prozess beendet wird, bevor alle Daten in stdin geschrieben wurden.

Wenn Daten an stdin des Prozesses gesendet werden sollen, muss der Prozess mit stdin=PIPE erstellt werden. Wenn etwas anderes als None im Ergebnis-Tupel gewünscht wird, muss der Prozess mit den Argumenten stdout=PIPE und/oder stderr=PIPE erstellt werden.

Beachten Sie, dass die gelesenen Daten im Speicher gepuffert werden. Verwenden Sie diese Methode daher nicht, wenn die Datengröße groß oder unbegrenzt ist.

Geändert in Version 3.12: stdin wird auch geschlossen, wenn input=None ist.

send_signal(signal)

Sendet das Signal signal an den Kindprozess.

Hinweis

Unter Windows ist SIGTERM ein Alias für terminate(). CTRL_C_EVENT und CTRL_BREAK_EVENT können an Prozesse gesendet werden, die mit einem creationflags-Parameter gestartet wurden, der CREATE_NEW_PROCESS_GROUP enthält.

terminate()

Beendet den Kindprozess.

Auf POSIX-Systemen sendet diese Methode SIGTERM an den Kindprozess.

Unter Windows wird die Win32 API-Funktion TerminateProcess() aufgerufen, um den Kindprozess zu beenden.

kill()

Beendet den Kindprozess.

Auf POSIX-Systemen sendet diese Methode SIGKILL an den Kindprozess.

Unter Windows ist diese Methode ein Alias für terminate().

stdin

Standard-Eingabestream (StreamWriter) oder None, wenn der Prozess mit stdin=None erstellt wurde.

stdout

Standard-Ausgabestream (StreamReader) oder None, wenn der Prozess mit stdout=None erstellt wurde.

stderr

Standard-Fehlerstrom (StreamReader) oder None, wenn der Prozess mit stderr=None erstellt wurde.

Warnung

Verwenden Sie die Methode communicate() anstelle von process.stdin.write(), await process.stdout.read() oder await process.stderr.read(). Dies vermeidet Deadlocks, da Streams das Lesen oder Schreiben pausieren und den Kindprozess blockieren.

pid

Prozessidentifikationsnummer (PID).

Beachten Sie, dass für Prozesse, die von der Funktion create_subprocess_shell() erstellt wurden, dieses Attribut die PID der gestarteten Shell ist.

returncode

Rückgabecode des Prozesses nach seiner Beendigung.

Ein Wert von None zeigt an, dass der Prozess noch nicht beendet wurde.

Ein negativer Wert -N zeigt an, dass das Kind durch das Signal N beendet wurde (nur POSIX).

Subprozesse und Threads

Die Standard-Event-Loop von asyncio unterstützt standardmäßig die Ausführung von Subprozessen aus verschiedenen Threads.

Unter Windows werden Subprozesse ausschließlich von ProactorEventLoop (Standard) bereitgestellt. SelectorEventLoop unterstützt keine Subprozesse.

Beachten Sie, dass alternative Implementierungen von Event-Loops eigene Einschränkungen haben können; siehe deren Dokumentation.

Siehe auch

Der Abschnitt Concurrency und Multithreading in asyncio.

Beispiele

Ein Beispiel, das die Klasse Process zur Steuerung eines Subprozesses und die Klasse StreamReader zum Lesen aus dessen Standardausgabe verwendet.

Der Subprozess wird von der Funktion create_subprocess_exec() erstellt

import asyncio
import sys

async def get_date():
    code = 'import datetime; print(datetime.datetime.now())'

    # Create the subprocess; redirect the standard output
    # into a pipe.
    proc = await asyncio.create_subprocess_exec(
        sys.executable, '-c', code,
        stdout=asyncio.subprocess.PIPE)

    # Read one line of output.
    data = await proc.stdout.readline()
    line = data.decode('ascii').rstrip()

    # Wait for the subprocess exit.
    await proc.wait()
    return line

date = asyncio.run(get_date())
print(f"Current date: {date}")

Siehe auch das gleiche Beispiel, das mit Low-Level-APIs geschrieben wurde.