Send data to asyncio coroutine

Javier jcarmena at gmail.com
Wed Jul 29 16:24:12 CEST 2015


El miércoles, 29 de julio de 2015, 1:07:22 (UTC+2), Ian  escribió:
> On Tue, Jul 28, 2015 at 1:17 PM, Javier <jcarmena at 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.



I see, so I was wrong. I used to see event loop, and yield from as a language improvement but it's actually a library improvement that, as a side effect, prevents you from usign some language features like ordinary generator/coroutine communication. That is a pity. 

Thank you very much for your time and explanations, now I understand some points. I'll keep on learning asyncio.


More information about the Python-list mailing list