
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/e303e077c9dcb5880c0ce9930d56... 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" <yselivanov@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@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/