Python v3.x: exception handler untuk fungsi coroutine dan sinkron. Secara umum, untuk semuanya

Di waktu luang saya, saya mengerjakan proyek kecil saya. Ditulis dalam Python v3.x + SQLAlchemy. Mungkin saya akan menulis tentang dia suatu hari nanti, tetapi hari ini saya ingin berbicara tentang dekorator saya untuk menangani pengecualian. Dapat digunakan untuk fungsi dan metode. Sinkron dan asinkron. Anda juga dapat menghubungkan penangan pengecualian khusus.

Dekorator saat ini terlihat seperti ini:
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 

Kami akan menganalisis secara berurutan. __slots__ Saya gunakan untuk menghemat sedikit memori. Ini berguna jika objek sering digunakan.

Pada tahap inisialisasi di __init__, kami menyimpan custom_handlers (jika perlu untuk mentransfernya). Untuk jaga-jaga, ia mengindikasikan bahwa kami berharap melihat kamus di sana, meskipun, mungkin di masa depan, masuk akal untuk menambahkan beberapa cek sulit. Properti self.exclude berisi daftar pengecualian yang tidak perlu Anda tangani. Dalam hal pengecualian seperti itu, fungsi dengan dekorator tidak akan mengembalikan satupun. Saat ini, daftar dipertajam untuk proyek saya, dan mungkin masuk akal untuk meletakkannya di konfigurasi terpisah.

Hal terpenting terjadi di __call__. Karena itu, ketika menggunakan dekorator, Anda perlu menyebutnya. Bahkan tanpa parameter:

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

Yaitu ini akan salah dan kesalahan akan dimunculkan:

 @ProcessException def some_function(*args): return None 

Dalam hal ini, kami mendapatkan fungsi saat ini, yang, tergantung pada derajat asinkroninya, kami akan memproses sebagai sinkron biasa atau sebagai coroutine.

Apa yang bisa Anda perhatikan di sini. Yang pertama adalah cek di properti:

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

Kenapa saya melakukan ini?

 Tentu saja 
          bukan karena 
                       Saya IT Mayakovsky 
                                    dan mereka membayar saya baris demi baris.

Dua jika ada di sini untuk meningkatkan keterbacaan (ya, karena kode dapat didukung oleh seseorang dengan kecenderungan sadis), dan kami melakukan self.custom_handlers .__ mendapatkan __ (diri, diri. Class__) jika kami memutuskan handler disimpan di kelas @property.

Misalnya, seperti ini:

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

Jika kita tidak melakukan self.custom_handlers .__ mendapatkan __ (...), maka alih-alih konten @ properti kita akan mendapatkan sesuatu seperti <objek properti di 0x7f78d844f9b0>.

Sebenarnya, contoh di atas menunjukkan bagaimana menghubungkan penangan kustom. Secara umum, ini dilakukan sebagai berikut:

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

Dalam kasus kelas (jika kita akan melewati properti / metode), kita perlu memperhitungkan bahwa pada tahap inisialisasi dekorator kelas, metode dan properti bukanlah fungsi yang sederhana. Karena itu, kami hanya dapat menyampaikan apa yang diumumkan di atas. Oleh karena itu, opsi @property adalah kemampuan untuk menggunakan sendiri semua fungsi yang lebih rendah dalam kode. Baik, Anda bisa menggunakan lambdas jika diri tidak diperlukan.

Untuk kode asinkron, semua contoh di atas adalah benar.

Pada akhirnya, saya ingin menarik perhatian pada fakta bahwa jika pengecualian tidak memenuhi penangan kustom di jalurnya, maka itu hanya meningkatkan lebih lanjut.

Menunggu komentar Anda. Terima kasih telah memperhatikan artikel saya.

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


All Articles