@@ -558,6 +558,10 @@ def pytest_pycollect_makeitem_convert_async_functions_to_subclass(
558
558
Session : "session" ,
559
559
}
560
560
561
+ # A stack used to push package-scoped loops during collection of a package
562
+ # and pop those loops during collection of a Module
563
+ __package_loop_stack : List [Union [FixtureFunctionMarker , FixtureFunction ]] = []
564
+
561
565
562
566
@pytest .hookimpl
563
567
def pytest_collectstart (collector : pytest .Collector ):
@@ -609,31 +613,11 @@ def scoped_event_loop(
609
613
# collected Python object, where it will be picked up by pytest.Class.collect()
610
614
# or pytest.Module.collect(), respectively
611
615
if type (collector ) is Package :
612
-
613
- def _patched_collect ():
614
- # When collector is a Package, collector.obj is the package's
615
- # __init__.py. Accessing the __init__.py to attach the fixture function
616
- # may trigger additional module imports or change the order of imports,
617
- # which leads to a number of problems.
618
- # see https://github.com/pytest-dev/pytest-asyncio/issues/729
619
- # Moreover, Package.obj has been removed in pytest 8.
620
- # Therefore, pytest-asyncio attaches the packages-scoped event loop
621
- # fixture to the first collected module in that package.
622
- package_scoped_loop_added = False
623
- for subcollector in collector .__original_collect ():
624
- if (
625
- not package_scoped_loop_added
626
- and isinstance (subcollector , Module )
627
- and getattr (subcollector , "obj" , None )
628
- ):
629
- subcollector .obj .__pytest_asyncio_package_scoped_event_loop = (
630
- scoped_event_loop
631
- )
632
- package_scoped_loop_added = True
633
- yield subcollector
634
-
635
- collector .__original_collect = collector .collect
636
- collector .collect = _patched_collect
616
+ # Packages do not have a corresponding Python object. Therefore, the fixture
617
+ # for the package-scoped event loop is added to a stack. When a module inside
618
+ # the package is collected, the module will attach the fixture to its
619
+ # Python object.
620
+ __package_loop_stack .append (scoped_event_loop )
637
621
elif isinstance (collector , Module ):
638
622
# Accessing Module.obj triggers a module import executing module-level
639
623
# statements. A module-level pytest.skip statement raises the "Skipped"
@@ -644,8 +628,14 @@ def _patched_collect():
644
628
# module before it runs the actual collection.
645
629
def _patched_collect ():
646
630
# If the collected module is a DoctestTextfile, collector.obj is None
647
- if collector .obj is not None :
648
- collector .obj .__pytest_asyncio_scoped_event_loop = scoped_event_loop
631
+ module = collector .obj
632
+ if module is not None :
633
+ module .__pytest_asyncio_scoped_event_loop = scoped_event_loop
634
+ try :
635
+ package_loop = __package_loop_stack .pop ()
636
+ module .__pytest_asyncio_package_scoped_event_loop = package_loop
637
+ except IndexError :
638
+ pass
649
639
return collector .__original_collect ()
650
640
651
641
collector .__original_collect = collector .collect
0 commit comments