[Async-sig] async generator confusion or bug?

Guido van Rossum guido at python.org
Mon Jun 26 22:46:53 EDT 2017


It does look complicated. The crux of the problem seems to be that helper()
is essentially

async def helper():
  g = generator()
  await g.asend(None)
  await asyncio.gather(user("user-0", g), user("user-1", g), user("user-2",
g))

which means that it is attempting to wait for three calls to user()
concurrently. This feels to me similar to three threads attempting to call
next() or send() on the same generator in parallel, which AFAIR is
explicitly guarded against somewhere. So detecting disallowing a similar
situation for async generators makes sense (and can be considered a bugfix).

On Mon, Jun 26, 2017 at 6:54 PM, Yury Selivanov <yselivanov at gmail.com>
wrote:

> (Posting here, rather than to the issue, because I think this actually
> needs more exposure).
>
> I looked at the code (genobject.c) and I think I know what's going on
> here.  Normally, when you work with an asynchronous generator (AG) you
> interact with it through "asend" or "athrow" *coroutines*.
>
> Each AG has its own private state, and when you await on "asend" coroutine
> you are changing that state.  The state changes on each "asend.send" or
> "asend.throw" call.  The normal relation between AGs and asends is 1 to 1.
>
>   AG - asend
>
> However, in your example you change that to 1 to many:
>
>      asend
>     /
>   AG - asend
>     \
>      asend
>
> Both 'ensure_future' and 'gather' will wrap each asend coroutine into an
> 'asyncio.Task'. And each Task will call "asend.send(None)" right in its
> '__init__', which changes the underlying *shared* AG instance completely
> out of order.
>
> I don't see how this can be fixed (or that it even needs to be fixed), so
> I propose to simply raise an exception if an AG has more than one asends
> changing it state *at the same time*.
>
> Thoughts?
>
> Yury
>
> > On Jun 26, 2017, at 12:25 PM, Dima Tisnek <dimaqq at gmail.com> wrote:
> >
> > Hi group,
> >
> > I'm trying to cross-use an sync generator across several async functions.
> > Is it allowed or a completely bad idea? (if so, why?)
> >
> > Here's MRE:
> >
> > import asyncio
> >
> >
> > async def generator():
> >    while True:
> >        x = yield
> >        print("received", x)
> >        await asyncio.sleep(0.1)
> >
> >
> > async def user(name, g):
> >    print("sending", name)
> >    await g.asend(name)
> >
> >
> > async def helper():
> >    g = generator()
> >    await g.asend(None)
> >
> >    await asyncio.gather(*[user(f"user-{x}", g) for x in range(3)])
> >
> >
> > if __name__ == "__main__":
> >    asyncio.get_event_loop().run_until_complete(helper())
> >
> >
> > And the output it produces when ran (py3.6.1):
> >
> > sending user-1
> > received user-1
> > sending user-2
> > sending user-0
> > received None
> > received None
> >
> >
> > Where are those None's coming from in the end?
> > Where did "user-0" and "user-1" data go?
> >
> > Is this a bug, or am I hopelessly confused?
> > Thanks!
> > _______________________________________________
> > Async-sig mailing list
> > Async-sig at python.org
> > https://mail.python.org/mailman/listinfo/async-sig
> > Code of Conduct: https://www.python.org/psf/codeofconduct/
>
> _______________________________________________
> Async-sig mailing list
> Async-sig at python.org
> https://mail.python.org/mailman/listinfo/async-sig
> Code of Conduct: https://www.python.org/psf/codeofconduct/
>



-- 
--Guido van Rossum (python.org/~guido)
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/async-sig/attachments/20170626/b6e763af/attachment.html>


More information about the Async-sig mailing list