Python v3.x:协程和同步函数的异常处理程序。 总的来说

在业余时间,我从事我的小项目 。 用Python v3.x + SQLAlchemy编写。 也许有一天我会写关于他的文章,但是今天我想谈谈我的装饰器处理异常。 它可以用于功能和方法。 同步和异步。 您还可以连接自定义异常处理程序。

装饰器当前看起来像这样:
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 

我们将按顺序进行分析。 __插槽__我用来节省一点内存。 如果经常使用该对象很有用。

在__init__的初始化阶段,我们保存了custom_handlers(以防有必要转移它们)。 为了以防万一,他表示我们希望在那里能看到字典,尽管在将来也许增加一些严格的检查是有意义的。 self.exclude属性包含不需要处理的异常列表。 如果发生此类异常,带有装饰器的函数将返回None。 目前,该列表已针对我的项目进行了优化,将它放在单独的配置中也许很有意义。

最重要的事情发生在__call__中。 因此,在使用装饰器时,需要调用它。 即使没有参数:

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

即 这将是错误的,并且将引发错误:

 @ProcessException def some_function(*args): return None 

在这种情况下,我们得到了当前函数,根据其异步程度,我们将其作为常规同步或协程进行处理。

您在这里要注意什么。 首先是对属性的检查:

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

我为什么要这样做?

当然啦 
         不是因为 
                      我是Mayakovsky 
                                   他们逐行向我付款。

这里有两个if可以提高可读性(是的,因为代码可以由具有虐待狂倾向的人支持),并且我们做self.custom_handlers .__得到__(self,self .__ class__),以防万一我们决定存储在@property类中的处理程序。

例如,像这样:

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

如果我们不做self.custom_handlers .__得到__(...),那么我们将得到类似<property object at 0x7f78d844f9b0>的内容,而不是@property的内容。

实际上,以上示例显示了如何连接自定义处理程序。 通常,此操作如下:

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

对于类(如果要传递属性/方法),我们需要考虑的是,在这样初始化装饰器阶段 ,方法和属性并不是简单的函数。 因此,我们只能传达上面宣布的内容。 因此,@ property选项是可以自行使用所有代码较低的功能的功能。 好吧,如果不需要self,也可以使用lambda。

对于异步代码,以上所有示例都是正确的。

最后,我想提请您注意以下事实:如果异常在其路径中未遇到自定义处理程序,那么它只会引发进一步的变化。

等待您的评论。 感谢您关注我的文章。

Source: https://habr.com/ru/post/zh-CN473596/


All Articles