Hello!
Can anybody explain to me this part of asyncio.coroutine code:
<https://github.com/python/asyncio/blob/3b6a64a9fb6ec4ad0c984532aa776b130067c901/asyncio/coroutines.py#L201>
# asyncio/coroutines.py:201:
def coroutine(func):
"""Decorator to mark coroutines.
If the coroutine is not yielded from before it is destroyed,
an error message is logged.
"""
if _inspect_iscoroutinefunction(func):
# In Python 3.5 that's all we need to do for coroutines
# defiend with "async def".
# Wrapping in CoroWrapper will happen via
# 'sys.set_coroutine_wrapper' function.
return func
if inspect.isgeneratorfunction(func):
coro = func
else:
@functools.wraps(func)
def coro(*args, **kw):
res = func(*args, **kw)
if isinstance(res, futures.Future) or inspect.isgenerator(res):
# <--- This part
res = yield from res
...
What does this code: if wrapped by asyncio.coroutine function is not
generator and returns generator, Future or awaitable object,
then this coroutine function will yield from returned value.
E.g. following coroutine function will actually return result of the
future, not future itself:
@asyncio.coroutine
def return_future():
fut = asyncio.Future()
fut.set_result("return_future")
return fut
I can't find where such behaviour is documented? Is this a desired behavior?
According to
<https://docs.python.org/3/library/asyncio-task.html?highlight=iscoroutine#coroutines>:
> Coroutines used with asyncio may be implemented using the async def
statement, or by using generators.
> ...
> Generator-based coroutines should be decorated with @asyncio.coroutine,
although this is not strictly enforced.
So technically, according to this part of documentation, it's not
explicitly allowed to use asyncio.coroutine with non-generator functions.
Special behaviour when asyncio.coroutine-wrapped non-generator function
returns generator or Future object
leads to inconsistent behaviour on Python 3.4 when asyncio debugging is
enabled and not enabled.
Consider following function:
@asyncio.coroutine
def return_coroutine_object():
@asyncio.coroutine
def g():
yield from asyncio.sleep(0.01)
return "return_coroutine_object"
return g()
When debugging is disabled following code will work:
assert loop.run_until_complete(return_coroutine_object()) ==
"return_coroutine_object"
When debugging is enabled same code will not work on Python 3.4,
because result of "g()" is not a generator or Future --- it's debugging
CoroWrapper.
Outer asyncio.coroutine will not yield from CoroWrapper and will return
inner "g()" CoroWrapper.
I made complete example demonstrating this issue:
<https://gist.github.com/rutsky/c72be2edeb1c8256d680>
This issue is not reproduced in Python 3.5, since CoroWrapper is awaitable
in Python 3.5,
but still I can't find is this is a desired behaviour and why?
Regards,
Vladimir Rutsky