From c3ca1f398d1665b22258521b9d0b1353817f013b Mon Sep 17 00:00:00 2001 From: Michael Seifert Date: Fri, 2 May 2025 12:04:03 +0200 Subject: [PATCH 01/10] feat!: Removed event_loop fixture. --- docs/reference/fixtures/event_loop_example.py | 5 - docs/reference/fixtures/index.rst | 27 --- pytest_asyncio/plugin.py | 34 ++-- .../test_async_fixtures_scope.py | 4 +- .../async_fixtures/test_parametrized_loop.py | 48 ----- tests/conftest.py | 31 ---- tests/hypothesis/test_base.py | 37 ---- tests/loop_fixture_scope/__init__.py | 0 tests/loop_fixture_scope/conftest.py | 17 -- .../test_loop_fixture_scope.py | 19 -- tests/markers/test_function_scope.py | 23 --- tests/markers/test_module_scope.py | 53 ------ tests/test_dependent_fixtures.py | 16 -- tests/test_event_loop_fixture_finalizer.py | 149 ---------------- ...event_loop_fixture_override_deprecation.py | 113 ------------ ...est_explicit_event_loop_fixture_request.py | 168 ------------------ tests/test_multiloop.py | 72 -------- tests/test_simple.py | 7 - 18 files changed, 26 insertions(+), 797 deletions(-) delete mode 100644 docs/reference/fixtures/event_loop_example.py delete mode 100644 tests/async_fixtures/test_parametrized_loop.py delete mode 100644 tests/loop_fixture_scope/__init__.py delete mode 100644 tests/loop_fixture_scope/conftest.py delete mode 100644 tests/loop_fixture_scope/test_loop_fixture_scope.py delete mode 100644 tests/test_dependent_fixtures.py delete mode 100644 tests/test_event_loop_fixture_finalizer.py delete mode 100644 tests/test_event_loop_fixture_override_deprecation.py delete mode 100644 tests/test_explicit_event_loop_fixture_request.py delete mode 100644 tests/test_multiloop.py diff --git a/docs/reference/fixtures/event_loop_example.py b/docs/reference/fixtures/event_loop_example.py deleted file mode 100644 index b5a82b62..00000000 --- a/docs/reference/fixtures/event_loop_example.py +++ /dev/null @@ -1,5 +0,0 @@ -import asyncio - - -def test_event_loop_fixture(event_loop): - event_loop.run_until_complete(asyncio.sleep(0)) diff --git a/docs/reference/fixtures/index.rst b/docs/reference/fixtures/index.rst index 04953783..3d151dcb 100644 --- a/docs/reference/fixtures/index.rst +++ b/docs/reference/fixtures/index.rst @@ -2,33 +2,6 @@ Fixtures ======== -event_loop -========== -*This fixture is deprecated.* - -*If you want to request an asyncio event loop with a scope other than function -scope, use the "loop_scope" argument to* :ref:`reference/markers/asyncio` *when marking the tests. -If you want to return different types of event loops, use the* :ref:`reference/fixtures/event_loop_policy` -*fixture.* - -Creates a new asyncio event loop based on the current event loop policy. The new loop -is available as the return value of this fixture for synchronous functions, or via `asyncio.get_running_loop `__ for asynchronous functions. -The event loop is closed when the fixture scope ends. -The fixture scope defaults to ``function`` scope. - -.. include:: event_loop_example.py - :code: python - -Note that, when using the ``event_loop`` fixture, you need to interact with the event loop using methods like ``event_loop.run_until_complete``. If you want to *await* code inside your test function, you need to write a coroutine and use it as a test function. The :ref:`asyncio ` marker -is used to mark coroutines that should be treated as test functions. - -If you need to change the type of the event loop, prefer setting a custom event loop policy over redefining the ``event_loop`` fixture. - -If the ``pytest.mark.asyncio`` decorator is applied to a test function, the ``event_loop`` -fixture will be requested automatically by the test function. - -.. _reference/fixtures/event_loop_policy: - event_loop_policy ================= Returns the event loop policy used to create asyncio event loops. diff --git a/pytest_asyncio/plugin.py b/pytest_asyncio/plugin.py index 8a1d8733..f50a7c8b 100644 --- a/pytest_asyncio/plugin.py +++ b/pytest_asyncio/plugin.py @@ -259,14 +259,17 @@ def _preprocess_async_fixtures( # Ignore async fixtures without explicit asyncio mark in strict mode # This applies to pytest_trio fixtures, for example continue - scope = ( + loop_scope = ( getattr(func, "_loop_scope", None) or default_loop_scope or fixturedef.scope ) - if scope == "function" and "event_loop" not in fixturedef.argnames: - fixturedef.argnames += ("event_loop",) - _make_asyncio_fixture_function(func, scope) + if ( + loop_scope == "function" + and "_function_event_loop" not in fixturedef.argnames + ): + fixturedef.argnames += ("_function_event_loop",) + _make_asyncio_fixture_function(func, loop_scope) function_signature = inspect.signature(func) if "event_loop" in function_signature.parameters: warnings.warn( @@ -415,7 +418,7 @@ def _get_event_loop_fixture_id_for_async_fixture( getattr(func, "_loop_scope", None) or default_loop_scope or request.scope ) if loop_scope == "function": - event_loop_fixture_id = "event_loop" + event_loop_fixture_id = "_function_event_loop" else: event_loop_node = _retrieve_scope_root(request._pyfuncitem, loop_scope) event_loop_fixture_id = event_loop_node.stash.get( @@ -1101,7 +1104,7 @@ def pytest_runtest_setup(item: pytest.Item) -> None: parent_node = _retrieve_scope_root(item, scope) event_loop_fixture_id = parent_node.stash[_event_loop_fixture_id] else: - event_loop_fixture_id = "event_loop" + event_loop_fixture_id = "_function_event_loop" fixturenames = item.fixturenames # type: ignore[attr-defined] if event_loop_fixture_id not in fixturenames: fixturenames.append(event_loop_fixture_id) @@ -1170,11 +1173,20 @@ def _retrieve_scope_root(item: Collector | Item, scope: str) -> Collector: raise pytest.UsageError(error_message) -@pytest.fixture -def event_loop(request: FixtureRequest) -> Iterator[asyncio.AbstractEventLoop]: - """Create an instance of the default event loop for each test case.""" - new_loop_policy = request.getfixturevalue(event_loop_policy.__name__) - with _temporary_event_loop_policy(new_loop_policy), _provide_event_loop() as loop: +@pytest.fixture( + scope="function", + name="_function_event_loop", +) +def _function_event_loop( + *args, # Function needs to accept "cls" when collected by pytest.Class + event_loop_policy, +) -> Iterator[asyncio.AbstractEventLoop]: + new_loop_policy = event_loop_policy + with ( + _temporary_event_loop_policy(new_loop_policy), + _provide_event_loop() as loop, + ): + asyncio.set_event_loop(loop) yield loop diff --git a/tests/async_fixtures/test_async_fixtures_scope.py b/tests/async_fixtures/test_async_fixtures_scope.py index 7fbed781..6efd7e0a 100644 --- a/tests/async_fixtures/test_async_fixtures_scope.py +++ b/tests/async_fixtures/test_async_fixtures_scope.py @@ -9,6 +9,8 @@ import pytest +import pytest_asyncio + @pytest.fixture(scope="module") def event_loop(): @@ -18,7 +20,7 @@ def event_loop(): loop.close() -@pytest.fixture(scope="module") +@pytest_asyncio.fixture(scope="module", loop_scope="module") async def async_fixture(): await asyncio.sleep(0.1) return 1 diff --git a/tests/async_fixtures/test_parametrized_loop.py b/tests/async_fixtures/test_parametrized_loop.py deleted file mode 100644 index ca2cb5c7..00000000 --- a/tests/async_fixtures/test_parametrized_loop.py +++ /dev/null @@ -1,48 +0,0 @@ -from __future__ import annotations - -from textwrap import dedent - -from pytest import Pytester - - -def test_event_loop_parametrization(pytester: Pytester): - pytester.makepyfile( - dedent( - """\ - import asyncio - - import pytest - import pytest_asyncio - - TESTS_COUNT = 0 - - - def teardown_module(): - # parametrized 2 * 2 times: 2 for 'event_loop' and 2 for 'fix' - assert TESTS_COUNT == 4 - - - @pytest.fixture(scope="module", params=[1, 2]) - def event_loop(request): - request.param - loop = asyncio.new_event_loop() - yield loop - loop.close() - - - @pytest_asyncio.fixture(params=["a", "b"]) - async def fix(request): - await asyncio.sleep(0) - return request.param - - - @pytest.mark.asyncio - async def test_parametrized_loop(fix): - await asyncio.sleep(0) - global TESTS_COUNT - TESTS_COUNT += 1 - """ - ) - ) - result = pytester.runpytest_subprocess("--asyncio-mode=strict") - result.assert_outcomes(passed=4) diff --git a/tests/conftest.py b/tests/conftest.py index 76e2026f..eecab735 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,34 +1,3 @@ from __future__ import annotations -import asyncio - -import pytest - pytest_plugins = "pytester" - - -@pytest.fixture -def dependent_fixture(event_loop): - """A fixture dependent on the event_loop fixture, doing some cleanup.""" - counter = 0 - - async def just_a_sleep(): - """Just sleep a little while.""" - nonlocal event_loop - await asyncio.sleep(0.1) - nonlocal counter - counter += 1 - - event_loop.run_until_complete(just_a_sleep()) - yield - event_loop.run_until_complete(just_a_sleep()) - - assert counter == 2 - - -@pytest.fixture(scope="session", name="factory_involving_factories") -def factory_involving_factories_fixture(unused_tcp_port_factory): - def factory(): - return unused_tcp_port_factory() - - return factory diff --git a/tests/hypothesis/test_base.py b/tests/hypothesis/test_base.py index 4b185f62..487b05fe 100644 --- a/tests/hypothesis/test_base.py +++ b/tests/hypothesis/test_base.py @@ -45,43 +45,6 @@ async def test_mark_and_parametrize(x, y): assert y in (1, 2) -def test_can_use_explicit_event_loop_fixture(pytester: Pytester): - pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = module") - pytester.makepyfile( - dedent( - """\ - import asyncio - import pytest - from hypothesis import given - import hypothesis.strategies as st - - pytest_plugins = 'pytest_asyncio' - - @pytest.fixture(scope="module") - def event_loop(): - loop = asyncio.get_event_loop_policy().new_event_loop() - yield loop - loop.close() - - @given(st.integers()) - @pytest.mark.asyncio - async def test_explicit_fixture_request(event_loop, n): - semaphore = asyncio.Semaphore(value=0) - event_loop.call_soon(semaphore.release) - await semaphore.acquire() - """ - ) - ) - result = pytester.runpytest_subprocess("--asyncio-mode=strict", "-W default") - result.assert_outcomes(passed=1, warnings=2) - result.stdout.fnmatch_lines( - [ - '*is asynchronous and explicitly requests the "event_loop" fixture*', - "*event_loop fixture provided by pytest-asyncio has been redefined*", - ] - ) - - def test_async_auto_marked(pytester: Pytester): pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") pytester.makepyfile( diff --git a/tests/loop_fixture_scope/__init__.py b/tests/loop_fixture_scope/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/tests/loop_fixture_scope/conftest.py b/tests/loop_fixture_scope/conftest.py deleted file mode 100644 index 4e8b06de..00000000 --- a/tests/loop_fixture_scope/conftest.py +++ /dev/null @@ -1,17 +0,0 @@ -from __future__ import annotations - -import asyncio - -import pytest - - -class CustomSelectorLoop(asyncio.SelectorEventLoop): - """A subclass with no overrides, just to test for presence.""" - - -@pytest.fixture(scope="module") -def event_loop(): - """Create an instance of the default event loop for each test case.""" - loop = CustomSelectorLoop() - yield loop - loop.close() diff --git a/tests/loop_fixture_scope/test_loop_fixture_scope.py b/tests/loop_fixture_scope/test_loop_fixture_scope.py deleted file mode 100644 index eb1bae58..00000000 --- a/tests/loop_fixture_scope/test_loop_fixture_scope.py +++ /dev/null @@ -1,19 +0,0 @@ -"""Unit tests for overriding the event loop with a larger scoped one.""" - -from __future__ import annotations - -import asyncio - -import pytest - - -@pytest.mark.asyncio -async def test_for_custom_loop(): - """This test should be executed using the custom loop.""" - await asyncio.sleep(0.01) - assert type(asyncio.get_event_loop()).__name__ == "CustomSelectorLoop" - - -@pytest.mark.asyncio -async def test_dependent_fixture(dependent_fixture): - await asyncio.sleep(0.1) diff --git a/tests/markers/test_function_scope.py b/tests/markers/test_function_scope.py index c17a6225..f750ba58 100644 --- a/tests/markers/test_function_scope.py +++ b/tests/markers/test_function_scope.py @@ -92,29 +92,6 @@ async def test_warns(): result.stdout.fnmatch_lines("*DeprecationWarning*") -def test_function_scope_supports_explicit_event_loop_fixture_request( - pytester: Pytester, -): - pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") - pytester.makepyfile( - dedent( - """\ - import pytest - - pytestmark = pytest.mark.asyncio - - async def test_remember_loop(event_loop): - pass - """ - ) - ) - result = pytester.runpytest_subprocess("--asyncio-mode=strict", "-W default") - result.assert_outcomes(passed=1, warnings=1) - result.stdout.fnmatch_lines( - '*is asynchronous and explicitly requests the "event_loop" fixture*' - ) - - def test_asyncio_mark_respects_the_loop_policy( pytester: Pytester, ): diff --git a/tests/markers/test_module_scope.py b/tests/markers/test_module_scope.py index 7dbdbb7f..fcb8df4c 100644 --- a/tests/markers/test_module_scope.py +++ b/tests/markers/test_module_scope.py @@ -5,59 +5,6 @@ from pytest import Pytester -def test_asyncio_mark_works_on_module_level(pytester: Pytester): - pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") - pytester.makepyfile( - dedent( - """\ - import asyncio - - import pytest - - pytestmark = pytest.mark.asyncio - - - class TestPyTestMark: - async def test_is_asyncio(self, event_loop, sample_fixture): - assert asyncio.get_event_loop() - - counter = 1 - - async def inc(): - nonlocal counter - counter += 1 - await asyncio.sleep(0) - - await asyncio.ensure_future(inc()) - assert counter == 2 - - - async def test_is_asyncio(event_loop, sample_fixture): - assert asyncio.get_event_loop() - counter = 1 - - async def inc(): - nonlocal counter - counter += 1 - await asyncio.sleep(0) - - await asyncio.ensure_future(inc()) - assert counter == 2 - - - @pytest.fixture - def sample_fixture(): - return None - """ - ) - ) - result = pytester.runpytest_subprocess("--asyncio-mode=strict", "-W default") - result.assert_outcomes(passed=2, warnings=2) - result.stdout.fnmatch_lines( - '*is asynchronous and explicitly requests the "event_loop" fixture*' - ) - - def test_asyncio_mark_provides_module_scoped_loop_strict_mode(pytester: Pytester): pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") pytester.makepyfile( diff --git a/tests/test_dependent_fixtures.py b/tests/test_dependent_fixtures.py deleted file mode 100644 index 2e53700a..00000000 --- a/tests/test_dependent_fixtures.py +++ /dev/null @@ -1,16 +0,0 @@ -from __future__ import annotations - -import asyncio - -import pytest - - -@pytest.mark.asyncio -async def test_dependent_fixture(dependent_fixture): - """Test a dependent fixture.""" - await asyncio.sleep(0.1) - - -@pytest.mark.asyncio -async def test_factory_involving_factories(factory_involving_factories): - factory_involving_factories() diff --git a/tests/test_event_loop_fixture_finalizer.py b/tests/test_event_loop_fixture_finalizer.py deleted file mode 100644 index 1e378643..00000000 --- a/tests/test_event_loop_fixture_finalizer.py +++ /dev/null @@ -1,149 +0,0 @@ -from __future__ import annotations - -from textwrap import dedent - -from pytest import Pytester - - -def test_event_loop_fixture_finalizer_returns_fresh_loop_after_test(pytester: Pytester): - pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") - pytester.makepyfile( - dedent( - """\ - import asyncio - - import pytest - - loop = asyncio.new_event_loop() - asyncio.set_event_loop(loop) - - @pytest.mark.asyncio - async def test_1(): - # This async test runs in its own event loop - global loop - running_loop = asyncio.get_event_loop_policy().get_event_loop() - # Make sure this test case received a different loop - assert running_loop is not loop - - def test_2(): - # Code outside of pytest-asyncio should not receive a "used" event loop - current_loop = asyncio.get_event_loop_policy().get_event_loop() - assert not current_loop.is_running() - assert not current_loop.is_closed() - """ - ) - ) - result = pytester.runpytest("--asyncio-mode=strict") - result.assert_outcomes(passed=2) - - -def test_event_loop_fixture_finalizer_handles_loop_set_to_none_sync( - pytester: Pytester, -): - pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") - pytester.makepyfile( - dedent( - """\ - import asyncio - - def test_sync(event_loop): - asyncio.get_event_loop_policy().set_event_loop(None) - """ - ) - ) - result = pytester.runpytest("--asyncio-mode=strict") - result.assert_outcomes(passed=1) - - -def test_event_loop_fixture_finalizer_handles_loop_set_to_none_async_without_fixture( - pytester: Pytester, -): - pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") - pytester.makepyfile( - dedent( - """\ - import asyncio - import pytest - - @pytest.mark.asyncio - async def test_async_without_explicit_fixture_request(): - asyncio.get_event_loop_policy().set_event_loop(None) - """ - ) - ) - result = pytester.runpytest("--asyncio-mode=strict") - result.assert_outcomes(passed=1) - - -def test_event_loop_fixture_finalizer_handles_loop_set_to_none_async_with_fixture( - pytester: Pytester, -): - pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") - pytester.makepyfile( - dedent( - """\ - import asyncio - import pytest - - @pytest.mark.asyncio - async def test_async_with_explicit_fixture_request(event_loop): - asyncio.get_event_loop_policy().set_event_loop(None) - """ - ) - ) - result = pytester.runpytest_subprocess("--asyncio-mode=strict", "-W default") - result.assert_outcomes(passed=1, warnings=1) - result.stdout.fnmatch_lines( - '*is asynchronous and explicitly requests the "event_loop" fixture*' - ) - - -def test_event_loop_fixture_finalizer_raises_warning_when_fixture_leaves_loop_unclosed( - pytester: Pytester, -): - pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") - pytester.makepyfile( - dedent( - """\ - import asyncio - import pytest - - pytest_plugins = 'pytest_asyncio' - - @pytest.fixture - def event_loop(): - loop = asyncio.get_event_loop_policy().new_event_loop() - yield loop - - @pytest.mark.asyncio - async def test_ends_with_unclosed_loop(): - pass - """ - ) - ) - result = pytester.runpytest_subprocess("--asyncio-mode=strict", "-W", "default") - result.assert_outcomes(passed=1, warnings=2) - result.stdout.fnmatch_lines("*unclosed event loop*") - - -def test_event_loop_fixture_finalizer_raises_warning_when_test_leaves_loop_unclosed( - pytester: Pytester, -): - pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") - 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_subprocess("--asyncio-mode=strict", "-W", "default") - result.assert_outcomes(passed=1, warnings=1) - result.stdout.fnmatch_lines("*unclosed event loop*") diff --git a/tests/test_event_loop_fixture_override_deprecation.py b/tests/test_event_loop_fixture_override_deprecation.py deleted file mode 100644 index 04859ef7..00000000 --- a/tests/test_event_loop_fixture_override_deprecation.py +++ /dev/null @@ -1,113 +0,0 @@ -from __future__ import annotations - -from textwrap import dedent - -from pytest import Pytester - - -def test_emit_warning_when_event_loop_fixture_is_redefined(pytester: Pytester): - pytester.makepyfile( - dedent( - """\ - import asyncio - import pytest - - @pytest.fixture - def event_loop(): - loop = asyncio.new_event_loop() - yield loop - loop.close() - - @pytest.mark.asyncio - async def test_emits_warning(): - pass - """ - ) - ) - result = pytester.runpytest_subprocess("--asyncio-mode=strict", "-W default") - result.assert_outcomes(passed=1, warnings=1) - result.stdout.fnmatch_lines( - ["*event_loop fixture provided by pytest-asyncio has been redefined*"] - ) - - -def test_emit_warning_when_event_loop_fixture_is_redefined_explicit_request( - pytester: Pytester, -): - pytester.makepyfile( - dedent( - """\ - import asyncio - import pytest - - @pytest.fixture - def event_loop(): - loop = asyncio.new_event_loop() - yield loop - loop.close() - - @pytest.mark.asyncio - async def test_emits_warning_when_requested_explicitly(event_loop): - pass - """ - ) - ) - result = pytester.runpytest_subprocess("--asyncio-mode=strict", "-W default") - result.assert_outcomes(passed=1, warnings=2) - result.stdout.fnmatch_lines( - ["*event_loop fixture provided by pytest-asyncio has been redefined*"] - ) - result.stdout.fnmatch_lines( - ['*is asynchronous and explicitly requests the "event_loop" fixture*'] - ) - - -def test_does_not_emit_warning_when_no_test_uses_the_event_loop_fixture( - pytester: Pytester, -): - pytester.makepyfile( - dedent( - """\ - import asyncio - import pytest - - @pytest.fixture - def event_loop(): - loop = asyncio.new_event_loop() - yield loop - loop.close() - - def test_emits_no_warning(): - pass - """ - ) - ) - result = pytester.runpytest_subprocess("--asyncio-mode=strict") - result.assert_outcomes(passed=1, warnings=0) - - -def test_emit_warning_when_redefined_event_loop_is_used_by_fixture(pytester: Pytester): - pytester.makepyfile( - dedent( - """\ - import asyncio - import pytest - import pytest_asyncio - - @pytest.fixture - def event_loop(): - loop = asyncio.new_event_loop() - yield loop - loop.close() - - @pytest_asyncio.fixture - async def uses_event_loop(): - pass - - def test_emits_warning(uses_event_loop): - pass - """ - ) - ) - result = pytester.runpytest_subprocess("--asyncio-mode=strict", "-W default") - result.assert_outcomes(passed=1, warnings=1) diff --git a/tests/test_explicit_event_loop_fixture_request.py b/tests/test_explicit_event_loop_fixture_request.py deleted file mode 100644 index c685ad84..00000000 --- a/tests/test_explicit_event_loop_fixture_request.py +++ /dev/null @@ -1,168 +0,0 @@ -from __future__ import annotations - -from textwrap import dedent - -from pytest import Pytester - - -def test_emit_warning_when_event_loop_is_explicitly_requested_in_coroutine( - pytester: Pytester, -): - pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") - pytester.makepyfile( - dedent( - """\ - import pytest - - @pytest.mark.asyncio - async def test_coroutine_emits_warning(event_loop): - pass - """ - ) - ) - result = pytester.runpytest_subprocess("--asyncio-mode=strict", "-W default") - result.assert_outcomes(passed=1, warnings=1) - result.stdout.fnmatch_lines( - ['*is asynchronous and explicitly requests the "event_loop" fixture*'] - ) - - -def test_emit_warning_when_event_loop_is_explicitly_requested_in_coroutine_method( - pytester: Pytester, -): - pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") - pytester.makepyfile( - dedent( - """\ - import pytest - - class TestEmitsWarning: - @pytest.mark.asyncio - async def test_coroutine_emits_warning(self, event_loop): - pass - """ - ) - ) - result = pytester.runpytest_subprocess("--asyncio-mode=strict", "-W default") - result.assert_outcomes(passed=1, warnings=1) - result.stdout.fnmatch_lines( - ['*is asynchronous and explicitly requests the "event_loop" fixture*'] - ) - - -def test_emit_warning_when_event_loop_is_explicitly_requested_in_coroutine_staticmethod( - pytester: Pytester, -): - pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") - pytester.makepyfile( - dedent( - """\ - import pytest - - class TestEmitsWarning: - @staticmethod - @pytest.mark.asyncio - async def test_coroutine_emits_warning(event_loop): - pass - """ - ) - ) - result = pytester.runpytest_subprocess("--asyncio-mode=strict", "-W default") - result.assert_outcomes(passed=1, warnings=1) - result.stdout.fnmatch_lines( - ['*is asynchronous and explicitly requests the "event_loop" fixture*'] - ) - - -def test_emit_warning_when_event_loop_is_explicitly_requested_in_coroutine_fixture( - pytester: Pytester, -): - pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") - pytester.makepyfile( - dedent( - """\ - import pytest - import pytest_asyncio - - @pytest_asyncio.fixture - async def emits_warning(event_loop): - pass - - @pytest.mark.asyncio - async def test_uses_fixture(emits_warning): - pass - """ - ) - ) - result = pytester.runpytest_subprocess("--asyncio-mode=strict", "-W default") - result.assert_outcomes(passed=1, warnings=1) - result.stdout.fnmatch_lines( - ['*is asynchronous and explicitly requests the "event_loop" fixture*'] - ) - - -def test_emit_warning_when_event_loop_is_explicitly_requested_in_async_gen_fixture( - pytester: Pytester, -): - pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") - pytester.makepyfile( - dedent( - """\ - import pytest - import pytest_asyncio - - @pytest_asyncio.fixture - async def emits_warning(event_loop): - yield - - @pytest.mark.asyncio - async def test_uses_fixture(emits_warning): - pass - """ - ) - ) - result = pytester.runpytest_subprocess("--asyncio-mode=strict", "-W default") - result.assert_outcomes(passed=1, warnings=1) - result.stdout.fnmatch_lines( - ['*is asynchronous and explicitly requests the "event_loop" fixture*'] - ) - - -def test_does_not_emit_warning_when_event_loop_is_explicitly_requested_in_sync_function( - pytester: Pytester, -): - pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") - pytester.makepyfile( - dedent( - """\ - import pytest - - def test_uses_fixture(event_loop): - pass - """ - ) - ) - result = pytester.runpytest("--asyncio-mode=strict") - result.assert_outcomes(passed=1) - - -def test_does_not_emit_warning_when_event_loop_is_explicitly_requested_in_sync_fixture( - pytester: Pytester, -): - pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") - pytester.makepyfile( - dedent( - """\ - import pytest - - @pytest.fixture - def any_fixture(event_loop): - pass - - def test_uses_fixture(any_fixture): - pass - """ - ) - ) - result = pytester.runpytest("--asyncio-mode=strict") - result.assert_outcomes(passed=1) diff --git a/tests/test_multiloop.py b/tests/test_multiloop.py deleted file mode 100644 index e6c852b9..00000000 --- a/tests/test_multiloop.py +++ /dev/null @@ -1,72 +0,0 @@ -from __future__ import annotations - -from textwrap import dedent - -from pytest import Pytester - - -def test_event_loop_override(pytester: Pytester): - pytester.makeconftest( - dedent( - '''\ - import asyncio - - import pytest - - - @pytest.fixture - def dependent_fixture(event_loop): - """A fixture dependent on the event_loop fixture, doing some cleanup.""" - counter = 0 - - async def just_a_sleep(): - """Just sleep a little while.""" - nonlocal event_loop - await asyncio.sleep(0.1) - nonlocal counter - counter += 1 - - event_loop.run_until_complete(just_a_sleep()) - yield - event_loop.run_until_complete(just_a_sleep()) - - assert counter == 2 - - - class CustomSelectorLoop(asyncio.SelectorEventLoop): - """A subclass with no overrides, just to test for presence.""" - - - @pytest.fixture - def event_loop(): - """Create an instance of the default event loop for each test case.""" - loop = CustomSelectorLoop() - yield loop - loop.close() - ''' - ) - ) - pytester.makepyfile( - dedent( - '''\ - """Unit tests for overriding the event loop.""" - import asyncio - - import pytest - - - @pytest.mark.asyncio - async def test_for_custom_loop(): - """This test should be executed using the custom loop.""" - await asyncio.sleep(0.01) - assert type(asyncio.get_event_loop()).__name__ == "CustomSelectorLoop" - - - @pytest.mark.asyncio - async def test_dependent_fixture(dependent_fixture): - await asyncio.sleep(0.1) - ''' - ) - ) - result = pytester.runpytest_subprocess("--asyncio-mode=strict") - result.assert_outcomes(passed=2, warnings=2) diff --git a/tests/test_simple.py b/tests/test_simple.py index b8a34fb2..f92ef4e7 100644 --- a/tests/test_simple.py +++ b/tests/test_simple.py @@ -14,13 +14,6 @@ async def async_coro(): return "ok" -def test_event_loop_fixture(event_loop): - """Test the injection of the event_loop fixture.""" - assert event_loop - ret = event_loop.run_until_complete(async_coro()) - assert ret == "ok" - - @pytest.mark.asyncio async def test_asyncio_marker(): """Test the asyncio pytest marker.""" From 094fe052f64ac394e71567e943dbfa0040321c57 Mon Sep 17 00:00:00 2001 From: Michael Seifert Date: Fri, 2 May 2025 12:24:00 +0200 Subject: [PATCH 02/10] refactor: Removed obsolete deprecation warning about *event_loop* being requested in async fixtures. --- pytest_asyncio/plugin.py | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/pytest_asyncio/plugin.py b/pytest_asyncio/plugin.py index f50a7c8b..0945b146 100644 --- a/pytest_asyncio/plugin.py +++ b/pytest_asyncio/plugin.py @@ -270,16 +270,6 @@ def _preprocess_async_fixtures( ): fixturedef.argnames += ("_function_event_loop",) _make_asyncio_fixture_function(func, loop_scope) - function_signature = inspect.signature(func) - if "event_loop" in function_signature.parameters: - warnings.warn( - PytestDeprecationWarning( - f"{func.__name__} is asynchronous and explicitly " - f'requests the "event_loop" fixture. Asynchronous fixtures and ' - f'test functions should use "asyncio.get_running_loop()" ' - f"instead." - ) - ) if "request" not in fixturedef.argnames: fixturedef.argnames += ("request",) _synchronize_async_fixture(fixturedef) From e869ce8a0fe5a79707e909b9525dc463e60d34d5 Mon Sep 17 00:00:00 2001 From: Michael Seifert Date: Fri, 2 May 2025 12:25:29 +0200 Subject: [PATCH 03/10] refactor: Removed obsolete deprecation warning about *event_loop* being requested in async tests. --- pytest_asyncio/plugin.py | 9 --------- 1 file changed, 9 deletions(-) diff --git a/pytest_asyncio/plugin.py b/pytest_asyncio/plugin.py index 0945b146..93f66634 100644 --- a/pytest_asyncio/plugin.py +++ b/pytest_asyncio/plugin.py @@ -509,15 +509,6 @@ def _from_function(cls, function: Function, /) -> Function: ) subclass_instance.own_markers = function.own_markers assert subclass_instance.own_markers == function.own_markers - subclassed_function_signature = inspect.signature(subclass_instance.obj) - if "event_loop" in subclassed_function_signature.parameters: - subclass_instance.warn( - PytestDeprecationWarning( - f"{subclass_instance.name} is asynchronous and explicitly " - f'requests the "event_loop" fixture. Asynchronous fixtures and ' - f'test functions should use "asyncio.get_running_loop()" instead.' - ) - ) return subclass_instance @staticmethod From ef202eb9bbf8f6b3ecaa7e620efea5f2a66afb5e Mon Sep 17 00:00:00 2001 From: Michael Seifert Date: Fri, 2 May 2025 12:28:15 +0200 Subject: [PATCH 04/10] refactor: Removed obsolete fixture handling hook for *event_loop*. --- pytest_asyncio/plugin.py | 120 --------------------------------------- 1 file changed, 120 deletions(-) diff --git a/pytest_asyncio/plugin.py b/pytest_asyncio/plugin.py index 93f66634..8503fc02 100644 --- a/pytest_asyncio/plugin.py +++ b/pytest_asyncio/plugin.py @@ -22,7 +22,6 @@ Mapping, Sequence, ) -from textwrap import dedent from typing import ( Any, Callable, @@ -788,20 +787,6 @@ def _temporary_event_loop_policy(policy: AbstractEventLoopPolicy) -> Iterator[No asyncio.set_event_loop(old_loop) -_REDEFINED_EVENT_LOOP_FIXTURE_WARNING = dedent( - """\ - The event_loop fixture provided by pytest-asyncio has been redefined in - %s:%d - Replacing the event_loop fixture with a custom implementation is deprecated - and will lead to errors in the future. - If you want to request an asyncio event loop with a scope other than function - scope, use the "loop_scope" argument to the asyncio mark when marking the tests. - If you want to return different types of event loops, use the event_loop_policy - fixture. - """ -) - - @pytest.hookimpl(tryfirst=True) def pytest_generate_tests(metafunc: Metafunc) -> None: marker = metafunc.definition.get_closest_marker("asyncio") @@ -841,51 +826,6 @@ def pytest_generate_tests(metafunc: Metafunc) -> None: ) -@pytest.hookimpl(hookwrapper=True) -def pytest_fixture_setup( - fixturedef: FixtureDef, -) -> Generator[None, pluggy.Result, None]: - """Adjust the event loop policy when an event loop is produced.""" - if fixturedef.argname == "event_loop": - # The use of a fixture finalizer is preferred over the - # pytest_fixture_post_finalizer hook. The fixture finalizer is invoked once - # for each fixture, whereas the hook may be invoked multiple times for - # any specific fixture. - # see https://github.com/pytest-dev/pytest/issues/5848 - _add_finalizers( - fixturedef, - _close_event_loop, - _restore_event_loop_policy(asyncio.get_event_loop_policy()), - _provide_clean_event_loop, - ) - outcome = yield - loop: asyncio.AbstractEventLoop = outcome.get_result() - # Weird behavior was observed when checking for an attribute of FixtureDef.func - # Instead, we now check for a special attribute of the returned event loop - fixture_filename = inspect.getsourcefile(fixturedef.func) - if not _is_pytest_asyncio_loop(loop): - _, fixture_line_number = inspect.getsourcelines(fixturedef.func) - warnings.warn( - _REDEFINED_EVENT_LOOP_FIXTURE_WARNING - % (fixture_filename, fixture_line_number), - DeprecationWarning, - ) - policy = asyncio.get_event_loop_policy() - try: - old_loop = _get_event_loop_no_warn(policy) - if old_loop is not loop and not _is_pytest_asyncio_loop(old_loop): - old_loop.close() - except RuntimeError: - # 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 - - yield - - def _make_pytest_asyncio_loop(loop: AbstractEventLoop) -> AbstractEventLoop: loop.__pytest_asyncio = True # type: ignore[attr-defined] return loop @@ -895,19 +835,6 @@ def _is_pytest_asyncio_loop(loop: AbstractEventLoop) -> bool: return getattr(loop, "__pytest_asyncio", False) -def _add_finalizers(fixturedef: FixtureDef, *finalizers: Callable[[], object]) -> None: - """ - Registers the specified fixture finalizers in the fixture. - - Finalizers need to be specified in the exact order in which they should be invoked. - - :param fixturedef: Fixture definition which finalizers should be added to - :param finalizers: Finalizers to be added - """ - for finalizer in reversed(finalizers): - fixturedef.addfinalizer(finalizer) - - _UNCLOSED_EVENT_LOOP_WARNING = dedent( """\ pytest-asyncio detected an unclosed event loop when tearing down the event_loop @@ -922,53 +849,6 @@ def _add_finalizers(fixturedef: FixtureDef, *finalizers: Callable[[], object]) - ) -def _close_event_loop() -> None: - policy = asyncio.get_event_loop_policy() - try: - loop = policy.get_event_loop() - except RuntimeError: - loop = None - if loop is not None and not _is_pytest_asyncio_loop(loop): - if not loop.is_closed(): - warnings.warn( - _UNCLOSED_EVENT_LOOP_WARNING % loop, - DeprecationWarning, - ) - loop.close() - - -def _restore_event_loop_policy(previous_policy) -> Callable[[], None]: - def _restore_policy(): - # Close any event loop associated with the old loop policy - # to avoid ResourceWarnings in the _provide_clean_event_loop finalizer - try: - loop = _get_event_loop_no_warn(previous_policy) - except RuntimeError: - loop = None - if loop and not _is_pytest_asyncio_loop(loop): - loop.close() - asyncio.set_event_loop_policy(previous_policy) - - return _restore_policy - - -def _provide_clean_event_loop() -> None: - # At this point, the event loop for the current thread is closed. - # When a user calls asyncio.get_event_loop(), they will get a closed loop. - # In order to avoid this side effect from pytest-asyncio, we need to replace - # the current loop with a fresh one. - # Note that we cannot set the loop to None, because get_event_loop only creates - # a new loop, when set_event_loop has not been called. - policy = asyncio.get_event_loop_policy() - try: - old_loop = _get_event_loop_no_warn(policy) - except RuntimeError: - old_loop = None - if old_loop is not None and not _is_pytest_asyncio_loop(old_loop): - new_loop = policy.new_event_loop() - policy.set_event_loop(new_loop) - - def _get_event_loop_no_warn( policy: AbstractEventLoopPolicy | None = None, ) -> asyncio.AbstractEventLoop: From 2dd829fd1ca9734ed5b5c57c89e83c317fb0a9c4 Mon Sep 17 00:00:00 2001 From: Michael Seifert Date: Fri, 2 May 2025 12:34:48 +0200 Subject: [PATCH 05/10] refactor: Removed warning when requesting a fixture named *event_loop*. --- pytest_asyncio/plugin.py | 24 ------------------------ tests/markers/test_module_scope.py | 22 ---------------------- tests/markers/test_package_scope.py | 22 ---------------------- tests/markers/test_session_scope.py | 22 ---------------------- 4 files changed, 90 deletions(-) diff --git a/pytest_asyncio/plugin.py b/pytest_asyncio/plugin.py index 8503fc02..03d8c0b1 100644 --- a/pytest_asyncio/plugin.py +++ b/pytest_asyncio/plugin.py @@ -70,10 +70,6 @@ class PytestAsyncioError(Exception): """Base class for exceptions raised by pytest-asyncio""" -class MultipleEventLoopsRequestedError(PytestAsyncioError): - """Raised when a test requests multiple asyncio event loops.""" - - class Mode(str, enum.Enum): AUTO = "auto" STRICT = "strict" @@ -808,14 +804,6 @@ def pytest_generate_tests(metafunc: Metafunc) -> None: return fixturemanager = metafunc.config.pluginmanager.get_plugin("funcmanage") assert fixturemanager is not None - if "event_loop" in metafunc.fixturenames: - raise MultipleEventLoopsRequestedError( - _MULTIPLE_LOOPS_REQUESTED_ERROR.format( - test_name=metafunc.definition.nodeid, - scope=scope, - scoped_loop_node=event_loop_node.nodeid, - ), - ) # Add the scoped event loop fixture to Metafunc's list of fixture names and # fixturedefs and leave the actual parametrization to pytest # The fixture needs to be appended to avoid messing up the fixture evaluation @@ -943,18 +931,6 @@ def inner(*args, **kwargs): return inner -_MULTIPLE_LOOPS_REQUESTED_ERROR = dedent( - """\ - Multiple asyncio event loops with different scopes have been requested - by {test_name}. The test explicitly requests the event_loop fixture, while - another event loop with {scope} scope is provided by {scoped_loop_node}. - Remove "event_loop" from the requested fixture in your test to run the test - in a {scope}-scoped event loop or remove the scope argument from the "asyncio" - mark to run the test in a function-scoped event loop. - """ -) - - def pytest_runtest_setup(item: pytest.Item) -> None: marker = item.get_closest_marker("asyncio") if marker is None: diff --git a/tests/markers/test_module_scope.py b/tests/markers/test_module_scope.py index fcb8df4c..a050f503 100644 --- a/tests/markers/test_module_scope.py +++ b/tests/markers/test_module_scope.py @@ -36,28 +36,6 @@ async def test_this_runs_in_same_loop(self): result.assert_outcomes(passed=3) -def test_raise_when_event_loop_fixture_is_requested_in_addition_to_scoped_loop( - pytester: Pytester, -): - pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") - pytester.makepyfile( - dedent( - """\ - import asyncio - import pytest - - pytestmark = pytest.mark.asyncio(loop_scope="module") - - async def test_remember_loop(event_loop): - pass - """ - ) - ) - result = pytester.runpytest("--asyncio-mode=strict") - result.assert_outcomes(errors=1) - result.stdout.fnmatch_lines("*MultipleEventLoopsRequestedError: *") - - def test_asyncio_mark_respects_the_loop_policy( pytester: Pytester, ): diff --git a/tests/markers/test_package_scope.py b/tests/markers/test_package_scope.py index 204238a4..94adba22 100644 --- a/tests/markers/test_package_scope.py +++ b/tests/markers/test_package_scope.py @@ -69,28 +69,6 @@ async def test_subpackage_runs_in_different_loop(): result.assert_outcomes(passed=4) -def test_raise_when_event_loop_fixture_is_requested_in_addition_to_scoped_loop( - pytester: Pytester, -): - pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") - pytester.makepyfile( - __init__="", - test_raises=dedent( - """\ - import asyncio - import pytest - - @pytest.mark.asyncio(loop_scope="package") - async def test_remember_loop(event_loop): - pass - """ - ), - ) - result = pytester.runpytest("--asyncio-mode=strict") - result.assert_outcomes(errors=1) - result.stdout.fnmatch_lines("*MultipleEventLoopsRequestedError: *") - - def test_asyncio_mark_respects_the_loop_policy( pytester: Pytester, ): diff --git a/tests/markers/test_session_scope.py b/tests/markers/test_session_scope.py index 70e191b2..2d3a4993 100644 --- a/tests/markers/test_session_scope.py +++ b/tests/markers/test_session_scope.py @@ -70,28 +70,6 @@ async def test_subpackage_runs_in_same_loop(): result.assert_outcomes(passed=4) -def test_raise_when_event_loop_fixture_is_requested_in_addition_to_scoped_loop( - pytester: Pytester, -): - pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") - pytester.makepyfile( - __init__="", - test_raises=dedent( - """\ - import asyncio - import pytest - - @pytest.mark.asyncio(loop_scope="session") - async def test_remember_loop(event_loop): - pass - """ - ), - ) - result = pytester.runpytest("--asyncio-mode=strict") - result.assert_outcomes(errors=1) - result.stdout.fnmatch_lines("*MultipleEventLoopsRequestedError: *") - - def test_asyncio_mark_respects_the_loop_policy( pytester: Pytester, ): From fd9a885a70b170d23144001810d98e1a6dc9e132 Mon Sep 17 00:00:00 2001 From: Michael Seifert Date: Fri, 2 May 2025 12:41:54 +0200 Subject: [PATCH 06/10] refactor: Stop injecting "event_loop" into fixture arguments. --- pytest_asyncio/plugin.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/pytest_asyncio/plugin.py b/pytest_asyncio/plugin.py index 03d8c0b1..e1b87b5c 100644 --- a/pytest_asyncio/plugin.py +++ b/pytest_asyncio/plugin.py @@ -283,15 +283,12 @@ def _synchronize_async_fixture(fixturedef: FixtureDef) -> None: def _add_kwargs( func: Callable[..., Any], kwargs: dict[str, Any], - event_loop: asyncio.AbstractEventLoop, request: FixtureRequest, ) -> dict[str, Any]: sig = inspect.signature(func) ret = kwargs.copy() if "request" in sig.parameters: ret["request"] = request - if "event_loop" in sig.parameters: - ret["event_loop"] = event_loop return ret @@ -322,7 +319,7 @@ def _asyncgen_fixture_wrapper(request: FixtureRequest, **kwargs: Any): ) event_loop = request.getfixturevalue(event_loop_fixture_id) kwargs.pop(event_loop_fixture_id, None) - gen_obj = func(**_add_kwargs(func, kwargs, event_loop, request)) + gen_obj = func(**_add_kwargs(func, kwargs, request)) async def setup(): res = await gen_obj.__anext__() # type: ignore[union-attr] @@ -371,7 +368,7 @@ def _async_fixture_wrapper(request: FixtureRequest, **kwargs: Any): kwargs.pop(event_loop_fixture_id, None) async def setup(): - res = await func(**_add_kwargs(func, kwargs, event_loop, request)) + res = await func(**_add_kwargs(func, kwargs, request)) return res context = contextvars.copy_context() From b684127254ecf535b4b5ae571f9fc9e5738d7497 Mon Sep 17 00:00:00 2001 From: Michael Seifert Date: Fri, 2 May 2025 12:47:12 +0200 Subject: [PATCH 07/10] refactor: Stop warning about unclosed loops when changing the loop policy. The warning was intended for user created loops and there shouldn't be anymore of them. --- pytest_asyncio/plugin.py | 27 --------------------------- 1 file changed, 27 deletions(-) diff --git a/pytest_asyncio/plugin.py b/pytest_asyncio/plugin.py index e1b87b5c..d5b7e10d 100644 --- a/pytest_asyncio/plugin.py +++ b/pytest_asyncio/plugin.py @@ -754,19 +754,6 @@ def _temporary_event_loop_policy(policy: AbstractEventLoopPolicy) -> Iterator[No try: yield finally: - # Try detecting user-created event loops that were left unclosed - # at the end of a test. - try: - current_loop: AbstractEventLoop | None = _get_event_loop_no_warn() - except RuntimeError: - current_loop = None - if current_loop is not None and not current_loop.is_closed(): - warnings.warn( - _UNCLOSED_EVENT_LOOP_WARNING % current_loop, - DeprecationWarning, - ) - current_loop.close() - asyncio.set_event_loop_policy(old_loop_policy) # When a test uses both a scoped event loop and the event_loop fixture, # the "_provide_clean_event_loop" finalizer of the event_loop fixture @@ -820,20 +807,6 @@ def _is_pytest_asyncio_loop(loop: AbstractEventLoop) -> bool: return getattr(loop, "__pytest_asyncio", False) -_UNCLOSED_EVENT_LOOP_WARNING = dedent( - """\ - 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. The scopes of your custom "event_loop" fixtures do not overlap - 3. Your code does not modify the event loop in async fixtures or tests - """ -) - - def _get_event_loop_no_warn( policy: AbstractEventLoopPolicy | None = None, ) -> asyncio.AbstractEventLoop: From 64c9ccbe9f4fc76c942ce53bb1d7839516e2ed8e Mon Sep 17 00:00:00 2001 From: Michael Seifert Date: Fri, 2 May 2025 12:48:58 +0200 Subject: [PATCH 08/10] refactor: Removed unused function _is_pytest_asyncio_loop. --- pytest_asyncio/plugin.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/pytest_asyncio/plugin.py b/pytest_asyncio/plugin.py index d5b7e10d..02e2bc60 100644 --- a/pytest_asyncio/plugin.py +++ b/pytest_asyncio/plugin.py @@ -803,10 +803,6 @@ def _make_pytest_asyncio_loop(loop: AbstractEventLoop) -> AbstractEventLoop: return loop -def _is_pytest_asyncio_loop(loop: AbstractEventLoop) -> bool: - return getattr(loop, "__pytest_asyncio", False) - - def _get_event_loop_no_warn( policy: AbstractEventLoopPolicy | None = None, ) -> asyncio.AbstractEventLoop: From f0613c1106b490e67b463e3b9195df073240af8c Mon Sep 17 00:00:00 2001 From: Michael Seifert Date: Fri, 2 May 2025 12:53:16 +0200 Subject: [PATCH 09/10] refactor: Stop marking event loops with a custom attribute. There's no longer a need to identify event loops provided by pytest-asyncio. --- pytest_asyncio/plugin.py | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/pytest_asyncio/plugin.py b/pytest_asyncio/plugin.py index 02e2bc60..a3354f43 100644 --- a/pytest_asyncio/plugin.py +++ b/pytest_asyncio/plugin.py @@ -11,7 +11,7 @@ import socket import sys import warnings -from asyncio import AbstractEventLoop, AbstractEventLoopPolicy +from asyncio import AbstractEventLoopPolicy from collections.abc import ( AsyncIterator, Awaitable, @@ -798,11 +798,6 @@ def pytest_generate_tests(metafunc: Metafunc) -> None: ) -def _make_pytest_asyncio_loop(loop: AbstractEventLoop) -> AbstractEventLoop: - loop.__pytest_asyncio = True # type: ignore[attr-defined] - return loop - - def _get_event_loop_no_warn( policy: AbstractEventLoopPolicy | None = None, ) -> asyncio.AbstractEventLoop: @@ -996,12 +991,6 @@ def _function_event_loop( @contextlib.contextmanager def _provide_event_loop() -> Iterator[asyncio.AbstractEventLoop]: loop = asyncio.get_event_loop_policy().new_event_loop() - # Add a magic value to the event loop, so pytest-asyncio can determine if the - # event_loop fixture was overridden. Other implementations of event_loop don't - # set this value. - # The magic value must be set as part of the function definition, because pytest - # seems to have multiple instances of the same FixtureDef or fixture function - loop = _make_pytest_asyncio_loop(loop) try: yield loop finally: From 44162c44b5d890fc21ccec0b4ecbbd50c538bd40 Mon Sep 17 00:00:00 2001 From: Michael Seifert Date: Fri, 2 May 2025 13:30:01 +0200 Subject: [PATCH 10/10] docs: Added changelog entry. --- changelog.d/1106.removed.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/1106.removed.rst diff --git a/changelog.d/1106.removed.rst b/changelog.d/1106.removed.rst new file mode 100644 index 00000000..75cec456 --- /dev/null +++ b/changelog.d/1106.removed.rst @@ -0,0 +1 @@ +The deprecated *event_loop* fixture.