On Tue, Jul 28, 2015 at 1:17 PM, Javier <jcarm...@gmail.com> wrote: > Hello again. I have been investigating a bit your example. I don't understand > why I can't write something like this: > > -------- > > import asyncio > > def foo(): > print("start foo") > try: > while True: > val = yield > print("foo:", val) > yield from asyncio.sleep(3) > except GeneratorExit: > print("foo closed") > print("exit foo") > > def bar(next): > print("start bar") > next.send(None) > try: > while True: > val = yield > next.send("bar/"+val) > except GeneratorExit: > print("bar closed") > print("exit bar") > > def fun(next): > next.send(None) > for e in ["hello", "world", "I'm", "pythonist"]: > next.send(e) > > @asyncio.coroutine > def run(): > fun(bar(foo())) > > loop = asyncio.get_event_loop() > loop.run_until_complete(run()) > loop.close()
Because "yield from asyncio.sleep(3)" doesn't magically pause the coroutine as you want it to. It yields a future, which is meant to be yielded back up the coroutine chain to the event loop. The event loop would then resume the coroutine once the future is done, which in the case of asyncio.sleep will happen after the sleep timer completes. In your example, the future never makes it back to the event loop. asyncio.sleep yields the future to foo, and since foo is suspended by a yield from, foo yields the future to bar, where it is the result of the next.send call. The return value of next.send is ignored, so the future just gets dropped on the floor at this point. bar yields to fun, which sends bar the next string in its list, "world". bar sends "world" to foo, and since foo is still suspended by a yield from, it sends "world" on to the asyncio.sleep future. Not a new asyncio.sleep future, but the same one that it's still yielding from. The future then realizes that something is wrong, because its generator code is running again but it doesn't have a result yet, so it throws that AssertionError. If you want the yield from in foo to work properly, then you need to make sure that the future gets back to the event loop, and if you do that in some tricky way other than a yield from chain, you'll also need to make sure that you're not trying to send it more data before foo has resumed. > I think this is a big flaw in python/asyncio design. I don't entirely disagree. I think that the implementation of async coroutines on top of synchronous coroutines on top of generators is overly clever and results in a somewhat leaky abstraction and a fair amount of confusion. -- https://mail.python.org/mailman/listinfo/python-list