如何使decorator使用异步函数?

2024-04-19 18:41:06 发布

您现在位置:Python中文网/ 问答频道 /正文

一般问题:

我有一个装饰程序@some_package.decorator,它期望函数包装为返回类型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)

但是,函数可以是syncasync,因此可以返回coroutine[Any]

如何使decoratorawait成为函数

我的特定用例: 我有一些代码在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()和异步函数时。因此,decorator 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

Tags: 函数selfjsonasyncreturndefargsfunction