user2297550
user2297550

Reputation: 3356

yield from asyncio.sleep works, but yield from async def fails

The following code works fine:

import asyncio
loop = asyncio.get_event_loop()
async def a ():
    print('hello')
def b ():
    yield from asyncio.sleep(1)
loop.run_until_complete(b())
loop.close()
print('done')

But, the following fails:

import asyncio
loop = asyncio.get_event_loop()
async def a ():
    print('hello')
def b ():
    yield from a() # <=========== only 1 tiny change
loop.run_until_complete(b())
loop.close()
print('done')

Decorating b with @asyncio.coroutine makes it work.

But, the question is why does the first piece of code work fine without the @asyncio.coroutine decorator? The docs clearly say that asyncio.sleep is a coroutine, and so is a, so why does the code fail in one case and work fine for the other case?

Upvotes: 1

Views: 1036

Answers (1)

Udi
Udi

Reputation: 30492

Your code produces the following error:

...
        yield from a()  # <=========== only 1 tiny change
TypeError: cannot 'yield from' a coroutine object in a non-coroutine generator

As clearly stated in the error message, when using asyncio, you should either use @coroutine or async def to mark your coroutines. In async defs, await should be used instead of yield from:

import asyncio


async def a():
    print('hello')


async def b():
    await a()


loop = asyncio.get_event_loop()
loop.run_until_complete(b())
loop.close()
print('done')

or, for python 3.4:

import asyncio

@asyncio.coroutine
def a():
    print('hello')


@asyncio.coroutine
def b():
    yield from a()


loop = asyncio.get_event_loop()
loop.run_until_complete(b())
loop.close()

print('done')

Your first example is considered "buggy", but it is executing "properly" because run_until_complete calls iscoroutine which currently returns True for generators (any def with a yield/yield from), but this is an implementation detail that might change in a future version of python. Using @couroutine on def a() (instead of async def a()), or even just adding yield from asyncio.sleep(1) to a regular def a() would make your second example run as well. Currently python might be "merciful" when using generators which are not marked as coroutines in asyncio, but not when using async defs.

Upvotes: 4

Related Questions