Asynchrone Aufgabengruppen in Python 3.11 / Sudo Null IT News

Gestern wurde der erste Release Candidate von Python 3.11 auf der offiziellen Website veröffentlicht, der wichtige Optimierungen und Verbesserungen der Features der Sprache bringen wird. Die Veröffentlichung ist für Oktober dieses Jahres geplant, aber jetzt können Sie mit neuen Funktionen experimentieren, und heute werden wir über Ausnahmegruppen und asynchrone Aufgaben sprechen. Erstere ermöglichen es Ihnen, mehrere Ausnahmen gleichzeitig auszulösen und zu behandeln, während letztere es Ihnen ermöglichen, Aufgaben in einer gemeinsamen Ereignisschleife zu kombinieren und Gruppen von Aufgaben koordiniert zu verwalten.

Zum Testen verwenden wir das Container-Image python:3.11-rc-slim-buster. Lassen Sie uns den Container mit docker run –rm -it python:3.11-rc-slim-buster starten und Zugriff auf die REPL erhalten, wo wir mit neuen Sprachfeatures experimentieren können. Wir können die Python-Datei auch mit cat test.py | ausführen docker run -i python:3.11-rc-slim-buster.

Beginnen wir mit Ausnahmegruppen und versuchen Sie sofort, ein Beispiel für sie vorzubereiten. Mit Ausnahmegruppen können Sie mehrere gleichzeitig auftretende Ausnahmen behandeln (z. B. in asynchronen Funktionen) und sie als Liste von Objekten interpretieren (vorher wurde nur die erste Ausnahme behandelt). Die Spezifikation von Ausschlussgruppen wird detaillierter beschrieben. hier.

In normalen Situationen erfolgt die Ausnahmebehandlung durch einen Try-Except-Block, und wenn mehrere konkurrierende Funktionen innerhalb eines Codeblocks eine Ausnahme zurückgeben, wird nur die erste verarbeitet. Wenn eine Ausnahme in einem Raise-Block auftritt, wird sie als „aufgetreten, während eine andere Ausnahme behandelt wurde“ markiert und nicht mit der ursprünglichen Ausnahme in Beziehung gesetzt.

Python 3.11 fügt eine neue ExceptionGroup-Klasse hinzu, die mehrere Ausnahmen melden und eine Textbeschreibung für sie bereitstellen kann. Dadurch wird der Fehlermeldung Kontext hinzugefügt und es können mehrere Ereignisse miteinander verkettet werden (eine Ausnahmegruppe kann auch andere Gruppen enthalten, und daher können Ausnahmen rekursiv sein). Wenn die Ausnahme nicht behandelt wird, erhalten wir eine Fehlerverfolgung, die den Ausnahmebaum einschließlich Verschachtelung und Kommentare zeigt. In diesem Fall wird beim Abfangen einer Gruppe durch Try-Exception nur das äußere ExceptionGroup-Objekt gefunden, aber keine damit verbundenen Ausnahmen. Für die korrekte Behandlung einer der Ausnahmen der Gruppe wird ein neues syntaktisches Konstrukt try – except* verwendet. Wenn Sie beispielsweise sowohl ValueError- als auch ZeroDivisionError-Ausnahmen gleichzeitig auslösen, kann ihre Behandlung unabhängig (und parallel) durchgeführt werden:

try: raise ExceptionGroup(‘Mehrere Ausnahmen’, [ValueError(), ZeroDivisionError()]) außer* ValueError as gr: print(‘Wertfehler ‘+gr.Ausnahmen) except* ZeroDivisionError as gr: print(‘Zero Division error ‘+gr.Exceptions)

Beachten Sie, dass das Fehlerobjekt alle Instanzen eines bestimmten Typs sammelt (z. B. können mehrere ValueErrors gesendet werden und auf jede einzelne kann über die Ausnahmeliste zugegriffen werden). Wenn nicht alle gesendeten Typen in except* verarbeitet wurden, werden sie oben gesendet (und können abstürzen, wenn nur unbehandelte Typen im Trace sind).

In Python 3.11 werden alle einzelnen Ausnahmetypen automatisch in except* erkannt, als ob sie in eine Ausnahmegruppe geworfen würden.

Das interessanteste Szenario ist das Auftreten aufeinanderfolgender Ausnahmen, während auf den Abschluss einer Gruppe von Aufgaben gewartet wird. Angenommen, wir haben eine relativ lange Aufgabe, die intern Daten aus einer Datei verarbeitet, was eine Ausnahme auslösen kann. Was wird dann zurückgegeben, wenn wir mehrere Aufgaben aufrufen und ihre Ergebnisse über asyncio.gather sammeln?

import asyncio import io async def process_file(filename): with open(filename, mode=”r”): print(‘Datei wird geöffnet’) return True async def process(filenames): task = [asyncio.create_task(process_file(filename)) for filename in filenames]
await asyncio.gather(*tasks) if __name__==”__main__”: asyncio.run(process([‘file1′,’file2′,’file3’])

Lassen Sie uns diese Anwendung ausführen (in einem Verzeichnis, in dem es keine Dateien file1, file2 und file3 gibt) und wir werden sehen, dass nur ein Fehler im Trace vermerkt wird. Um dieses Problem zu umgehen, können wir das Flag return_exceptions in Gather verwenden, aber eine bessere Lösung wäre die Verwendung von asyncio.TaskGroup . Beim Ausführen von Aufgaben in einer Gruppe werden Ausnahmen zu einer gemeinsamen ExceptionGroup zusammengefasst und können mit except* behandelt werden. Die Ressourcenverwaltung (z. B. Aufgabenerstellung) erfolgt nun innerhalb der Gruppe (anstelle von asyncio.create_task wird tg.create_task verwendet), die über den Kontextmanager erstellt wird. Der obige Code könnte beispielsweise wie folgt umgeschrieben werden:

import asyncio async def process_file(filename): with open(filename, mode=”r”): print(‘Datei wird geöffnet’) return True async def process(filenames): try: async with asyncio.TaskGroup() as tg: Aufgaben = [tg.create_task(process_file(filename)) for filename in filenames]
außer* FileNotFoundError als Fehler: print(f’Dateien nicht gefunden’) print([e.filename for e in errors.exceptions]) if __name__==”__main__”: asyncio.run(process([‘file1′,’file2′,’file3’])

Wie Sie sehen können, ersetzt TaskGroup das Sammeln und bietet die gleichzeitige Ausführung von Aufgaben und die Gruppierung von Ausnahmen, die in einer Gruppe aufgetreten sind. Nach der Ausführung sehen wir, dass beim Abfangen des Fehlers drei fehlende Dateien gemeldet werden (trotz der Tatsache, dass sie in separaten asynchronen Tasks ausgeführt wurden, aber innerhalb der TaskGroup werden alle gesammelten Ausnahmen in einer Ausnahmegruppe zusammengefasst).

Katze test.py | docker run -i python:3.11-rc-slim-buster Dateien nicht gefunden
[‘file1’, ‘file2’, ‘file3’]

Durch die Verwendung von Gruppen von Coroutinen und Ausnahmen können Sie also komplexere Szenarien mit paralleler Ausführung von Aufgaben durchführen, bei denen Ausnahmen auftreten können.

Und zum Schluss lade ich alle zu einer kostenlosen Lektion zum Thema „Clean Architecture in Python Development“ ein, die von meinem Kollegen von OTUS – Stanislav Stupnikov – geleitet wird.

Similar Posts

Leave a Reply

Your email address will not be published.