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
2 changes: 1 addition & 1 deletion changelog.d/1298.changed.rst
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.

The news fragment describes a change of a warning message. Your change is unrelated to this news fragment and will be included in a different release. Please remove any modifications to this file.

Original file line number Diff line number Diff line change
@@ -1 +1 @@
Improved the readability of the warning message that is displayed when ``asyncio_default_fixture_loop_scope`` is unset
The default value of ``asyncio_default_fixture_loop_scope`` is now ``function``. The deprecated unset behavior and warning have been removed.
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.
2 changes: 1 addition & 1 deletion docs/how-to-guides/migrate_from_0_21.rst
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.

This is the migration guide for users looking to upgrade from an older version of pytest-asyncio. This patch doesn't change anything in the upgrade path. Please remove any modifications to this file.

Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,4 @@ Go through all re-implemented *event_loop* fixtures in your test suite one by on
1. For all tests and fixtures affected by the re-implemented *event_loop* fixture, configure the *loop_scope* for async tests and fixtures to match the *event_loop* fixture scope. This can be done for each test and fixture individually using either the ``pytest.mark.asyncio(loop_scope="…")`` marker for async tests or ``@pytest_asyncio.fixture(loop_scope="…")`` for async fixtures. Alternatively, you can set the default loop scope for fixtures using the :ref:`asyncio_default_fixture_loop_scope <configuration/asyncio_default_fixture_loop_scope>` configuration option. Snippets to mark all tests with the same *asyncio* marker, thus sharing the same loop scope, are present in the how-to section of the documentation. Depending on the homogeneity of your test suite, you may want a mixture of explicit decorators and default settings.
2. Remove the re-implemented *event_loop* fixture.

If you haven't set the *asyncio_default_fixture_loop_scope* configuration option, yet, set it to *function* to silence the deprecation warning.
The *asyncio_default_fixture_loop_scope* configuration option now defaults to *function* when unset.
2 changes: 1 addition & 1 deletion docs/how-to-guides/migrate_from_0_23.rst
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.

This is the migration guide for users looking to upgrade from an older version of pytest-asyncio. This patch doesn't change anything in the upgrade path. Please remove any modifications to this file.

Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@ How to migrate from pytest-asyncio v0.23
The following steps assume that your test suite has no re-implementations of the *event_loop* fixture, nor explicit fixtures requests for it. If this isn't the case, please follow the :ref:`migration guide for pytest-asyncio v0.21. <how_to_guides/migrate_from_0_21>`

1. Explicitly set the *loop_scope* of async fixtures by replacing occurrences of ``@pytest.fixture(scope="…")`` and ``@pytest_asyncio.fixture(scope="…")`` with ``@pytest_asyncio.fixture(loop_scope="…", scope="…")`` such that *loop_scope* and *scope* are the same. If you use auto mode, resolve all import errors from missing imports of *pytest_asyncio*. If your async fixtures all use the same *loop_scope*, you may choose to set the *asyncio_default_fixture_loop_scope* configuration option to that loop scope, instead.
2. If you haven't set *asyncio_default_fixture_loop_scope*, set it to *function* to address the deprecation warning about the unset configuration option.
2. The *asyncio_default_fixture_loop_scope* configuration option now defaults to *function* when unset.
3. Change all occurrences of ``pytest.mark.asyncio(scope="…")`` to ``pytest.mark.asyncio(loop_scope="…")`` to address the deprecation warning about the *scope* argument to the *asyncio* marker.
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
23 changes: 23 additions & 0 deletions tests/test_fixture_loop_scopes.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,29 @@ 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 pytest
import pytest_asyncio

@pytest_asyncio.fixture
async def value():
return 1

@pytest.mark.asyncio
async def test_value(value):
assert value == 1
"""))

result = pytester.runpytest_subprocess("--asyncio-mode=strict", "-W", "error")
result.assert_outcomes(passed=1)
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,
Expand Down
Loading