Skip to content

AsyncGenerator early exit doesn't raise CancelledError and doesn't run finally branch #759

Closed
@zpz

Description

@zpz

This code:

import asyncio

async def gen():
    try:
        for x in range(100):
            try:
                yield x
            except BaseException as e:
                print('raised', repr(e))
                raise
    finally:
        print('cleaning up!')


async def test_earlystop():
    async for x in gen():
        print(x)
        if x > 8:
            break
    assert True


if __name__ == '__main__':
    asyncio.run(test_earlystop())

Run it:

$ python debug.py 
0
1
2
3
4
5
6
7
8
9
raised CancelledError()
cleaning up!

I was surprised to see CancelledError rather than GeneratorExit but it's OK. Now this code:

import asyncio
import pytest

async def gen():
    try:
        for x in range(100):
            try:
                yield x
            except BaseException as e:
                print('raised', repr(e))
                raise
    finally:
        print('cleaning up!')


@pytest.mark.asyncio
async def test_earlystop():
    async for x in gen():
        print(x)
        if x > 8:
            break
    assert True

Run it,

$ py.test -sv debug.py 
=================================================================== test session starts ====================================================================
platform linux -- Python 3.10.12, pytest-7.4.4, pluggy-1.3.0 -- /usr/bin/python
rootdir: /home/docker-user/mpservice
configfile: pyproject.toml
plugins: asyncio-0.23.3, cov-4.1.0, anyio-4.2.0, Faker-22.4.0, pudb-0.7.0
asyncio: mode=strict
collected 1 item                                                                                                                                           

debug.py::test_earlystop 0
1
2
3
4
5
6
7
8
9
PASSED
-------------------------------------------------------------------- live log teardown ---------------------------------------------------------------------
ERROR    asyncio:base_events.py:1758 Task was destroyed but it is pending!
task: <Task pending name='Task-2' coro=<<async_generator_athrow without __name__>()>>


=================================================================== slowest 3 durations ====================================================================
0.11s setup    tests/debug.py::test_earlystop
0.00s teardown tests/debug.py::test_earlystop
0.00s call     tests/debug.py::test_earlystop
==================================================================== 1 passed in 0.18s =====================================================================

Now I don't understand why

  1. The CancelledError was not raised, it appears
  2. The finally block was not executed, it appears

Of course, that "destroyed but it is pending" is also unpleasant to see.

Is there a bug in pytest-asyncio or am I missing something? I think this code should simply pass with no drama of any kind.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions