31
31
FixtureRequest ,
32
32
Function ,
33
33
Item ,
34
+ Metafunc ,
34
35
Parser ,
35
36
PytestPluginManager ,
36
37
Session ,
@@ -367,6 +368,7 @@ def pytest_collectstart(collector: pytest.Collector):
367
368
for mark in marks :
368
369
if not mark .name == "asyncio_event_loop" :
369
370
continue
371
+ event_loop_policy = mark .kwargs .get ("policy" , asyncio .get_event_loop_policy ())
370
372
371
373
# There seem to be issues when a fixture is shadowed by another fixture
372
374
# and both differ in their params.
@@ -381,13 +383,23 @@ def pytest_collectstart(collector: pytest.Collector):
381
383
@pytest .fixture (
382
384
scope = "class" if isinstance (collector , pytest .Class ) else "module" ,
383
385
name = event_loop_fixture_id ,
386
+ params = (event_loop_policy ,),
387
+ ids = (type (event_loop_policy ).__name__ ,),
384
388
)
385
389
def scoped_event_loop (
386
390
* args , # Function needs to accept "cls" when collected by pytest.Class
391
+ request ,
387
392
) -> Iterator [asyncio .AbstractEventLoop ]:
388
- loop = asyncio .get_event_loop_policy ().new_event_loop ()
393
+ new_loop_policy = request .param
394
+ old_loop_policy = asyncio .get_event_loop_policy ()
395
+ old_loop = asyncio .get_event_loop ()
396
+ asyncio .set_event_loop_policy (new_loop_policy )
397
+ loop = asyncio .new_event_loop ()
398
+ asyncio .set_event_loop (loop )
389
399
yield loop
390
400
loop .close ()
401
+ asyncio .set_event_loop_policy (old_loop_policy )
402
+ asyncio .set_event_loop (old_loop )
391
403
392
404
# @pytest.fixture does not register the fixture anywhere, so pytest doesn't
393
405
# know it exists. We work around this by attaching the fixture function to the
@@ -430,6 +442,33 @@ def _hypothesis_test_wraps_coroutine(function: Any) -> bool:
430
442
return _is_coroutine (function .hypothesis .inner_test )
431
443
432
444
445
+ @pytest .hookimpl (tryfirst = True )
446
+ def pytest_generate_tests (metafunc : Metafunc ) -> None :
447
+ for event_loop_provider_node , _ in metafunc .definition .iter_markers_with_node (
448
+ "asyncio_event_loop"
449
+ ):
450
+ event_loop_fixture_id = event_loop_provider_node .stash .get (
451
+ _event_loop_fixture_id , None
452
+ )
453
+ if event_loop_fixture_id :
454
+ event_loop_fixture_id = event_loop_provider_node .stash [
455
+ _event_loop_fixture_id
456
+ ]
457
+ fixturemanager = metafunc .config .pluginmanager .get_plugin ("funcmanage" )
458
+ if "event_loop" in metafunc .fixturenames :
459
+ raise MultipleEventLoopsRequestedError (
460
+ _MULTIPLE_LOOPS_REQUESTED_ERROR
461
+ % (metafunc .definition .nodeid , event_loop_provider_node .nodeid ),
462
+ )
463
+ # Add the scoped event loop fixture to Metafunc's list of fixture names and
464
+ # fixturedefs and leave the actual parametrization to pytest
465
+ metafunc .fixturenames .insert (0 , event_loop_fixture_id )
466
+ metafunc ._arg2fixturedefs [
467
+ event_loop_fixture_id
468
+ ] = fixturemanager ._arg2fixturedefs [event_loop_fixture_id ]
469
+ break
470
+
471
+
433
472
@pytest .hookimpl (hookwrapper = True )
434
473
def pytest_fixture_setup (
435
474
fixturedef : FixtureDef , request : SubRequest
@@ -609,18 +648,12 @@ def pytest_runtest_setup(item: pytest.Item) -> None:
609
648
return
610
649
event_loop_fixture_id = "event_loop"
611
650
for node , mark in item .iter_markers_with_node ("asyncio_event_loop" ):
612
- scoped_event_loop_provider_node = node
613
651
event_loop_fixture_id = node .stash .get (_event_loop_fixture_id , None )
614
652
if event_loop_fixture_id :
615
653
break
616
654
fixturenames = item .fixturenames # type: ignore[attr-defined]
617
655
# inject an event loop fixture for all async tests
618
656
if "event_loop" in fixturenames :
619
- if event_loop_fixture_id != "event_loop" :
620
- raise MultipleEventLoopsRequestedError (
621
- _MULTIPLE_LOOPS_REQUESTED_ERROR
622
- % (item .nodeid , scoped_event_loop_provider_node .nodeid ),
623
- )
624
657
fixturenames .remove ("event_loop" )
625
658
fixturenames .insert (0 , event_loop_fixture_id )
626
659
obj = getattr (item , "obj" , None )
0 commit comments