From b208df678a7f17aa4b1c4cdad8c0a422dbf7f268 Mon Sep 17 00:00:00 2001
From: jakkdl
Date: Tue, 5 Nov 2024 15:59:39 +0100
Subject: [PATCH] feat: Warn if asyncio test requests async pytest fixture in
strict mode
fix test
make it raise PytestDeprecationWarning instead
add changelog entry
docs: Changed release date of v0.25.0 to UNRELEASED.
---
docs/reference/changelog.rst | 4 ++
pytest_asyncio/plugin.py | 27 +++++++++-
tests/modes/test_strict_mode.py | 87 +++++++++++++++++++++++++++++++++
3 files changed, 117 insertions(+), 1 deletion(-)
diff --git a/docs/reference/changelog.rst b/docs/reference/changelog.rst
index 77cf3451..285aadf5 100644
--- a/docs/reference/changelog.rst
+++ b/docs/reference/changelog.rst
@@ -2,6 +2,10 @@
Changelog
=========
+0.25.0 (UNRELEASED)
+===================
+- Deprecated: Added warning when asyncio test requests async ``@pytest.fixture`` in strict mode. This will become an error in a future version of flake8-asyncio. `#979 `_
+
0.24.0 (2024-08-22)
===================
- BREAKING: Updated minimum supported pytest version to v8.2.0
diff --git a/pytest_asyncio/plugin.py b/pytest_asyncio/plugin.py
index b4e6ab33..0261d12e 100644
--- a/pytest_asyncio/plugin.py
+++ b/pytest_asyncio/plugin.py
@@ -880,7 +880,32 @@ def pytest_pyfunc_call(pyfuncitem: Function) -> object | None:
"""
if pyfuncitem.get_closest_marker("asyncio") is not None:
if isinstance(pyfuncitem, PytestAsyncioFunction):
- pass
+ asyncio_mode = _get_asyncio_mode(pyfuncitem.config)
+ for fixname, fixtures in pyfuncitem._fixtureinfo.name2fixturedefs.items():
+ # name2fixturedefs is a dict between fixture name and a list of matching
+ # fixturedefs. The last entry in the list is closest and the one used.
+ func = fixtures[-1].func
+ if (
+ _is_coroutine_or_asyncgen(func)
+ and not _is_asyncio_fixture_function(func)
+ and asyncio_mode == Mode.STRICT
+ ):
+ warnings.warn(
+ PytestDeprecationWarning(
+ f"asyncio test {pyfuncitem.name!r} requested async "
+ "@pytest.fixture "
+ f"{fixname!r} in strict mode. "
+ "You might want to use @pytest_asyncio.fixture or switch "
+ "to auto mode. "
+ "This will become an error in future versions of "
+ "flake8-asyncio."
+ ),
+ stacklevel=1,
+ )
+ # no stacklevel points at the users code, so we set stacklevel=1
+ # so it at least indicates that it's the plugin complaining.
+ # Pytest gives the test file & name in the warnings summary at least
+
else:
pyfuncitem.warn(
pytest.PytestWarning(
diff --git a/tests/modes/test_strict_mode.py b/tests/modes/test_strict_mode.py
index c5a7351a..52cbb251 100644
--- a/tests/modes/test_strict_mode.py
+++ b/tests/modes/test_strict_mode.py
@@ -124,3 +124,90 @@ async def test_anything(any_fixture):
"*coroutine 'any_fixture' was never awaited*",
],
)
+
+
+def test_strict_mode_marked_test_unmarked_fixture_warning(pytester: Pytester):
+ pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function")
+ pytester.makepyfile(
+ dedent(
+ """\
+ import pytest
+
+ # Not using pytest_asyncio.fixture
+ @pytest.fixture()
+ async def any_fixture():
+ pass
+
+ @pytest.mark.asyncio
+ async def test_anything(any_fixture):
+ # suppress unawaited coroutine warning
+ try:
+ any_fixture.send(None)
+ except StopIteration:
+ pass
+ """
+ )
+ )
+ result = pytester.runpytest_subprocess("--asyncio-mode=strict", "-W default")
+ result.assert_outcomes(passed=1, failed=0, skipped=0, warnings=1)
+ result.stdout.fnmatch_lines(
+ [
+ "*warnings summary*",
+ (
+ "test_strict_mode_marked_test_unmarked_fixture_warning.py::"
+ "test_anything"
+ ),
+ (
+ "*/pytest_asyncio/plugin.py:*: PytestDeprecationWarning: "
+ "asyncio test 'test_anything' requested async "
+ "@pytest.fixture 'any_fixture' in strict mode. "
+ "You might want to use @pytest_asyncio.fixture or switch to "
+ "auto mode. "
+ "This will become an error in future versions of flake8-asyncio."
+ ),
+ ],
+ )
+
+
+# autouse is not handled in any special way currently
+def test_strict_mode_marked_test_unmarked_autouse_fixture_warning(pytester: Pytester):
+ pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function")
+ pytester.makepyfile(
+ dedent(
+ """\
+ import pytest
+
+ # Not using pytest_asyncio.fixture
+ @pytest.fixture(autouse=True)
+ async def any_fixture():
+ pass
+
+ @pytest.mark.asyncio
+ async def test_anything(any_fixture):
+ # suppress unawaited coroutine warning
+ try:
+ any_fixture.send(None)
+ except StopIteration:
+ pass
+ """
+ )
+ )
+ result = pytester.runpytest_subprocess("--asyncio-mode=strict", "-W default")
+ result.assert_outcomes(passed=1, warnings=1)
+ result.stdout.fnmatch_lines(
+ [
+ "*warnings summary*",
+ (
+ "test_strict_mode_marked_test_unmarked_autouse_fixture_warning.py::"
+ "test_anything"
+ ),
+ (
+ "*/pytest_asyncio/plugin.py:*: PytestDeprecationWarning: "
+ "*asyncio test 'test_anything' requested async "
+ "@pytest.fixture 'any_fixture' in strict mode. "
+ "You might want to use @pytest_asyncio.fixture or switch to "
+ "auto mode. "
+ "This will become an error in future versions of flake8-asyncio."
+ ),
+ ],
+ )