From 303b9ffd31997c5ed982212e230bf50a559a5381 Mon Sep 17 00:00:00 2001 From: Michael Seifert Date: Sat, 25 Feb 2023 09:58:55 +0100 Subject: [PATCH 1/2] [docs] Corrected comment on when a RuntimeError occurs upon calling get_event_loop. Signed-off-by: Michael Seifert --- pytest_asyncio/plugin.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pytest_asyncio/plugin.py b/pytest_asyncio/plugin.py index 0b6fa9db..2a82328d 100644 --- a/pytest_asyncio/plugin.py +++ b/pytest_asyncio/plugin.py @@ -397,7 +397,9 @@ def pytest_fixture_setup( if old_loop is not loop: old_loop.close() except RuntimeError: - # Swallow this, since it's probably bad event loop hygiene. + # Either the current event loop has been set to None + # or the loop policy doesn't specify to create new loops + # or we're not in the main thread pass policy.set_event_loop(loop) return From 0be5e4dcc38f482c758acefc2e53251a5d060911 Mon Sep 17 00:00:00 2001 From: Michael Seifert Date: Thu, 16 Mar 2023 16:45:46 +0100 Subject: [PATCH 2/2] [refactor] Turn ResourceWarning for unclosed event loops into a DeprecationWarning. Changing the warning type emphasizes that the functionality will no longer be available in the future. The commit also extends the tests for the deprecation warning and adjusts the changlog entry and deprecation message. Signed-off-by: Michael Seifert --- docs/source/reference/changelog.rst | 5 ++++- pytest_asyncio/plugin.py | 18 +++++++--------- tests/test_event_loop_fixture_finalizer.py | 25 ++++++++++++++++++++-- 3 files changed, 35 insertions(+), 13 deletions(-) diff --git a/docs/source/reference/changelog.rst b/docs/source/reference/changelog.rst index 851bbea0..ec681a24 100644 --- a/docs/source/reference/changelog.rst +++ b/docs/source/reference/changelog.rst @@ -5,7 +5,10 @@ Changelog UNRELEASED ================= - Drop compatibility with pytest 6.1. Pytest-asyncio now depends on pytest 7.0 or newer. -- event_loop fixture teardown emits a ResourceWarning when the current event loop has not been closed. +- pytest-asyncio cleans up any stale event loops when setting up and tearing down the + event_loop fixture. This behavior has been deprecated and pytest-asyncio emits a + DeprecationWarning when tearing down the event_loop fixture and current event loop + has not been closed. 0.20.3 (22-12-08) ================= diff --git a/pytest_asyncio/plugin.py b/pytest_asyncio/plugin.py index 2a82328d..c0aa4261 100644 --- a/pytest_asyncio/plugin.py +++ b/pytest_asyncio/plugin.py @@ -422,11 +422,13 @@ def _add_finalizers(fixturedef: FixtureDef, *finalizers: Callable[[], object]) - _UNCLOSED_EVENT_LOOP_WARNING = dedent( """\ - unclosed event loop %r. - Possible causes are: - 1. A custom "event_loop" fixture is used which doesn't close the loop - 2. Your code or one of your dependencies created a new event loop during - the test run + pytest-asyncio detected an unclosed event loop when tearing down the event_loop + fixture: %r + pytest-asyncio will close the event loop for you, but future versions of the + library will no longer do so. In order to ensure compatibility with future + versions, please make sure that: + 1. Any custom "event_loop" fixture properly closes the loop after yielding it + 2. Your code does not modify the event loop in async fixtures or tests """ ) @@ -438,14 +440,10 @@ def _close_event_loop() -> None: except RuntimeError: loop = None if loop is not None: - # Emit ResourceWarnings in the context of the fixture/test case - # rather than waiting for the interpreter to trigger the warning when - # garbage collecting the event loop. if not loop.is_closed(): warnings.warn( _UNCLOSED_EVENT_LOOP_WARNING % loop, - ResourceWarning, - source=loop, + DeprecationWarning, ) loop.close() diff --git a/tests/test_event_loop_fixture_finalizer.py b/tests/test_event_loop_fixture_finalizer.py index 2d12f7f4..b676df2d 100644 --- a/tests/test_event_loop_fixture_finalizer.py +++ b/tests/test_event_loop_fixture_finalizer.py @@ -88,7 +88,7 @@ async def test_async_with_explicit_fixture_request(event_loop): result.assert_outcomes(passed=1) -def test_event_loop_fixture_finalizer_raises_warning_when_loop_is_unclosed( +def test_event_loop_fixture_finalizer_raises_warning_when_fixture_leaves_loop_unclosed( pytester: Pytester, ): pytester.makepyfile( @@ -96,7 +96,6 @@ def test_event_loop_fixture_finalizer_raises_warning_when_loop_is_unclosed( """\ import asyncio import pytest - import pytest_asyncio pytest_plugins = 'pytest_asyncio' @@ -114,3 +113,25 @@ async def test_ends_with_unclosed_loop(): result = pytester.runpytest("--asyncio-mode=strict", "-W", "default") result.assert_outcomes(passed=1, warnings=1) result.stdout.fnmatch_lines("*unclosed event loop*") + + +def test_event_loop_fixture_finalizer_raises_warning_when_test_leaves_loop_unclosed( + pytester: Pytester, +): + pytester.makepyfile( + dedent( + """\ + import asyncio + import pytest + + pytest_plugins = 'pytest_asyncio' + + @pytest.mark.asyncio + async def test_ends_with_unclosed_loop(): + asyncio.set_event_loop(asyncio.new_event_loop()) + """ + ) + ) + result = pytester.runpytest("--asyncio-mode=strict", "-W", "default") + result.assert_outcomes(passed=1, warnings=1) + result.stdout.fnmatch_lines("*unclosed event loop*")