16
16
Awaitable ,
17
17
Callable ,
18
18
Dict ,
19
+ Generator ,
19
20
Iterable ,
20
21
Iterator ,
21
22
List ,
22
23
Literal ,
24
+ Mapping ,
23
25
Optional ,
26
+ Sequence ,
24
27
Set ,
25
28
Type ,
26
29
TypeVar ,
47
50
StashKey ,
48
51
)
49
52
50
- _R = TypeVar ("_R" )
51
-
52
53
_ScopeName = Literal ["session" , "package" , "module" , "class" , "function" ]
53
54
_T = TypeVar ("_T" )
54
55
55
56
SimpleFixtureFunction = TypeVar (
56
- "SimpleFixtureFunction" , bound = Callable [..., Awaitable [_R ]]
57
+ "SimpleFixtureFunction" , bound = Callable [..., Awaitable [object ]]
57
58
)
58
59
FactoryFixtureFunction = TypeVar (
59
- "FactoryFixtureFunction" , bound = Callable [..., AsyncIterator [_R ]]
60
+ "FactoryFixtureFunction" , bound = Callable [..., AsyncIterator [object ]]
60
61
)
61
62
FixtureFunction = Union [SimpleFixtureFunction , FactoryFixtureFunction ]
62
63
FixtureFunctionMarker = Callable [[FixtureFunction ], FixtureFunction ]
@@ -204,6 +205,7 @@ def _preprocess_async_fixtures(
204
205
config = collector .config
205
206
asyncio_mode = _get_asyncio_mode (config )
206
207
fixturemanager = config .pluginmanager .get_plugin ("funcmanage" )
208
+ assert fixturemanager is not None
207
209
for fixtures in fixturemanager ._arg2fixturedefs .values ():
208
210
for fixturedef in fixtures :
209
211
func = fixturedef .func
@@ -217,11 +219,13 @@ def _preprocess_async_fixtures(
217
219
continue
218
220
scope = fixturedef .scope
219
221
if scope == "function" :
220
- event_loop_fixture_id = "event_loop"
222
+ event_loop_fixture_id : Optional [ str ] = "event_loop"
221
223
else :
222
224
event_loop_node = _retrieve_scope_root (collector , scope )
223
225
event_loop_fixture_id = event_loop_node .stash .get (
224
- _event_loop_fixture_id , None
226
+ # Type ignored because of non-optimal mypy inference.
227
+ _event_loop_fixture_id , # type: ignore[arg-type]
228
+ None ,
225
229
)
226
230
_make_asyncio_fixture_function (func )
227
231
function_signature = inspect .signature (func )
@@ -234,8 +238,15 @@ def _preprocess_async_fixtures(
234
238
f"instead."
235
239
)
236
240
)
237
- _inject_fixture_argnames (fixturedef , event_loop_fixture_id )
238
- _synchronize_async_fixture (fixturedef , event_loop_fixture_id )
241
+ assert event_loop_fixture_id
242
+ _inject_fixture_argnames (
243
+ fixturedef ,
244
+ event_loop_fixture_id ,
245
+ )
246
+ _synchronize_async_fixture (
247
+ fixturedef ,
248
+ event_loop_fixture_id ,
249
+ )
239
250
assert _is_asyncio_fixture_function (fixturedef .func )
240
251
processed_fixturedefs .add (fixturedef )
241
252
@@ -512,25 +523,26 @@ def pytest_pycollect_makeitem_preprocess_async_fixtures(
512
523
return None
513
524
514
525
526
+ # TODO: #778 Narrow down return type of function when dropping support for pytest 7
515
527
# The function name needs to start with "pytest_"
516
528
# see https://github.com/pytest-dev/pytest/issues/11307
517
529
@pytest .hookimpl (specname = "pytest_pycollect_makeitem" , hookwrapper = True )
518
530
def pytest_pycollect_makeitem_convert_async_functions_to_subclass (
519
531
collector : Union [pytest .Module , pytest .Class ], name : str , obj : object
520
- ) -> Union [
521
- pytest .Item , pytest .Collector , List [Union [pytest .Item , pytest .Collector ]], None
522
- ]:
532
+ ) -> Generator [None , Any , None ]:
523
533
"""
524
534
Converts coroutines and async generators collected as pytest.Functions
525
535
to AsyncFunction items.
526
536
"""
527
537
hook_result = yield
528
- node_or_list_of_nodes = hook_result .get_result ()
538
+ node_or_list_of_nodes : Union [
539
+ pytest .Item , pytest .Collector , List [Union [pytest .Item , pytest .Collector ]], None
540
+ ] = hook_result .get_result ()
529
541
if not node_or_list_of_nodes :
530
542
return
531
- try :
543
+ if isinstance ( node_or_list_of_nodes , Sequence ) :
532
544
node_iterator = iter (node_or_list_of_nodes )
533
- except TypeError :
545
+ else :
534
546
# Treat single node as a single-element iterable
535
547
node_iterator = iter ((node_or_list_of_nodes ,))
536
548
updated_node_collection = []
@@ -549,8 +561,8 @@ def pytest_pycollect_makeitem_convert_async_functions_to_subclass(
549
561
hook_result .force_result (updated_node_collection )
550
562
551
563
552
- _event_loop_fixture_id = StashKey [str ]
553
- _fixture_scope_by_collector_type = {
564
+ _event_loop_fixture_id = StashKey [str ]()
565
+ _fixture_scope_by_collector_type : Mapping [ Type [ pytest . Collector ], _ScopeName ] = {
554
566
Class : "class" ,
555
567
# Package is a subclass of module and the dict is used in isinstance checks
556
568
# Therefore, the order matters and Package needs to appear before Module
@@ -565,7 +577,7 @@ def pytest_pycollect_makeitem_convert_async_functions_to_subclass(
565
577
566
578
567
579
@pytest .hookimpl
568
- def pytest_collectstart (collector : pytest .Collector ):
580
+ def pytest_collectstart (collector : pytest .Collector ) -> None :
569
581
try :
570
582
collector_scope = next (
571
583
scope
@@ -639,8 +651,8 @@ def _patched_collect():
639
651
pass
640
652
return collector .__original_collect ()
641
653
642
- collector .__original_collect = collector .collect
643
- collector .collect = _patched_collect
654
+ collector .__original_collect = collector .collect # type: ignore[attr-defined]
655
+ collector .collect = _patched_collect # type: ignore[method-assign]
644
656
elif isinstance (collector , Class ):
645
657
collector .obj .__pytest_asyncio_scoped_event_loop = scoped_event_loop
646
658
@@ -708,6 +720,7 @@ def pytest_generate_tests(metafunc: Metafunc) -> None:
708
720
if event_loop_fixture_id in metafunc .fixturenames :
709
721
return
710
722
fixturemanager = metafunc .config .pluginmanager .get_plugin ("funcmanage" )
723
+ assert fixturemanager is not None
711
724
if "event_loop" in metafunc .fixturenames :
712
725
raise MultipleEventLoopsRequestedError (
713
726
_MULTIPLE_LOOPS_REQUESTED_ERROR .format (
@@ -726,10 +739,11 @@ def pytest_generate_tests(metafunc: Metafunc) -> None:
726
739
)
727
740
728
741
742
+ # TODO: #778 Narrow down return type of function when dropping support for pytest 7
729
743
@pytest .hookimpl (hookwrapper = True )
730
744
def pytest_fixture_setup (
731
- fixturedef : FixtureDef , request : SubRequest
732
- ) -> Optional [ object ]:
745
+ fixturedef : FixtureDef ,
746
+ ) -> Generator [ None , Any , None ]:
733
747
"""Adjust the event loop policy when an event loop is produced."""
734
748
if fixturedef .argname == "event_loop" :
735
749
# The use of a fixture finalizer is preferred over the
@@ -744,7 +758,7 @@ def pytest_fixture_setup(
744
758
_provide_clean_event_loop ,
745
759
)
746
760
outcome = yield
747
- loop = outcome .get_result ()
761
+ loop : asyncio . AbstractEventLoop = outcome .get_result ()
748
762
# Weird behavior was observed when checking for an attribute of FixtureDef.func
749
763
# Instead, we now check for a special attribute of the returned event loop
750
764
fixture_filename = inspect .getsourcefile (fixturedef .func )
@@ -946,6 +960,7 @@ def _retrieve_scope_root(item: Union[Collector, Item], scope: str) -> Collector:
946
960
scope_root_type = node_type_by_scope [scope ]
947
961
for node in reversed (item .listchain ()):
948
962
if isinstance (node , scope_root_type ):
963
+ assert isinstance (node , pytest .Collector )
949
964
return node
950
965
error_message = (
951
966
f"{ item .name } is marked to be run in an event loop with scope { scope } , "
0 commit comments