Runner

Quellcode: Lib/asyncio/runners.py

Dieser Abschnitt beschreibt High-Level-asyncio-Primitiven zur Ausführung von asyncio-Code.

Sie basieren auf einer Ereignisschleife mit dem Ziel, die Verwendung von asynchronem Code für häufige, weit verbreitete Szenarien zu vereinfachen.

Ein asyncio-Programm ausführen

asyncio.run(coro, *, debug=None, loop_factory=None)

Führt coro in einer asyncio-Ereignisschleife aus und gibt das Ergebnis zurück.

Das Argument kann jedes awaitable-Objekt sein.

Diese Funktion führt das awaitable aus und kümmert sich um die Verwaltung der asyncio-Ereignisschleife, die Finalisierung asynchroner Generatoren und das Schließen des Executors.

Diese Funktion kann nicht aufgerufen werden, wenn bereits eine andere asyncio-Ereignisschleife im selben Thread läuft.

Wenn debug True ist, wird die Ereignisschleife im Debug-Modus ausgeführt. False deaktiviert den Debug-Modus explizit. None wird verwendet, um die globalen Einstellungen des Debug-Modus zu berücksichtigen.

Wenn loop_factory nicht None ist, wird sie zur Erstellung einer neuen Ereignisschleife verwendet; andernfalls wird asyncio.new_event_loop() verwendet. Die Schleife wird am Ende geschlossen. Diese Funktion sollte als Haupteinstiegspunkt für asyncio-Programme verwendet werden und idealerweise nur einmal aufgerufen werden. Es wird empfohlen, loop_factory zur Konfiguration der Ereignisschleife anstelle von Richtlinien zu verwenden. Die Übergabe von asyncio.EventLoop ermöglicht die Ausführung von asyncio ohne das Richtliniensystem.

Der Executor erhält eine Timeout-Dauer von 5 Minuten zum Herunterfahren. Wenn der Executor innerhalb dieser Dauer nicht fertig ist, wird eine Warnung ausgegeben und der Executor geschlossen.

Beispiel

async def main():
    await asyncio.sleep(1)
    print('hello')

asyncio.run(main())

Hinzugefügt in Version 3.7.

Geändert in Version 3.9: Aktualisiert zur Verwendung von loop.shutdown_default_executor().

Geändert in Version 3.10: debug ist standardmäßig None, um die globalen Einstellungen des Debug-Modus zu berücksichtigen.

Geändert in Version 3.12: loop_factory-Parameter hinzugefügt.

Geändert in Version 3.14: coro kann jedes awaitable-Objekt sein.

Hinweis

Das asyncio-Richtliniensystem ist veraltet und wird in Python 3.16 entfernt; danach ist ein expliziter loop_factory erforderlich, um die Ereignisschleife zu konfigurieren.

Runner-Kontextmanager

class asyncio.Runner(*, debug=None, loop_factory=None)

Ein Kontextmanager, der die Ausführung von mehreren asynchronen Funktionsaufrufen im selben Kontext vereinfacht.

Manchmal sollten mehrere Top-Level-Async-Funktionen in derselben Ereignisschleife und demselben contextvars.Context aufgerufen werden.

Wenn debug True ist, wird die Ereignisschleife im Debug-Modus ausgeführt. False deaktiviert den Debug-Modus explizit. None wird verwendet, um die globalen Einstellungen des Debug-Modus zu berücksichtigen.

loop_factory kann zur Überschreibung der Schleppenerstellung verwendet werden. Es liegt in der Verantwortung von loop_factory, die erstellte Schleife als aktuelle zu setzen. Standardmäßig wird asyncio.new_event_loop() verwendet und mit asyncio.set_event_loop() als aktuelle Ereignisschleife gesetzt, wenn loop_factory None ist.

Im Grunde kann das Beispiel von asyncio.run() mit der Verwendung des Runners neu geschrieben werden.

async def main():
    await asyncio.sleep(1)
    print('hello')

with asyncio.Runner() as runner:
    runner.run(main())

Hinzugefügt in Version 3.11.

run(coro, *, context=None)

Führt coro in der eingebetteten Ereignisschleife aus.

Das Argument kann jedes awaitable-Objekt sein.

Wenn das Argument eine Coroutine ist, wird es in einen Task eingewickelt.

Ein optionales Keyword-only-Argument context erlaubt die Angabe eines benutzerdefinierten contextvars.Context, in dem der Code ausgeführt werden soll. Der Standardkontext des Runners wird verwendet, wenn context None ist.

Gibt das Ergebnis des awaitable zurück oder löst eine Ausnahme aus.

Diese Funktion kann nicht aufgerufen werden, wenn bereits eine andere asyncio-Ereignisschleife im selben Thread läuft.

Geändert in Version 3.14: coro kann jedes awaitable-Objekt sein.

close()

Schließt den Runner.

Finalisiert asynchrone Generatoren, fährt den Standard-Executor herunter, schließt die Ereignisschleife und gibt eingebettete contextvars.Context frei.

get_loop()

Gibt die der Runner-Instanz zugeordnete Ereignisschleife zurück.

Hinweis

Runner verwendet eine Strategie der verzögerten Initialisierung; sein Konstruktor initialisiert die zugrunde liegenden Low-Level-Strukturen nicht.

Die eingebettete Schleife und der Kontext werden beim Eintritt in den with-Block oder beim ersten Aufruf von run() oder get_loop() erstellt.

Behandlung von Tastaturunterbrechungen

Hinzugefügt in Version 3.11.

Wenn signal.SIGINT durch Strg-C ausgelöst wird, wird standardmäßig die Ausnahme KeyboardInterrupt im Hauptthread ausgelöst. Dies funktioniert jedoch nicht mit asyncio, da es interne asyncio-Abläufe unterbrechen kann und das Beenden des Programms blockieren kann.

Um dieses Problem zu beheben, behandelt asyncio signal.SIGINT wie folgt:

  1. asyncio.Runner.run() installiert einen benutzerdefinierten signal.SIGINT-Handler, bevor irgendein Benutzercode ausgeführt wird, und entfernt ihn beim Verlassen der Funktion wieder.

  2. Der Runner erstellt den Haupt-Task für die übergebene Coroutine zur Ausführung.

  3. Wenn signal.SIGINT durch Strg-C ausgelöst wird, bricht der benutzerdefinierte Signal-Handler den Haupt-Task ab, indem er asyncio.Task.cancel() aufruft, was asyncio.CancelledError innerhalb des Haupt-Tasks auslöst. Dies führt zum Auswickeln des Python-Stacks; try/except- und try/finally-Blöcke können für die Ressourcenbereinigung verwendet werden. Nachdem der Haupt-Task abgebrochen wurde, löst asyncio.Runner.run() KeyboardInterrupt aus.

  4. Ein Benutzer könnte eine enge Schleife schreiben, die nicht durch asyncio.Task.cancel() unterbrochen werden kann, in diesem Fall löst die zweite nachfolgende Strg-C-Betätigung sofort die KeyboardInterrupt aus, ohne den Haupt-Task abzubrechen.