diff --git a/changelog.d/924.changed.rst b/changelog.d/924.changed.rst new file mode 100644 index 00000000..1f3b5d1e --- /dev/null +++ b/changelog.d/924.changed.rst @@ -0,0 +1 @@ +The default value of ``asyncio_default_fixture_loop_scope`` is now ``function``. The deprecated unset behavior and warning have been removed. diff --git a/pytest_asyncio/plugin.py b/pytest_asyncio/plugin.py index 2fe8db12..afd1d2ed 100644 --- a/pytest_asyncio/plugin.py +++ b/pytest_asyncio/plugin.py @@ -137,7 +137,7 @@ def pytest_addoption(parser: Parser, pluginmanager: PytestPluginManager) -> None "asyncio_default_fixture_loop_scope", type="string", help="default scope of the asyncio event loop used to execute async fixtures", - default=None, + default="function", ) parser.addini( "asyncio_default_test_loop_scope", @@ -271,16 +271,6 @@ def _collect_hook_loop_factories( return factories -_DEFAULT_FIXTURE_LOOP_SCOPE_UNSET = """\ -The configuration option "asyncio_default_fixture_loop_scope" is unset. -The event loop scope for asynchronous fixtures will default to the "fixture" caching \ -scope. Future versions of pytest-asyncio will default the loop scope for asynchronous \ -fixtures to "function" scope. Set the default fixture loop scope explicitly in order \ -to avoid unexpected behavior in the future. Valid fixture loop scopes are: \ -"function", "class", "module", "package", "session" -""" - - def _validate_scope(scope: str | None, option_name: str) -> None: if scope is None: return @@ -295,8 +285,6 @@ def _validate_scope(scope: str | None, option_name: str) -> None: def pytest_configure(config: Config) -> None: default_fixture_loop_scope = config.getini("asyncio_default_fixture_loop_scope") _validate_scope(default_fixture_loop_scope, "asyncio_default_fixture_loop_scope") - if not default_fixture_loop_scope: - warnings.warn(PytestDeprecationWarning(_DEFAULT_FIXTURE_LOOP_SCOPE_UNSET)) default_test_loop_scope = config.getini("asyncio_default_test_loop_scope") _validate_scope(default_test_loop_scope, "asyncio_default_test_loop_scope") @@ -925,11 +913,7 @@ def pytest_fixture_setup(fixturedef: FixtureDef, request) -> object | None: if not _is_coroutine_or_asyncgen(fixturedef.func): return (yield) default_loop_scope = request.config.getini("asyncio_default_fixture_loop_scope") - loop_scope = ( - getattr(fixturedef.func, "_loop_scope", None) - or default_loop_scope - or fixturedef.scope - ) + loop_scope = getattr(fixturedef.func, "_loop_scope", None) or default_loop_scope runner_fixture_id = f"_{loop_scope}_scoped_runner" runner = request.getfixturevalue(runner_fixture_id) # Prevent the runner closing before the fixture's async teardown. diff --git a/tests/test_fixture_loop_scopes.py b/tests/test_fixture_loop_scopes.py index a25e56a0..b4b612e3 100644 --- a/tests/test_fixture_loop_scopes.py +++ b/tests/test_fixture_loop_scopes.py @@ -35,6 +35,39 @@ async def test_runs_in_same_loop_as_fixture(fixture): result.assert_outcomes(passed=1) +def test_default_fixture_loop_scope_is_function_when_unset(pytester: Pytester): + pytester.makepyfile(dedent(""" + import asyncio + import pytest + import pytest_asyncio + + fixture_loops = [] + + @pytest_asyncio.fixture + async def fixture_loop(): + loop = asyncio.get_running_loop() + fixture_loops.append(loop) + return loop + + @pytest.mark.asyncio + async def test_fixture_uses_function_loop_scope(fixture_loop): + assert asyncio.get_running_loop() is fixture_loop + + @pytest.mark.asyncio + async def test_fixture_uses_new_function_loop(fixture_loop): + assert asyncio.get_running_loop() is fixture_loop + assert fixture_loops[0] is not fixture_loops[1] + """)) + + result = pytester.runpytest_subprocess("--asyncio-mode=strict", "-W", "error") + result.assert_outcomes(passed=2) + result.stdout.fnmatch_lines( + [ + "*asyncio_default_fixture_loop_scope=function*", + ] + ) + + @pytest.mark.parametrize("default_loop_scope", ("function", "module", "session")) def test_default_loop_scope_config_option_changes_fixture_loop_scope( pytester: Pytester,