@@ -213,7 +213,8 @@ def pytest_pycollect_makeitem(
213213 and _hypothesis_test_wraps_coroutine (obj )
214214 ):
215215 item = pytest .Function .from_parent (collector , name = name )
216- if "asyncio" in item .keywords :
216+ marker = item .get_closest_marker ("asyncio" )
217+ if marker is not None :
217218 return list (collector ._genfunctions (name , obj ))
218219 else :
219220 if _get_asyncio_mode (item .config ) == Mode .AUTO :
@@ -390,16 +391,19 @@ def pytest_pyfunc_call(pyfuncitem: pytest.Function) -> Optional[object]:
390391 Wraps marked tests in a synchronous function
391392 where the wrapped test coroutine is executed in an event loop.
392393 """
393- if "asyncio" in pyfuncitem .keywords :
394+ marker = pyfuncitem .get_closest_marker ("asyncio" )
395+ if marker is not None :
394396 funcargs : Dict [str , object ] = pyfuncitem .funcargs # type: ignore[name-defined]
395397 loop = cast (asyncio .AbstractEventLoop , funcargs ["event_loop" ])
396398 if _is_hypothesis_test (pyfuncitem .obj ):
397399 pyfuncitem .obj .hypothesis .inner_test = wrap_in_sync (
400+ pyfuncitem ,
398401 pyfuncitem .obj .hypothesis .inner_test ,
399402 _loop = loop ,
400403 )
401404 else :
402405 pyfuncitem .obj = wrap_in_sync (
406+ pyfuncitem ,
403407 pyfuncitem .obj ,
404408 _loop = loop ,
405409 )
@@ -410,7 +414,11 @@ def _is_hypothesis_test(function: Any) -> bool:
410414 return getattr (function , "is_hypothesis_test" , False )
411415
412416
413- def wrap_in_sync (func : Callable [..., Awaitable [Any ]], _loop : asyncio .AbstractEventLoop ):
417+ def wrap_in_sync (
418+ pyfuncitem : pytest .Function ,
419+ func : Callable [..., Awaitable [Any ]],
420+ _loop : asyncio .AbstractEventLoop ,
421+ ):
414422 """Return a sync wrapper around an async function executing it in the
415423 current event loop."""
416424
@@ -424,34 +432,44 @@ def wrap_in_sync(func: Callable[..., Awaitable[Any]], _loop: asyncio.AbstractEve
424432 @functools .wraps (func )
425433 def inner (** kwargs ):
426434 coro = func (** kwargs )
427- if coro is not None :
428- task = asyncio .ensure_future (coro , loop = _loop )
429- try :
430- _loop .run_until_complete (task )
431- except BaseException :
432- # run_until_complete doesn't get the result from exceptions
433- # that are not subclasses of `Exception`. Consume all
434- # exceptions to prevent asyncio's warning from logging.
435- if task .done () and not task .cancelled ():
436- task .exception ()
437- raise
435+ if not inspect .isawaitable (coro ):
436+ pyfuncitem .warn (
437+ pytest .PytestWarning (
438+ f"The test { pyfuncitem } is marked with '@pytest.mark.asyncio' "
439+ "but it is not an async function. "
440+ "Please remove asyncio marker. "
441+ "If the test is not marked explicitly, "
442+ "check for global markers applied via 'pytestmark'."
443+ )
444+ )
445+ return
446+ task = asyncio .ensure_future (coro , loop = _loop )
447+ try :
448+ _loop .run_until_complete (task )
449+ except BaseException :
450+ # run_until_complete doesn't get the result from exceptions
451+ # that are not subclasses of `Exception`. Consume all
452+ # exceptions to prevent asyncio's warning from logging.
453+ if task .done () and not task .cancelled ():
454+ task .exception ()
455+ raise
438456
439457 inner ._raw_test_func = func # type: ignore[attr-defined]
440458 return inner
441459
442460
443461def pytest_runtest_setup (item : pytest .Item ) -> None :
444- if "asyncio" in item .keywords :
445- fixturenames = item .fixturenames # type: ignore[attr-defined]
446- # inject an event loop fixture for all async tests
447- if "event_loop" in fixturenames :
448- fixturenames .remove ("event_loop" )
449- fixturenames .insert (0 , "event_loop" )
462+ marker = item .get_closest_marker ("asyncio" )
463+ if marker is None :
464+ return
465+ fixturenames = item .fixturenames # type: ignore[attr-defined]
466+ # inject an event loop fixture for all async tests
467+ if "event_loop" in fixturenames :
468+ fixturenames .remove ("event_loop" )
469+ fixturenames .insert (0 , "event_loop" )
450470 obj = getattr (item , "obj" , None )
451- if (
452- item .get_closest_marker ("asyncio" ) is not None
453- and not getattr (obj , "hypothesis" , False )
454- and getattr (obj , "is_hypothesis_test" , False )
471+ if not getattr (obj , "hypothesis" , False ) and getattr (
472+ obj , "is_hypothesis_test" , False
455473 ):
456474 pytest .fail (
457475 "test function `%r` is using Hypothesis, but pytest-asyncio "
0 commit comments