Skip to content

Commit 569b29c

Browse files
committed
Avoid errors in cleanup when event loop is already closed
check `is_closed()` before calling cleanup methods and catch errors in cleanup to avoid problems # Conflicts: # docs/reference/changelog.rst
1 parent fe32d2d commit 569b29c

File tree

3 files changed

+42
-5
lines changed

3 files changed

+42
-5
lines changed

docs/reference/changelog.rst

+2-1
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,10 @@ Changelog
44

55
0.26.0 (UNRELEASED)
66
===================
7-
- Adds configuration option that sets default event loop scope for all testss `#793 <https://github.com/pytest-dev/pytest-asyncio/issues/793>`_
7+
- Adds configuration option that sets default event loop scope for all tests `#793 <https://github.com/pytest-dev/pytest-asyncio/issues/793>`_
88
- Improved type annotations for ``pytest_asyncio.fixture`` `#1045 <https://github.com/pytest-dev/pytest-asyncio/pull/1045>`_
99
- Added ``typing-extensions`` as additional dependency for Python ``<3.10`` `#1045 <https://github.com/pytest-dev/pytest-asyncio/pull/1045>`_
10+
- Avoid errors in cleanup when event loop is already closed `#1051 <https://github.com/pytest-dev/pytest-asyncio/issues/1051>`_
1011

1112

1213
0.25.2 (2025-01-08)

pytest_asyncio/plugin.py

+8-4
Original file line numberDiff line numberDiff line change
@@ -1190,10 +1190,14 @@ def _provide_event_loop() -> Iterator[asyncio.AbstractEventLoop]:
11901190
try:
11911191
yield loop
11921192
finally:
1193-
try:
1194-
loop.run_until_complete(loop.shutdown_asyncgens())
1195-
finally:
1196-
loop.close()
1193+
# cleanup the event loop if it hasn't been cleaned up already
1194+
if not loop.is_closed():
1195+
try:
1196+
loop.run_until_complete(loop.shutdown_asyncgens())
1197+
except Exception as e:
1198+
warnings.warn(f"Error cleaning up asyncio loop: {e}", RuntimeWarning)
1199+
finally:
1200+
loop.close()
11971201

11981202

11991203
@pytest.fixture(scope="session")

tests/test_event_loop_fixture.py

+32
Original file line numberDiff line numberDiff line change
@@ -80,3 +80,35 @@ async def generator_fn():
8080
)
8181
result = pytester.runpytest_subprocess("--asyncio-mode=strict", "-W", "default")
8282
result.assert_outcomes(passed=1, warnings=0)
83+
84+
85+
def test_event_loop_already_closed(
86+
pytester: Pytester,
87+
):
88+
pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function")
89+
pytester.makepyfile(
90+
dedent(
91+
"""\
92+
import asyncio
93+
import pytest
94+
import pytest_asyncio
95+
pytest_plugins = 'pytest_asyncio'
96+
97+
@pytest_asyncio.fixture
98+
async def _event_loop():
99+
return asyncio.get_running_loop()
100+
101+
@pytest.fixture
102+
def cleanup_after(_event_loop):
103+
yield
104+
# fixture has its own cleanup code
105+
_event_loop.close()
106+
107+
@pytest.mark.asyncio
108+
async def test_something(cleanup_after):
109+
await asyncio.sleep(0.01)
110+
"""
111+
)
112+
)
113+
result = pytester.runpytest_subprocess("--asyncio-mode=strict", "-W", "default")
114+
result.assert_outcomes(passed=1, warnings=0)

0 commit comments

Comments
 (0)