Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/924.changed.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
The default value of ``asyncio_default_fixture_loop_scope`` is now ``function``. The deprecated unset behavior and warning have been removed.
20 changes: 2 additions & 18 deletions pytest_asyncio/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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
Expand All @@ -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")
Expand Down Expand Up @@ -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.
Expand Down
26 changes: 26 additions & 0 deletions tests/test_fixture_loop_scopes.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,32 @@ 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):
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How does this test assert that the fixture is function-scoped? In other words, how would the test fail if the fixture was not function-scoped?

pytester.makepyfile(dedent("""
import asyncio
import pytest
import pytest_asyncio

@pytest_asyncio.fixture(scope="module")
async def fixture_loop():
return asyncio.get_running_loop()

@pytest.mark.asyncio(loop_scope="module")
async def test_fixture_uses_default_function_loop_scope(fixture_loop):
assert asyncio.get_running_loop() is fixture_loop
"""))

result = pytester.runpytest_subprocess("--asyncio-mode=strict", "-W", "error")
result.assert_outcomes(errors=1)
result.stdout.fnmatch_lines(
[
"*asyncio_default_fixture_loop_scope=function*",
"*ScopeMismatch*function scoped fixture _function_scoped_runner*"
"module scoped request object*",
]
)


@pytest.mark.parametrize("default_loop_scope", ("function", "module", "session"))
def test_default_loop_scope_config_option_changes_fixture_loop_scope(
pytester: Pytester,
Expand Down
Loading