首页 > 解决方案 > 如何使装饰器与异步功能一起使用?

问题描述

一般问题:

我有一个装饰器@some_package.decorator,它希望函数包装返回 type T

### runs
@some_package.decorator
def my_func(param) -> int:
    return len(param)

### I also want to be able to wrap an async function
@some_package.decorator
async def my_func(param):
    await asyncio.sleep(1)
    return len(param)

但是,该函数可以是syncor async,因此可以返回coroutine[Any]

我怎样才能使装饰器await的功能

我的具体用例:我有一些代码在sync=True

import uplink
class MyAPI(Consumer):
        def __init__(
        self,
        password: str,
        sync: bool = True,
        auto_auth: bool = True,
        **kwargs
    ):
        self.sync = sync
        self.client = None

        if not self.sync:
            self.client = AiohttpClient()

        # initialise the super class
        super(GLinet, self).__init__(client=self.client, **kwargs)

        if auto_auth:
                self._login(password)

    @uplink.returns.json(key="token")
    @uplink.post("router/login")
    def _login(self, pwd: uplink.Field):
        """fetches token"""

但是,当sync=False我使用的 API 生成 _login() 和异步函数时。因此,装饰器uplink.returns.json变得不安,因为它期望它包装的函数来证明一个可以解析的响应,但它却收到了一个协程。

如何使uplink.returns.json接受异步函数并等待它可以解析的返回值。

我尝试过这样的事情

def dec(fn):
    if asyncio.iscoroutinefunction(fn):
        @returns.json
        async def wrapper(*args, **kwargs):
            print("wrapping async function")
            print(fn)
            return await fn()
        return wrapper
    else:
        @returns.json
        def wrapper(*args, **kwargs):
            print("wrapping sync function")
            print(fn)
            return fn()
        return wrapper

有些人开发了自己的装饰器,可以同时使用这两种功能,但我还没有找到任何修改过另一个装饰器的人

def dec(fn):
    if asyncio.iscoroutinefunction(fn):
        @wraps(fn)
        async def wrapper(*args, **kwargs):
            print(fn, args, kwargs)  # <function foo at 0x10952d598> () {}
            await asyncio.sleep(5)
            print("done with wrapper, going to call fn")
            return await fn()
        return wrapper
    else:
        @wraps(fn)
        def wrapper(*args, **kwargs):
            print(fn, args, kwargs)  # <function bar at 0x108fb5a60> () {}
            time.sleep(5)
            print("done with wrapper, going to call fn")
            return fn()
        return wrapper

标签: pythonapiasync-awaitpython-asyncio

解决方案


推荐阅读