Python v3.x: Ausnahmehandler für Coroutine- und Synchronfunktionen. Im Allgemeinen für alles

In meiner Freizeit arbeite ich an meinem kleinen Projekt . Geschrieben in Python v3.x + SQLAlchemy. Vielleicht werde ich eines Tages über ihn schreiben, aber heute möchte ich über meinen Dekorateur sprechen, um Ausnahmen zu behandeln. Es kann sowohl für Funktionen als auch für Methoden verwendet werden. Synchron und asynchron. Sie können auch benutzerdefinierte Ausnahmebehandlungsroutinen verbinden.

Der Dekorateur sieht derzeit so aus:
import asyncio from asyncio import QueueEmpty, QueueFull from concurrent.futures import TimeoutError class ProcessException(object): __slots__ = ('func', 'custom_handlers', 'exclude') def __init__(self, custom_handlers=None): self.func = None self.custom_handlers: dict = custom_handlers self.exclude = [QueueEmpty, QueueFull, TimeoutError] def __call__(self, func, *a): self.func = func def wrapper(*args, **kwargs): if self.custom_handlers: if isinstance(self.custom_handlers, property): self.custom_handlers = self.custom_handlers.__get__(self, self.__class__) if asyncio.iscoroutinefunction(self.func): return self._coroutine_exception_handler(*args, **kwargs) else: return self._sync_exception_handler(*args, **kwargs) return wrapper async def _coroutine_exception_handler(self, *args, **kwargs): try: return await self.func(*args, **kwargs) except Exception as e: if self.custom_handlers and e.__class__ in self.custom_handlers: return self.custom_handlers[e.__class__]() if e.__class__ not in self.exclude: raise e def _sync_exception_handler(self, *args, **kwargs): try: return self.func(*args, **kwargs) except Exception as e: if self.custom_handlers and e.__class__ in self.custom_handlers: return self.custom_handlers[e.__class__]() if e.__class__ not in self.exclude: raise e 

Wir werden in der richtigen Reihenfolge analysieren. __slots__ Ich speichere ein wenig Speicher. Es ist nützlich, wenn das Objekt sooo oft verwendet wird.

In der Initialisierungsphase in __init__ speichern wir custom_handlers (falls diese übertragen werden mussten). Für alle Fälle gab er an, dass wir dort ein Wörterbuch erwarten, obwohl es vielleicht in Zukunft sinnvoll ist, ein paar harte Prüfungen hinzuzufügen. Die Eigenschaft self.exclude enthält eine Liste von Ausnahmen, die Sie nicht behandeln müssen. In einem solchen Fall gibt die Funktion mit dem Dekorateur None zurück. Im Moment ist die Liste für mein Projekt geschärft, und vielleicht ist es sinnvoll, sie in eine separate Konfiguration zu stellen.

Das Wichtigste passiert in __call__. Wenn Sie den Dekorator verwenden, müssen Sie ihn daher aufrufen. Auch ohne Parameter:

 @ProcessException() def some_function(*args): return None 

Das heißt, Dies ist falsch und es wird ein Fehler ausgegeben:

 @ProcessException def some_function(*args): return None 

In diesem Fall erhalten wir die aktuelle Funktion, die wir je nach Grad ihrer Asynchronität entweder als reguläre Synchronfunktion oder als Coroutine verarbeiten.

Worauf Sie hier achten können. Das erste ist eine Überprüfung der Eigenschaft:

 if self.custom_handlers: if isinstance(self.custom_handlers, property): self.custom_handlers = self.custom_handlers.__get__(self, self.__class__) 

Warum mache ich das?

 Natürlich 
          nicht weil 
                       Ich bin IT Mayakovsky 
                                    und sie bezahlen mich Zeile für Zeile.

Zwei davon sind hier, um die Lesbarkeit zu verbessern (ja, weil der Code von einer Person mit sadistischen Tendenzen unterstützt werden kann), und wir machen self.custom_handlers .__ get __ (self, self .__ class__), falls wir uns entscheiden Handler, die in der Klasse @property gespeichert sind.

Zum Beispiel so:

 class Math(object): @property def exception_handlers(self): return { ZeroDivisionError: lambda: '   ,   ' } @ProcessException(exception_handlers) def divide(self, a, b): return a // b 

Wenn wir self.custom_handlers .__ get __ (...) nicht ausführen, erhalten wir anstelle des Inhalts von @property so etwas wie <property object at 0x7f78d844f9b0>.

Das obige Beispiel zeigt, wie benutzerdefinierte Handler verbunden werden. Im Allgemeinen erfolgt dies wie folgt:

 @ProcessException({ZeroDivisionError: lambda: '   ,   '}) def divide(a, b): return a // b 

Im Fall einer Klasse (wenn wir Eigenschaften / Methoden übergeben wollen) müssen wir berücksichtigen, dass es zum Zeitpunkt der Initialisierung des Klassendekorators keine solchen Methoden gibt und Eigenschaften / Methoden einfache Funktionen sind. Daher können wir nur das vermitteln, was oben angekündigt wurde. Daher ist die Option @property die Fähigkeit, alle Funktionen, deren Code niedriger ist, selbst zu verwenden. Nun, entweder können Sie Lambdas verwenden, wenn Selbst nicht benötigt wird.

Für asynchronen Code gelten alle obigen Beispiele.

Am Ende möchte ich darauf hinweisen, dass eine Ausnahme, die auf ihrem Weg nicht auf benutzerdefinierte Handler gestoßen ist, einfach weiter ausgelöst wird.

Warten auf Ihre Kommentare. Vielen Dank, dass Sie meinen Artikel beachtet haben.

Source: https://habr.com/ru/post/de473596/


All Articles