From efd7a31b75326630c6f86c7e71c1ce03a9bb9b84 Mon Sep 17 00:00:00 2001 From: Tom Kuson Date: Sat, 18 Apr 2026 21:39:40 +0100 Subject: [PATCH 1/2] Delete unused code --- ...oop_custom_policies_strict_mode_example.py | 23 ------------------- 1 file changed, 23 deletions(-) delete mode 100644 docs/reference/markers/class_scoped_loop_custom_policies_strict_mode_example.py diff --git a/docs/reference/markers/class_scoped_loop_custom_policies_strict_mode_example.py b/docs/reference/markers/class_scoped_loop_custom_policies_strict_mode_example.py deleted file mode 100644 index 5bb26247..00000000 --- a/docs/reference/markers/class_scoped_loop_custom_policies_strict_mode_example.py +++ /dev/null @@ -1,23 +0,0 @@ -import warnings - -with warnings.catch_warnings(): - warnings.simplefilter("ignore", DeprecationWarning) - from asyncio import DefaultEventLoopPolicy - -import pytest - - -@pytest.fixture( - params=[ - DefaultEventLoopPolicy(), - DefaultEventLoopPolicy(), - ] -) -def event_loop_policy(request): - return request.param - - -class TestWithDifferentLoopPolicies: - @pytest.mark.asyncio - async def test_parametrized_loop(self): - pass From 651f65c0025f6e2ca38150b4a94947c7ed633fbc Mon Sep 17 00:00:00 2001 From: Tom Kuson Date: Sat, 18 Apr 2026 21:40:27 +0100 Subject: [PATCH 2/2] Deprecate event_loop_policy hook --- changelog.d/1419.deprecated.rst | 1 + docs/how-to-guides/multiple_loops.rst | 4 +++ docs/how-to-guides/uvloop.rst | 2 +- docs/reference/fixtures/index.rst | 5 +++ pytest_asyncio/plugin.py | 13 +++++++ tests/markers/test_class_scope.py | 4 +-- tests/markers/test_function_scope.py | 49 +++++++++++++++++++++++++-- tests/markers/test_module_scope.py | 4 +-- tests/markers/test_package_scope.py | 4 +-- tests/markers/test_session_scope.py | 4 +-- 10 files changed, 79 insertions(+), 11 deletions(-) create mode 100644 changelog.d/1419.deprecated.rst diff --git a/changelog.d/1419.deprecated.rst b/changelog.d/1419.deprecated.rst new file mode 100644 index 00000000..2e4f39e2 --- /dev/null +++ b/changelog.d/1419.deprecated.rst @@ -0,0 +1 @@ +Overriding the *event_loop_policy* fixture is deprecated. Use the ``pytest_asyncio_loop_factories`` hook instead. diff --git a/docs/how-to-guides/multiple_loops.rst b/docs/how-to-guides/multiple_loops.rst index 3453c49f..7ebf0616 100644 --- a/docs/how-to-guides/multiple_loops.rst +++ b/docs/how-to-guides/multiple_loops.rst @@ -2,6 +2,10 @@ How to test with different event loops ====================================== +.. warning:: + + Overriding the *event_loop_policy* fixture is deprecated and will be removed in a future version of pytest-asyncio. Use the ``pytest_asyncio_loop_factories`` hook instead. See :doc:`custom_loop_factory` for details. + Parametrizing the *event_loop_policy* fixture parametrizes all async tests. The following example causes all async tests to run multiple times, once for each event loop in the fixture parameters: .. include:: multiple_loops_example.py diff --git a/docs/how-to-guides/uvloop.rst b/docs/how-to-guides/uvloop.rst index 99afd238..03143be5 100644 --- a/docs/how-to-guides/uvloop.rst +++ b/docs/how-to-guides/uvloop.rst @@ -24,7 +24,7 @@ Using the event_loop_policy fixture .. note:: - ``asyncio.AbstractEventLoopPolicy`` is deprecated as of Python 3.14 (removal planned for 3.16), and ``uvloop.EventLoopPolicy`` will be removed alongside it. Prefer the hook approach above. + ``asyncio.AbstractEventLoopPolicy`` is deprecated as of Python 3.14 (removal planned for 3.16), and ``uvloop.EventLoopPolicy`` will be removed alongside it. Overriding the *event_loop_policy* fixture is also deprecated in pytest-asyncio. Prefer the hook approach above. For older versions of Python and uvloop, you can override the *event_loop_policy* fixture in your *conftest.py:* diff --git a/docs/reference/fixtures/index.rst b/docs/reference/fixtures/index.rst index 3d151dcb..2791a484 100644 --- a/docs/reference/fixtures/index.rst +++ b/docs/reference/fixtures/index.rst @@ -4,6 +4,11 @@ Fixtures event_loop_policy ================= + +.. warning:: + + Overriding the *event_loop_policy* fixture is deprecated and will be removed in a future version of pytest-asyncio. Use the ``pytest_asyncio_loop_factories`` hook instead. See :doc:`../hooks` for details. + Returns the event loop policy used to create asyncio event loops. The default return value is *asyncio.get_event_loop_policy().* diff --git a/pytest_asyncio/plugin.py b/pytest_asyncio/plugin.py index 18d84181..3810f775 100644 --- a/pytest_asyncio/plugin.py +++ b/pytest_asyncio/plugin.py @@ -919,6 +919,13 @@ def inner(*args, **kwargs): @pytest.hookimpl(wrapper=True) def pytest_fixture_setup(fixturedef: FixtureDef, request) -> object | None: + if ( + fixturedef.argname == "event_loop_policy" + and fixturedef.func.__module__ != __name__ + ): + warnings.warn( + PytestDeprecationWarning(_EVENT_LOOP_POLICY_FIXTURE_DEPRECATION_WARNING), + ) asyncio_mode = _get_asyncio_mode(request.config) if not _is_asyncio_fixture_function(fixturedef.func): if asyncio_mode == Mode.STRICT: @@ -962,6 +969,12 @@ def pytest_fixture_setup(fixturedef: FixtureDef, request) -> object | None: mark.asyncio 'loop_factories' must be a non-empty sequence of strings. """ +_EVENT_LOOP_POLICY_FIXTURE_DEPRECATION_WARNING = """\ +Overriding the "event_loop_policy" fixture is deprecated \ +and will be removed in a future version of pytest-asyncio. \ +Use the "pytest_asyncio_loop_factories" hook to customize event loop creation.\ +""" + def _parse_asyncio_marker( asyncio_marker: Mark, diff --git a/tests/markers/test_class_scope.py b/tests/markers/test_class_scope.py index 5394c2e9..28f7187c 100644 --- a/tests/markers/test_class_scope.py +++ b/tests/markers/test_class_scope.py @@ -133,7 +133,7 @@ async def test_does_not_use_custom_event_loop_policy(): pytest_args.extend(["-W", "default"]) result = pytester.runpytest(*pytest_args) if sys.version_info >= (3, 14): - result.assert_outcomes(passed=2, warnings=3) + result.assert_outcomes(passed=2, warnings=4) result.stdout.fnmatch_lines("*DefaultEventLoopPolicy*") else: result.assert_outcomes(passed=2) @@ -168,7 +168,7 @@ async def test_parametrized_loop(self, request): pytest_args.extend(["-W", "default"]) result = pytester.runpytest(*pytest_args) if sys.version_info >= (3, 14): - result.assert_outcomes(passed=2, warnings=2) + result.assert_outcomes(passed=2, warnings=4) result.stdout.fnmatch_lines("*DefaultEventLoopPolicy*") else: result.assert_outcomes(passed=2) diff --git a/tests/markers/test_function_scope.py b/tests/markers/test_function_scope.py index 5005a69a..180d5c71 100644 --- a/tests/markers/test_function_scope.py +++ b/tests/markers/test_function_scope.py @@ -107,7 +107,7 @@ async def test_uses_custom_event_loop_policy(): pytest_args.extend(["-W", "default"]) result = pytester.runpytest(*pytest_args) if sys.version_info >= (3, 14): - result.assert_outcomes(passed=1, warnings=2) + result.assert_outcomes(passed=1, warnings=3) result.stdout.fnmatch_lines("*DefaultEventLoopPolicy*") else: result.assert_outcomes(passed=1) @@ -148,12 +148,57 @@ async def test_parametrized_loop(): pytest_args.extend(["-W", "default"]) result = pytester.runpytest(*pytest_args) if sys.version_info >= (3, 14): - result.assert_outcomes(passed=2, warnings=3) + result.assert_outcomes(passed=2, warnings=5) result.stdout.fnmatch_lines("*DefaultEventLoopPolicy*") else: result.assert_outcomes(passed=2) +def test_event_loop_policy_fixture_override_emits_deprecation_warning( + pytester: Pytester, +): + pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") + pytester.makepyfile( + dedent("""\ + import asyncio + import pytest + + pytestmark = pytest.mark.asyncio + + @pytest.fixture + def event_loop_policy(): + return asyncio.DefaultEventLoopPolicy() + + async def test_anything(): + pass + """), + ) + result = pytester.runpytest("--asyncio-mode=strict", "-W", "default") + result.assert_outcomes(passed=1) + result.stdout.fnmatch_lines( + "*PytestDeprecationWarning*event_loop_policy*deprecated*" + ) + + +def test_default_event_loop_policy_fixture_does_not_warn( + pytester: Pytester, +): + pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") + pytester.makepyfile( + dedent("""\ + import pytest + + pytestmark = pytest.mark.asyncio + + async def test_anything(): + pass + """), + ) + result = pytester.runpytest("--asyncio-mode=strict", "-W", "default") + result.assert_outcomes(passed=1) + result.stdout.no_fnmatch_line("*PytestDeprecationWarning*event_loop_policy*") + + def test_asyncio_mark_provides_function_scoped_loop_to_fixtures( pytester: Pytester, ): diff --git a/tests/markers/test_module_scope.py b/tests/markers/test_module_scope.py index 3f7ee3e9..a2662812 100644 --- a/tests/markers/test_module_scope.py +++ b/tests/markers/test_module_scope.py @@ -83,7 +83,7 @@ async def test_does_not_use_custom_event_loop_policy(): pytest_args.extend(["-W", "default"]) result = pytester.runpytest(*pytest_args) if sys.version_info >= (3, 14): - result.assert_outcomes(passed=2, warnings=3) + result.assert_outcomes(passed=2, warnings=4) result.stdout.fnmatch_lines("*DefaultEventLoopPolicy*") else: result.assert_outcomes(passed=2) @@ -118,7 +118,7 @@ async def test_parametrized_loop(): pytest_args.extend(["-W", "default"]) result = pytester.runpytest(*pytest_args) if sys.version_info >= (3, 14): - result.assert_outcomes(passed=2, warnings=2) + result.assert_outcomes(passed=2, warnings=4) result.stdout.fnmatch_lines("*DefaultEventLoopPolicy*") else: result.assert_outcomes(passed=2) diff --git a/tests/markers/test_package_scope.py b/tests/markers/test_package_scope.py index 0783eb9c..ff6aba4a 100644 --- a/tests/markers/test_package_scope.py +++ b/tests/markers/test_package_scope.py @@ -115,7 +115,7 @@ async def test_also_uses_custom_event_loop_policy(): pytest_args.extend(["-W", "default"]) result = pytester.runpytest(*pytest_args) if sys.version_info >= (3, 14): - result.assert_outcomes(passed=2, warnings=3) + result.assert_outcomes(passed=2, warnings=4) result.stdout.fnmatch_lines("*DefaultEventLoopPolicy*") else: result.assert_outcomes(passed=2) @@ -153,7 +153,7 @@ async def test_parametrized_loop(): pytest_args.extend(["-W", "default"]) result = pytester.runpytest(*pytest_args) if sys.version_info >= (3, 14): - result.assert_outcomes(passed=2, warnings=2) + result.assert_outcomes(passed=2, warnings=4) result.stdout.fnmatch_lines("*DefaultEventLoopPolicy*") else: result.assert_outcomes(passed=2) diff --git a/tests/markers/test_session_scope.py b/tests/markers/test_session_scope.py index 66ea5ed8..24566c5e 100644 --- a/tests/markers/test_session_scope.py +++ b/tests/markers/test_session_scope.py @@ -116,7 +116,7 @@ async def test_also_uses_custom_event_loop_policy(): pytest_args.extend(["-W", "default"]) result = pytester.runpytest(*pytest_args) if sys.version_info >= (3, 14): - result.assert_outcomes(passed=2, warnings=3) + result.assert_outcomes(passed=2, warnings=4) result.stdout.fnmatch_lines("*DefaultEventLoopPolicy*") else: result.assert_outcomes(passed=2) @@ -154,7 +154,7 @@ async def test_parametrized_loop(): pytest_args.extend(["-W", "default"]) result = pytester.runpytest(*pytest_args) if sys.version_info >= (3, 14): - result.assert_outcomes(passed=2, warnings=2) + result.assert_outcomes(passed=2, warnings=4) result.stdout.fnmatch_lines("*DefaultEventLoopPolicy*") else: result.assert_outcomes(passed=2)