@@ -558,6 +558,10 @@ def pytest_pycollect_makeitem_convert_async_functions_to_subclass(
558558 Session : "session" ,
559559}
560560
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+
561565
562566@pytest .hookimpl
563567def pytest_collectstart (collector : pytest .Collector ):
@@ -609,31 +613,11 @@ def scoped_event_loop(
609613 # collected Python object, where it will be picked up by pytest.Class.collect()
610614 # or pytest.Module.collect(), respectively
611615 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 )
637621 elif isinstance (collector , Module ):
638622 # Accessing Module.obj triggers a module import executing module-level
639623 # statements. A module-level pytest.skip statement raises the "Skipped"
@@ -644,8 +628,14 @@ def _patched_collect():
644628 # module before it runs the actual collection.
645629 def _patched_collect ():
646630 # 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
649639 return collector .__original_collect ()
650640
651641 collector .__original_collect = collector .collect
0 commit comments