@@ -447,7 +447,14 @@ def _can_substitute(item: Function) -> bool:
447447 return inspect .iscoroutinefunction (func )
448448
449449 def runtest (self ) -> None :
450- synchronized_obj = wrap_in_sync (self .obj )
450+ marker = self .get_closest_marker ("asyncio" )
451+ assert marker is not None
452+ default_loop_scope = _get_default_test_loop_scope (self .config )
453+ loop_scope = _get_marked_loop_scope (marker , default_loop_scope )
454+ runner_fixture_id = f"_{ loop_scope } _scoped_runner"
455+ runner = self ._request .getfixturevalue (runner_fixture_id )
456+ context = contextvars .copy_context ()
457+ synchronized_obj = wrap_in_sync (self .obj , runner , context )
451458 with MonkeyPatch .context () as c :
452459 c .setattr (self , "obj" , synchronized_obj )
453460 super ().runtest ()
@@ -489,7 +496,14 @@ def _can_substitute(item: Function) -> bool:
489496 )
490497
491498 def runtest (self ) -> None :
492- synchronized_obj = wrap_in_sync (self .obj )
499+ marker = self .get_closest_marker ("asyncio" )
500+ assert marker is not None
501+ default_loop_scope = _get_default_test_loop_scope (self .config )
502+ loop_scope = _get_marked_loop_scope (marker , default_loop_scope )
503+ runner_fixture_id = f"_{ loop_scope } _scoped_runner"
504+ runner = self ._request .getfixturevalue (runner_fixture_id )
505+ context = contextvars .copy_context ()
506+ synchronized_obj = wrap_in_sync (self .obj , runner , context = context )
493507 with MonkeyPatch .context () as c :
494508 c .setattr (self , "obj" , synchronized_obj )
495509 super ().runtest ()
@@ -511,7 +525,14 @@ def _can_substitute(item: Function) -> bool:
511525 )
512526
513527 def runtest (self ) -> None :
514- synchronized_obj = wrap_in_sync (self .obj .hypothesis .inner_test )
528+ marker = self .get_closest_marker ("asyncio" )
529+ assert marker is not None
530+ default_loop_scope = _get_default_test_loop_scope (self .config )
531+ loop_scope = _get_marked_loop_scope (marker , default_loop_scope )
532+ runner_fixture_id = f"_{ loop_scope } _scoped_runner"
533+ runner = self ._request .getfixturevalue (runner_fixture_id )
534+ context = contextvars .copy_context ()
535+ synchronized_obj = wrap_in_sync (self .obj .hypothesis .inner_test , runner , context )
515536 with MonkeyPatch .context () as c :
516537 c .setattr (self .obj .hypothesis , "inner_test" , synchronized_obj )
517538 super ().runtest ()
@@ -653,27 +674,19 @@ def pytest_pyfunc_call(pyfuncitem: Function) -> object | None:
653674
654675
655676def wrap_in_sync (
656- func : Callable [..., Awaitable [Any ]],
677+ func : Callable [..., CoroutineType ],
678+ runner : asyncio .Runner ,
679+ context : contextvars .Context ,
657680):
658681 """
659- Return a sync wrapper around an async function executing it in the
660- current event loop .
682+ Return a sync wrapper around a coroutine executing it in the
683+ specified runner and context .
661684 """
662685
663686 @functools .wraps (func )
664687 def inner (* args , ** kwargs ):
665688 coro = func (* args , ** kwargs )
666- _loop = _get_event_loop_no_warn ()
667- task = asyncio .ensure_future (coro , loop = _loop )
668- try :
669- _loop .run_until_complete (task )
670- except BaseException :
671- # run_until_complete doesn't get the result from exceptions
672- # that are not subclasses of `Exception`. Consume all
673- # exceptions to prevent asyncio's warning from logging.
674- if task .done () and not task .cancelled ():
675- task .exception ()
676- raise
689+ runner .run (coro , context = context )
677690
678691 return inner
679692
0 commit comments