I actually thought that async generators already guarded against this using their ag_running attribute. If I try running Dima's example with async_generator, I get:
sending user-1 received user-1 sending user-2 sending user-0 Traceback (most recent call last): [...] ValueError: async generator already executing The relevant code is here: https://github.com/njsmith/async_generator/blob/e303e077c9dcb5880c0ce9930d560b282f8288ec/async_generator/impl.py#L273-L279 But I added this in the first place because I thought it was needed for compatibility with native async generators :-) -n On Jun 26, 2017 6:54 PM, "Yury Selivanov" <yseliva...@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 <dim...@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@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@python.org https://mail.python.org/mailman/listinfo/async-sig Code of Conduct: https://www.python.org/psf/codeofconduct/