Reputation: 61225
I have a decorator that decorate a coroutine function and assign the value returned by the coroutine to a future instance.
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
Demo:
>>> 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
As you can see I need to run two times the event loop in order to get the future result. Is there any way to make the event loop return the value after the first run? I don't want to await in my code and assign the result(function) to a name.
Upvotes: 3
Views: 1232
Reputation: 21453
in your code you have made your decorator
wrapper to be async
which is not what you wanted, it means that any time you use your wrapper it gives back a coroutine object that will generate the wrapped function:
>>> 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
so just remove the async
in the line
async def decorator(asyncfunc):
And your problem will be solved:
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>
Upvotes: 1