Skip to content

Commit c98ef2b

Browse files
committed
change implementation so the check happens in pytest_fixture_setup after any hooks (from async plugins) has had a chance to resolve the awaitable
1 parent 7256c0c commit c98ef2b

File tree

3 files changed

+43
-55
lines changed

3 files changed

+43
-55
lines changed

src/_pytest/compat.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ class NotSetType(enum.Enum):
4444

4545

4646
def is_generator(func: object) -> bool:
47+
# note: this also returns true for async generator functions
4748
genfunc = inspect.isgeneratorfunction(func)
4849
return genfunc and not iscoroutinefunction(func)
4950

src/_pytest/fixtures.py

Lines changed: 21 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -595,42 +595,6 @@ def _get_active_fixturedef(
595595
raise FixtureLookupError(argname, self)
596596
fixturedef = fixturedefs[index]
597597

598-
# Check for attempted use of an async fixture by a sync test
599-
# `self.scope` here is not the scope of the requested fixture, but the scope of
600-
# the requester.
601-
if (
602-
self.scope == "function"
603-
and not inspect.iscoroutinefunction(self._pyfuncitem.obj)
604-
and (
605-
inspect.iscoroutinefunction(fixturedef.func)
606-
or inspect.isasyncgenfunction(fixturedef.func)
607-
)
608-
):
609-
if fixturedef._autouse:
610-
warnings.warn(
611-
PytestRemovedIn9Warning(
612-
f"Sync test {self._pyfuncitem.name!r} requested async fixture "
613-
f"{argname!r} with autouse=True. "
614-
"If you intended to use the fixture you may want to make the "
615-
"test asynchronous or the fixture synchronous. "
616-
"If you did not intend to use it you should "
617-
"restructure your test setup. "
618-
"This will turn into an error in pytest 9."
619-
),
620-
stacklevel=3,
621-
)
622-
else:
623-
warnings.warn(
624-
PytestRemovedIn9Warning(
625-
f"Sync test {self._pyfuncitem.name!r} requested async fixture "
626-
f"{argname!r}. "
627-
"You may want to make the test asynchronous and run it with "
628-
"a suitable async framework test plugin, or make the fixture synchronous. "
629-
"This will turn into an error in pytest 9."
630-
),
631-
stacklevel=3,
632-
)
633-
634598
# Prepare a SubRequest object for calling the fixture.
635599
try:
636600
callspec = self._pyfuncitem.callspec
@@ -921,6 +885,8 @@ def call_fixture_func(
921885
fixturefunc: _FixtureFunc[FixtureValue], request: FixtureRequest, kwargs
922886
) -> FixtureValue:
923887
if is_generator(fixturefunc):
888+
# note: this also triggers on async generators, suppressing 'unawaited coroutine'
889+
# warning.
924890
fixturefunc = cast(
925891
Callable[..., Generator[FixtureValue, None, None]], fixturefunc
926892
)
@@ -1179,6 +1145,25 @@ def pytest_fixture_setup(
11791145

11801146
fixturefunc = resolve_fixture_function(fixturedef, request)
11811147
my_cache_key = fixturedef.cache_key(request)
1148+
1149+
if inspect.isasyncgenfunction(fixturefunc) or inspect.iscoroutinefunction(
1150+
fixturefunc
1151+
):
1152+
auto_str = " with autouse=True" if fixturedef._autouse else ""
1153+
1154+
warnings.warn(
1155+
PytestRemovedIn9Warning(
1156+
f"{request.node.name!r} requested an async fixture "
1157+
f"{request.fixturename!r}{auto_str}, with no plugin or hook that "
1158+
"handled it. This is usually an error, as pytest does not natively "
1159+
"support it. If this is intentional, consider making the fixture "
1160+
"sync and return a coroutine/asyncgen. "
1161+
"This will turn into an error in pytest 9."
1162+
),
1163+
# no stacklevel will point at users code, so we just point here
1164+
stacklevel=1,
1165+
)
1166+
11821167
try:
11831168
result = call_fixture_func(fixturefunc, request, kwargs)
11841169
except TEST_OUTCOME as e:

testing/acceptance_test.py

Lines changed: 21 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1306,11 +1306,13 @@ def test_foo(async_fixture):
13061306
result = pytester.runpytest()
13071307
result.stdout.fnmatch_lines(
13081308
[
1309+
"*== warnings summary ==*",
13091310
(
1310-
"*Sync test 'test_foo' requested async fixture "
1311-
"'async_fixture'. "
1312-
"You may want to make the test asynchronous and run it with "
1313-
"a suitable async framework test plugin, or make the fixture synchronous. "
1311+
"*PytestRemovedIn9Warning: 'test_foo' requested an async "
1312+
"fixture 'async_fixture', with no plugin or hook that handled it. "
1313+
"This is usually an error, as pytest does not natively support it. "
1314+
"If this is intentional, consider making the fixture sync and return "
1315+
"a coroutine/asyncgen. "
13141316
"This will turn into an error in pytest 9."
13151317
),
13161318
]
@@ -1328,21 +1330,21 @@ async def async_fixture():
13281330
yield
13291331
13301332
def test_foo(async_fixture):
1331-
# suppress unawaited coroutine warning
1332-
try:
1333-
async_fixture.asend(None)
1334-
except StopIteration:
1335-
pass
1333+
# we don't need to suppress RuntimeWarning for unawaited coroutine
1334+
# as pytest internals accidentally do so already for async gens
1335+
...
13361336
"""
13371337
)
13381338
result = pytester.runpytest()
13391339
result.stdout.fnmatch_lines(
13401340
[
1341+
"*== warnings summary ==*",
13411342
(
1342-
"*Sync test 'test_foo' requested async fixture "
1343-
"'async_fixture'. "
1344-
"You may want to make the test asynchronous and run it with "
1345-
"a suitable async framework test plugin, or make the fixture synchronous. "
1343+
"*PytestRemovedIn9Warning: 'test_foo' requested an async "
1344+
"fixture 'async_fixture', with no plugin or hook that handled it. "
1345+
"This is usually an error, as pytest does not natively support it. "
1346+
"If this is intentional, consider making the fixture sync and return "
1347+
"a coroutine/asyncgen. "
13461348
"This will turn into an error in pytest 9."
13471349
),
13481350
]
@@ -1371,13 +1373,13 @@ def test_foo(async_fixture):
13711373
result = pytester.runpytest()
13721374
result.stdout.fnmatch_lines(
13731375
[
1376+
"*== warnings summary ==*",
13741377
(
1375-
"*Sync test 'test_foo' requested async fixture "
1376-
"'async_fixture' with autouse=True. "
1377-
"If you intended to use the fixture you may want to make the "
1378-
"test asynchronous or the fixture synchronous. "
1379-
"If you did not intend to use it you should "
1380-
"restructure your test setup. "
1378+
"*PytestRemovedIn9Warning: Sync test 'test_foo' requested an async "
1379+
"fixture 'async_fixture' with autouse=True, with no plugin or hook "
1380+
"that handled it. This is usually an error, as pytest does not "
1381+
"natively support it. If this is intentional, consider making the "
1382+
"fixture sync and return a coroutine/asyncgen. "
13811383
"This will turn into an error in pytest 9."
13821384
),
13831385
]

0 commit comments

Comments
 (0)