classification
Title: asyncio.run() error related to finalizing async generators
Type: behavior Stage: resolved
Components: asyncio Versions: Python 3.8, Python 3.7
process
Status: closed Resolution: duplicate
Dependencies: Superseder: AsyncGenerator breaks when not iterated fully with RuntimeError("can't send non-None value to a just-started coroutine")
View: 38013
Assigned To: Nosy List: asvetlov, samfrances, xtreak, yselivanov
Priority: normal Keywords:

Created on 2019-06-30 23:53 by samfrances, last changed 2019-09-12 21:35 by xtreak. This issue is now closed.

Messages (5)
msg346943 - (view) Author: Sam Frances (samfrances) Date: 2019-06-30 23:53
The documentation for `asyncio.run()` states:

"This function runs the passed coroutine, taking care of managing the asyncio event loop and finalizing asynchronous generators."

However, the following example seems to indicate that async generators are not being cleared up correctly.

```
import asyncio


async def count():
    try:
        i = 0
        while True:
            yield i
            i += 1
    finally:
        print("count() cleanup")


async def double(source):
    try:
        async for n in source:
            yield n * 2
    finally:
        print("double() cleanup")


async def anext(aiter):
    return await aiter.__anext__()


async def main():
    async for i in double(count()):
        if i > 10:
            return
        print(i)

asyncio.run(main())

```

The result is:

```
$ python example.py 
0
2
4
6
8
10
unhandled exception during asyncio.run() shutdown
task: <Task finished name='Task-2' coro=<<async_generator_athrow without __name__>()> exception=RuntimeError("can't send non-None value to a just-started coroutine")>
RuntimeError: can't send non-None value to a just-started coroutine
count() cleanup

```

The above error is from Python 3.8.0b1+, but the exact same error occurs in Python 3.7.

I'm not sure if this is a bug or if I am misunderstanding the intended behaviour of `asyncio.run()`, but the behaviour was certainly surprising to me.
msg346944 - (view) Author: Sam Frances (samfrances) Date: 2019-06-30 23:55
Apologies for the stray unused function in the example. It should have read:

```
import asyncio


async def count():
    try:
        i = 0
        while True:
            yield i
            i += 1
    finally:
        print("count() cleanup")


async def double(source):
    try:
        async for n in source:
            yield n * 2
    finally:
        print("double() cleanup")


async def main():
    async for i in double(count()):
        if i > 10:
            return
        print(i)

asyncio.run(main())
```
msg346946 - (view) Author: Sam Frances (samfrances) Date: 2019-07-01 00:06
One final note: changing the `main()` function from the example to add a zero-sleep seems to fix it, but this seems like a rather ad-hoc solution.

```
async def main():
    async for i in double(count()):
        if i > 10:
            break
        print(i)
    await asyncio.sleep(0)
```

result:

```
$ python example.py 
0
2
4
6
8
10
double() cleanup
count() cleanup
```
msg352215 - (view) Author: Karthikeyan Singaravelan (xtreak) * (Python triager) Date: 2019-09-12 16:43
I think this would be fixed by issue38013. Andrew I feel these are the same issues here and can be closed.
msg352222 - (view) Author: Andrew Svetlov (asvetlov) * (Python committer) Date: 2019-09-12 17:46
Yes, please close as a duplicate.
History
Date User Action Args
2019-09-12 21:35:01xtreaksetstatus: open -> closed
superseder: AsyncGenerator breaks when not iterated fully with RuntimeError("can't send non-None value to a just-started coroutine")
resolution: duplicate
stage: resolved
2019-09-12 17:46:52asvetlovsetmessages: + msg352222
2019-09-12 16:43:28xtreaksetnosy: + xtreak
messages: + msg352215
2019-07-01 00:06:54samfrancessetmessages: + msg346946
2019-06-30 23:55:50samfrancessetmessages: + msg346944
2019-06-30 23:53:08samfrancescreate