Return 使用带有修饰协程的单个事件循环的未来结果

Return a future result using single event loop with decorated coroutine

我有一个装饰器,它装饰协程函数并将协程 return 编辑的值分配给未来的实例。

import asyncio
import functools


def ensure_prepare(future):
    async def decorator(asyncfunc):
        @functools.wraps(asyncfunc)
        async def wrapper(*args, **kwargs):
            future.set_result(await asyncfunc(*args, **kwargs))
            return future
        return wrapper
    return decorator

演示:

>>> future = asyncio.Future()
>>> 
>>> @ensure_prepare(future)
... async def check_sanity():
...     return 9
... 
>>> loop = asyncio.get_event_loop()
>>> loop.run_until_complete(check_sanity)
<function check_sanity at 0x7f935300a158>
>>> _()
<coroutine object check_sanity at 0x7f934f78a728>
>>> loop.run_until_complete(_)
<Future finished result=9>
>>> _.result()
9

如您所见,我需要 运行 两次事件循环才能获得未来的结果。有没有办法让事件循环return成为第一个运行之后的值?我不想在我的代码中 await 并将结果(函数)分配给一个名称。

在您的代码中,您已将 decorator 包装器设为 async,这不是您想要的,这意味着无论何时您使用包装器,它都会返回一个协程对象,该对象将生成包装函数:

>>> future = asyncio.Future()
>>> @ensure_prepare(future)
async def chech_sanity():
    return 9

>>> check_sanity
<coroutine object ensure_prepare.<locals>.decorator at 0x10572f4c0>

>>> check_sanity.send(None) #advance coroutine
Traceback (most recent call last):
  File "<pyshell#7>", line 1, in <module>
    check_sanity.send(None) #advance coroutine
StopIteration: <function check_sanity at 0x105096a60>

               # ^ the function is the result of the coroutine

所以只需删除行

中的async
async def decorator(asyncfunc):

你的问题将得到解决:

def ensure_prepare(future):
    def decorator(asyncfunc):
        @functools.wraps(asyncfunc)
        async def wrapper(*args, **kwargs):
            future.set_result(await asyncfunc(*args, **kwargs))
            return future
        return wrapper
    return decorator


>>> future = asyncio.Future()
>>> @ensure_prepare(future)
async def check_sanity():
    return 9

>>> chech_sanity
<function check_sanity at 0x105784a60>
>>> loop = asyncio.get_event_loop()
>>> loop.run_until_complete(check_sanity()) #remember to call check_sanity!
<Future finished result=9>