You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让普通装饰器兼容异步函数,解决uplink.returns.json异步适配问题

解决方案

核心问题原因

你之前的实现错误在于装饰器执行顺序:把不支持异步的returns.json套在了异步wrapper外层,导致它拿到的仍然是协程对象,完全没有解决核心矛盾。正确逻辑是先获取到函数的实际返回值(异步函数需await拿到结果),再将返回值交给原同步装饰器处理。

通用适配方案

可以写一个通用的包装工具,将任意仅支持同步函数的第三方装饰器,转为同时兼容同步/异步函数的版本:

import asyncio
from functools import wraps

def make_async_compatible(sync_decorator):
    def decorator(fn):
        if asyncio.iscoroutinefunction(fn):
            @wraps(fn)
            async def async_wrapper(*args, **kwargs):
                # 先await拿到异步函数的实际返回结果
                raw_result = await fn(*args, **kwargs)
                # 构造临时同步函数,套入原装饰器处理结果
                @sync_decorator
                def temp_sync_func(_):
                    return raw_result
                return temp_sync_func(None)
            return async_wrapper
        else:
            # 同步函数直接套用原装饰器
            return sync_decorator(fn)
    return decorator

针对uplink场景的具体实现

# 先将uplink原生的json返回装饰器转为异步兼容版本
async_friendly_json = make_async_compatible(uplink.returns.json(key="token"))

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()

        super().__init__(client=self.client, **kwargs)

        if auto_auth:
            if self.sync:
                self._login(password)
            else:
                # 注意:异步模式下不能在同步的__init__中直接调用异步方法,需要上层调用者手动await _login,或单独实现异步初始化逻辑
                pass

    # 替换原生装饰器即可
    @async_friendly_json
    @uplink.post("router/login")
    def _login(self, pwd: uplink.Field):
        """获取token"""

额外注意事项

如果不想用通用适配方案,也可以直接读uplink源码找到json解析的底层方法,跳过临时函数的构造直接调用解析逻辑,性能会更好。

内容的提问来源于stack exchange,提问作者Harvs

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.12 15:24:03