From a3ce79ec14faef2b8b4e5a3b0eb3fb3a76ec89a7 Mon Sep 17 00:00:00 2001 From: Michael Seifert Date: Wed, 18 Jun 2025 08:51:54 +0200 Subject: [PATCH 01/10] refactor: _wrap_async_fixture returns a Callable rather than modifying the FixtureDef --- pytest_asyncio/plugin.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/pytest_asyncio/plugin.py b/pytest_asyncio/plugin.py index aecf6e96..6e35f862 100644 --- a/pytest_asyncio/plugin.py +++ b/pytest_asyncio/plugin.py @@ -268,7 +268,7 @@ def _synchronize_async_fixture(fixturedef: FixtureDef) -> None: if inspect.isasyncgenfunction(fixturedef.func): _wrap_asyncgen_fixture(fixturedef) elif inspect.iscoroutinefunction(fixturedef.func): - _wrap_async_fixture(fixturedef) + fixturedef.func = _wrap_async_fixture(fixturedef) # type: ignore[misc] def _add_kwargs( @@ -346,12 +346,12 @@ async def async_finalizer() -> None: fixturedef.func = _asyncgen_fixture_wrapper # type: ignore[misc] -def _wrap_async_fixture(fixturedef: FixtureDef) -> None: - fixture = fixturedef.func +def _wrap_async_fixture(fixturedef: FixtureDef) -> Callable: + fixture_function = fixturedef.func - @functools.wraps(fixture) + @functools.wraps(fixture_function) def _async_fixture_wrapper(request: FixtureRequest, **kwargs: Any): - func = _perhaps_rebind_fixture_func(fixture, request.instance) + func = _perhaps_rebind_fixture_func(fixture_function, request.instance) event_loop_fixture_id = _get_event_loop_fixture_id_for_async_fixture( request, func ) @@ -380,7 +380,7 @@ async def setup(): return result - fixturedef.func = _async_fixture_wrapper # type: ignore[misc] + return _async_fixture_wrapper def _get_event_loop_fixture_id_for_async_fixture( From 58aab032d9539cfeccf4f4eebf67da0caeb6e56c Mon Sep 17 00:00:00 2001 From: Michael Seifert Date: Wed, 18 Jun 2025 08:53:15 +0200 Subject: [PATCH 02/10] refactor: _wrap_asyncgen_fixture returns a Callable rather than modifying the FixtureDef --- pytest_asyncio/plugin.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pytest_asyncio/plugin.py b/pytest_asyncio/plugin.py index 6e35f862..8e67b66c 100644 --- a/pytest_asyncio/plugin.py +++ b/pytest_asyncio/plugin.py @@ -266,7 +266,7 @@ def _preprocess_async_fixtures( def _synchronize_async_fixture(fixturedef: FixtureDef) -> None: """Wraps the fixture function of an async fixture in a synchronous function.""" if inspect.isasyncgenfunction(fixturedef.func): - _wrap_asyncgen_fixture(fixturedef) + fixturedef.func = _wrap_asyncgen_fixture(fixturedef) # type: ignore[misc] elif inspect.iscoroutinefunction(fixturedef.func): fixturedef.func = _wrap_async_fixture(fixturedef) # type: ignore[misc] @@ -299,7 +299,7 @@ def _perhaps_rebind_fixture_func(func: _T, instance: Any | None) -> _T: return func -def _wrap_asyncgen_fixture(fixturedef: FixtureDef) -> None: +def _wrap_asyncgen_fixture(fixturedef: FixtureDef) -> Callable: fixture = fixturedef.func @functools.wraps(fixture) @@ -343,7 +343,7 @@ async def async_finalizer() -> None: request.addfinalizer(finalizer) return result - fixturedef.func = _asyncgen_fixture_wrapper # type: ignore[misc] + return _asyncgen_fixture_wrapper def _wrap_async_fixture(fixturedef: FixtureDef) -> Callable: From eb8793fd83219c129a23aa9b7d62b12320b78d18 Mon Sep 17 00:00:00 2001 From: Michael Seifert Date: Wed, 18 Jun 2025 09:48:38 +0200 Subject: [PATCH 03/10] refactor: _wrap_async_fixture receives the Fixture function rather than the FixtureDef. --- pytest_asyncio/plugin.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/pytest_asyncio/plugin.py b/pytest_asyncio/plugin.py index 8e67b66c..aa60091e 100644 --- a/pytest_asyncio/plugin.py +++ b/pytest_asyncio/plugin.py @@ -21,6 +21,7 @@ Iterator, Sequence, ) +from types import CoroutineType from typing import ( Any, Callable, @@ -268,7 +269,7 @@ def _synchronize_async_fixture(fixturedef: FixtureDef) -> None: if inspect.isasyncgenfunction(fixturedef.func): fixturedef.func = _wrap_asyncgen_fixture(fixturedef) # type: ignore[misc] elif inspect.iscoroutinefunction(fixturedef.func): - fixturedef.func = _wrap_async_fixture(fixturedef) # type: ignore[misc] + fixturedef.func = _wrap_async_fixture(fixturedef.func) # type: ignore[misc] def _add_kwargs( @@ -346,10 +347,14 @@ async def async_finalizer() -> None: return _asyncgen_fixture_wrapper -def _wrap_async_fixture(fixturedef: FixtureDef) -> Callable: - fixture_function = fixturedef.func +AsyncFixtureReturnType = TypeVar("AsyncFixtureReturnType") - @functools.wraps(fixture_function) + +def _wrap_async_fixture( + fixture_function: Callable[..., CoroutineType[Any, Any, AsyncFixtureReturnType]], +) -> Callable[..., AsyncFixtureReturnType]: + + @functools.wraps(fixture_function) # type: ignore[arg-type] def _async_fixture_wrapper(request: FixtureRequest, **kwargs: Any): func = _perhaps_rebind_fixture_func(fixture_function, request.instance) event_loop_fixture_id = _get_event_loop_fixture_id_for_async_fixture( From 8277e953c976fc5741f6e85fab484233b3b964ef Mon Sep 17 00:00:00 2001 From: Michael Seifert Date: Wed, 18 Jun 2025 09:52:24 +0200 Subject: [PATCH 04/10] refactor: _wrap_async_fixture forwards positional args from the original fixture function to the wrapper. --- pytest_asyncio/plugin.py | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/pytest_asyncio/plugin.py b/pytest_asyncio/plugin.py index aa60091e..85da35af 100644 --- a/pytest_asyncio/plugin.py +++ b/pytest_asyncio/plugin.py @@ -51,9 +51,9 @@ ) if sys.version_info >= (3, 10): - from typing import ParamSpec + from typing import Concatenate, ParamSpec else: - from typing_extensions import ParamSpec + from typing_extensions import Concatenate, ParamSpec _ScopeName = Literal["session", "package", "module", "class", "function"] @@ -347,15 +347,22 @@ async def async_finalizer() -> None: return _asyncgen_fixture_wrapper +AsyncFixtureParams = ParamSpec("AsyncFixtureParams") AsyncFixtureReturnType = TypeVar("AsyncFixtureReturnType") def _wrap_async_fixture( - fixture_function: Callable[..., CoroutineType[Any, Any, AsyncFixtureReturnType]], -) -> Callable[..., AsyncFixtureReturnType]: + fixture_function: Callable[ + AsyncFixtureParams, CoroutineType[Any, Any, AsyncFixtureReturnType] + ], +) -> Callable[Concatenate[FixtureRequest, AsyncFixtureParams], AsyncFixtureReturnType]: @functools.wraps(fixture_function) # type: ignore[arg-type] - def _async_fixture_wrapper(request: FixtureRequest, **kwargs: Any): + def _async_fixture_wrapper( + request: FixtureRequest, + *args: AsyncFixtureParams.args, + **kwargs: AsyncFixtureParams.kwargs, + ): func = _perhaps_rebind_fixture_func(fixture_function, request.instance) event_loop_fixture_id = _get_event_loop_fixture_id_for_async_fixture( request, func @@ -364,7 +371,7 @@ def _async_fixture_wrapper(request: FixtureRequest, **kwargs: Any): kwargs.pop(event_loop_fixture_id, None) async def setup(): - res = await func(**_add_kwargs(func, kwargs, request)) + res = await func(*args, **_add_kwargs(func, kwargs, request)) return res context = contextvars.copy_context() From d5afe7ddaee71ffff02f6735b89644e321318457 Mon Sep 17 00:00:00 2001 From: Michael Seifert Date: Wed, 18 Jun 2025 10:32:42 +0200 Subject: [PATCH 05/10] refactor: _wrap_asyncgen_fixture receives the fixture function rather than the FixtureDef. --- pytest_asyncio/plugin.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/pytest_asyncio/plugin.py b/pytest_asyncio/plugin.py index 85da35af..a9e961e0 100644 --- a/pytest_asyncio/plugin.py +++ b/pytest_asyncio/plugin.py @@ -21,7 +21,7 @@ Iterator, Sequence, ) -from types import CoroutineType +from types import AsyncGeneratorType, CoroutineType from typing import ( Any, Callable, @@ -267,7 +267,7 @@ def _preprocess_async_fixtures( def _synchronize_async_fixture(fixturedef: FixtureDef) -> None: """Wraps the fixture function of an async fixture in a synchronous function.""" if inspect.isasyncgenfunction(fixturedef.func): - fixturedef.func = _wrap_asyncgen_fixture(fixturedef) # type: ignore[misc] + fixturedef.func = _wrap_asyncgen_fixture(fixturedef.func) # type: ignore[misc] elif inspect.iscoroutinefunction(fixturedef.func): fixturedef.func = _wrap_async_fixture(fixturedef.func) # type: ignore[misc] @@ -300,12 +300,15 @@ def _perhaps_rebind_fixture_func(func: _T, instance: Any | None) -> _T: return func -def _wrap_asyncgen_fixture(fixturedef: FixtureDef) -> Callable: - fixture = fixturedef.func +AsyncGenFixtureYieldType = TypeVar("AsyncGenFixtureYieldType") - @functools.wraps(fixture) + +def _wrap_asyncgen_fixture( + fixture_function: Callable[..., AsyncGeneratorType[AsyncGenFixtureYieldType, Any]], +) -> Callable[..., AsyncGenFixtureYieldType]: + @functools.wraps(fixture_function) def _asyncgen_fixture_wrapper(request: FixtureRequest, **kwargs: Any): - func = _perhaps_rebind_fixture_func(fixture, request.instance) + func = _perhaps_rebind_fixture_func(fixture_function, request.instance) event_loop_fixture_id = _get_event_loop_fixture_id_for_async_fixture( request, func ) From ca41a2aec4f14ea122d5056d65211ac0795597e5 Mon Sep 17 00:00:00 2001 From: Michael Seifert Date: Wed, 18 Jun 2025 10:37:51 +0200 Subject: [PATCH 06/10] refactor: _wrap_asyncgen_fixture forwards positional args from the original fixture function to the wrapper. --- pytest_asyncio/plugin.py | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/pytest_asyncio/plugin.py b/pytest_asyncio/plugin.py index a9e961e0..73370704 100644 --- a/pytest_asyncio/plugin.py +++ b/pytest_asyncio/plugin.py @@ -300,21 +300,30 @@ def _perhaps_rebind_fixture_func(func: _T, instance: Any | None) -> _T: return func +AsyncGenFixtureParams = ParamSpec("AsyncGenFixtureParams") AsyncGenFixtureYieldType = TypeVar("AsyncGenFixtureYieldType") def _wrap_asyncgen_fixture( - fixture_function: Callable[..., AsyncGeneratorType[AsyncGenFixtureYieldType, Any]], -) -> Callable[..., AsyncGenFixtureYieldType]: + fixture_function: Callable[ + AsyncGenFixtureParams, AsyncGeneratorType[AsyncGenFixtureYieldType, Any] + ], +) -> Callable[ + Concatenate[FixtureRequest, AsyncGenFixtureParams], AsyncGenFixtureYieldType +]: @functools.wraps(fixture_function) - def _asyncgen_fixture_wrapper(request: FixtureRequest, **kwargs: Any): + def _asyncgen_fixture_wrapper( + request: FixtureRequest, + *args: AsyncGenFixtureParams.args, + **kwargs: AsyncGenFixtureParams.kwargs, + ): func = _perhaps_rebind_fixture_func(fixture_function, request.instance) event_loop_fixture_id = _get_event_loop_fixture_id_for_async_fixture( request, func ) event_loop = request.getfixturevalue(event_loop_fixture_id) kwargs.pop(event_loop_fixture_id, None) - gen_obj = func(**_add_kwargs(func, kwargs, request)) + gen_obj = func(*args, **_add_kwargs(func, kwargs, request)) async def setup(): res = await gen_obj.__anext__() # type: ignore[union-attr] From 7ef628014e2fcc7c5f8b27fbfc8b3a8e91572e9b Mon Sep 17 00:00:00 2001 From: Michael Seifert Date: Wed, 18 Jun 2025 10:46:17 +0200 Subject: [PATCH 07/10] refactor: _synchronize_fixture returns a callable rather than modifying the fixture. --- pytest_asyncio/plugin.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/pytest_asyncio/plugin.py b/pytest_asyncio/plugin.py index 73370704..6b2a67d7 100644 --- a/pytest_asyncio/plugin.py +++ b/pytest_asyncio/plugin.py @@ -259,17 +259,19 @@ def _preprocess_async_fixtures( _make_asyncio_fixture_function(func, loop_scope) if "request" not in fixturedef.argnames: fixturedef.argnames += ("request",) - _synchronize_async_fixture(fixturedef) + fixturedef.func = _fixture_synchronizer(fixturedef) # type: ignore[misc] assert _is_asyncio_fixture_function(fixturedef.func) processed_fixturedefs.add(fixturedef) -def _synchronize_async_fixture(fixturedef: FixtureDef) -> None: - """Wraps the fixture function of an async fixture in a synchronous function.""" +def _fixture_synchronizer(fixturedef: FixtureDef) -> Callable: + """Returns a synchronous function evaluating the specified fixture.""" if inspect.isasyncgenfunction(fixturedef.func): - fixturedef.func = _wrap_asyncgen_fixture(fixturedef.func) # type: ignore[misc] + return _wrap_asyncgen_fixture(fixturedef.func) elif inspect.iscoroutinefunction(fixturedef.func): - fixturedef.func = _wrap_async_fixture(fixturedef.func) # type: ignore[misc] + return _wrap_async_fixture(fixturedef.func) + else: + return fixturedef.func def _add_kwargs( From b04c4607cb8d5ae6914e58759dcfb6331bd591c3 Mon Sep 17 00:00:00 2001 From: Michael Seifert Date: Fri, 20 Jun 2025 07:42:03 +0200 Subject: [PATCH 08/10] refactor: Async fixtures are synchronized on demand rather than statically. Previously, async coroutines and async generators used as fixture functions were wrapped with a fixture synchronizer during collection time. This allowed fixture function to be run as synchronous functions. This patch changes the synchronization to occur during the pytest_fixture_setup hook. The synchronization is now temporary, which means the wrapper fixture function is restored after the fixture setup has finished. --- pytest_asyncio/plugin.py | 77 ++++++++++++++-------------------------- 1 file changed, 27 insertions(+), 50 deletions(-) diff --git a/pytest_asyncio/plugin.py b/pytest_asyncio/plugin.py index 6b2a67d7..9ff2af71 100644 --- a/pytest_asyncio/plugin.py +++ b/pytest_asyncio/plugin.py @@ -36,7 +36,6 @@ import pytest from _pytest.scope import Scope from pytest import ( - Collector, Config, FixtureDef, FixtureRequest, @@ -44,6 +43,7 @@ Item, Mark, Metafunc, + MonkeyPatch, Parser, PytestCollectionWarning, PytestDeprecationWarning, @@ -231,39 +231,6 @@ def pytest_report_header(config: Config) -> list[str]: ] -def _preprocess_async_fixtures( - collector: Collector, - processed_fixturedefs: set[FixtureDef], -) -> None: - config = collector.config - default_loop_scope = config.getini("asyncio_default_fixture_loop_scope") - asyncio_mode = _get_asyncio_mode(config) - fixturemanager = config.pluginmanager.get_plugin("funcmanage") - assert fixturemanager is not None - for fixtures in fixturemanager._arg2fixturedefs.values(): - for fixturedef in fixtures: - func = fixturedef.func - if fixturedef in processed_fixturedefs or not _is_coroutine_or_asyncgen( - func - ): - continue - if asyncio_mode == Mode.STRICT and not _is_asyncio_fixture_function(func): - # Ignore async fixtures without explicit asyncio mark in strict mode - # This applies to pytest_trio fixtures, for example - continue - loop_scope = ( - getattr(func, "_loop_scope", None) - or default_loop_scope - or fixturedef.scope - ) - _make_asyncio_fixture_function(func, loop_scope) - if "request" not in fixturedef.argnames: - fixturedef.argnames += ("request",) - fixturedef.func = _fixture_synchronizer(fixturedef) # type: ignore[misc] - assert _is_asyncio_fixture_function(fixturedef.func) - processed_fixturedefs.add(fixturedef) - - def _fixture_synchronizer(fixturedef: FixtureDef) -> Callable: """Returns a synchronous function evaluating the specified fixture.""" if inspect.isasyncgenfunction(fixturedef.func): @@ -599,22 +566,6 @@ def runtest(self) -> None: super().runtest() -_HOLDER: set[FixtureDef] = set() - - -# The function name needs to start with "pytest_" -# see https://github.com/pytest-dev/pytest/issues/11307 -@pytest.hookimpl(specname="pytest_pycollect_makeitem", tryfirst=True) -def pytest_pycollect_makeitem_preprocess_async_fixtures( - collector: pytest.Module | pytest.Class, name: str, obj: object -) -> pytest.Item | pytest.Collector | list[pytest.Item | pytest.Collector] | None: - """A pytest hook to collect asyncio coroutines.""" - if not collector.funcnamefilter(name): - return None - _preprocess_async_fixtures(collector, _HOLDER) - return None - - # The function name needs to start with "pytest_" # see https://github.com/pytest-dev/pytest/issues/11307 @pytest.hookimpl(specname="pytest_pycollect_makeitem", hookwrapper=True) @@ -829,6 +780,32 @@ def pytest_runtest_setup(item: pytest.Item) -> None: ) +@pytest.hookimpl(wrapper=True) +def pytest_fixture_setup(fixturedef: FixtureDef, request) -> object | None: + asyncio_mode = _get_asyncio_mode(request.config) + if not _is_asyncio_fixture_function(fixturedef.func): + if asyncio_mode == Mode.STRICT: + # Ignore async fixtures without explicit asyncio mark in strict mode + # This applies to pytest_trio fixtures, for example + return (yield) + if not _is_coroutine_or_asyncgen(fixturedef.func): + return (yield) + default_loop_scope = request.config.getini("asyncio_default_fixture_loop_scope") + loop_scope = ( + getattr(fixturedef.func, "_loop_scope", None) + or default_loop_scope + or fixturedef.scope + ) + synchronizer = _fixture_synchronizer(fixturedef) + _make_asyncio_fixture_function(synchronizer, loop_scope) + with MonkeyPatch.context() as c: + if "request" not in fixturedef.argnames: + c.setattr(fixturedef, "argnames", (*fixturedef.argnames, "request")) + c.setattr(fixturedef, "func", synchronizer) + hook_result = yield + return hook_result + + _DUPLICATE_LOOP_SCOPE_DEFINITION_ERROR = """\ An asyncio pytest marker defines both "scope" and "loop_scope", \ but it should only use "loop_scope". From f0f8f7ae66f9deb310e832267d40a8cad131c740 Mon Sep 17 00:00:00 2001 From: Michael Seifert Date: Fri, 20 Jun 2025 07:55:27 +0200 Subject: [PATCH 09/10] refactor: Removed obsolete logic for removing the name of the loop fixture from a fixture wrapper's arguments. --- pytest_asyncio/plugin.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/pytest_asyncio/plugin.py b/pytest_asyncio/plugin.py index 9ff2af71..2ef66d62 100644 --- a/pytest_asyncio/plugin.py +++ b/pytest_asyncio/plugin.py @@ -291,7 +291,6 @@ def _asyncgen_fixture_wrapper( request, func ) event_loop = request.getfixturevalue(event_loop_fixture_id) - kwargs.pop(event_loop_fixture_id, None) gen_obj = func(*args, **_add_kwargs(func, kwargs, request)) async def setup(): @@ -349,7 +348,6 @@ def _async_fixture_wrapper( request, func ) event_loop = request.getfixturevalue(event_loop_fixture_id) - kwargs.pop(event_loop_fixture_id, None) async def setup(): res = await func(*args, **_add_kwargs(func, kwargs, request)) From dfa855f40e45dc2c5e0fd367f207022eb5f340d9 Mon Sep 17 00:00:00 2001 From: Michael Seifert Date: Fri, 20 Jun 2025 08:37:42 +0200 Subject: [PATCH 10/10] refactor: Fixture synchronizers rely on the caller to pass the correct event loop instance. --- pytest_asyncio/plugin.py | 35 ++++++++++------------------------- 1 file changed, 10 insertions(+), 25 deletions(-) diff --git a/pytest_asyncio/plugin.py b/pytest_asyncio/plugin.py index 2ef66d62..5eadd110 100644 --- a/pytest_asyncio/plugin.py +++ b/pytest_asyncio/plugin.py @@ -28,7 +28,6 @@ Literal, TypeVar, Union, - cast, overload, ) @@ -231,12 +230,14 @@ def pytest_report_header(config: Config) -> list[str]: ] -def _fixture_synchronizer(fixturedef: FixtureDef) -> Callable: +def _fixture_synchronizer( + fixturedef: FixtureDef, event_loop: AbstractEventLoop +) -> Callable: """Returns a synchronous function evaluating the specified fixture.""" if inspect.isasyncgenfunction(fixturedef.func): - return _wrap_asyncgen_fixture(fixturedef.func) + return _wrap_asyncgen_fixture(fixturedef.func, event_loop) elif inspect.iscoroutinefunction(fixturedef.func): - return _wrap_async_fixture(fixturedef.func) + return _wrap_async_fixture(fixturedef.func, event_loop) else: return fixturedef.func @@ -277,6 +278,7 @@ def _wrap_asyncgen_fixture( fixture_function: Callable[ AsyncGenFixtureParams, AsyncGeneratorType[AsyncGenFixtureYieldType, Any] ], + event_loop: AbstractEventLoop, ) -> Callable[ Concatenate[FixtureRequest, AsyncGenFixtureParams], AsyncGenFixtureYieldType ]: @@ -287,10 +289,6 @@ def _asyncgen_fixture_wrapper( **kwargs: AsyncGenFixtureParams.kwargs, ): func = _perhaps_rebind_fixture_func(fixture_function, request.instance) - event_loop_fixture_id = _get_event_loop_fixture_id_for_async_fixture( - request, func - ) - event_loop = request.getfixturevalue(event_loop_fixture_id) gen_obj = func(*args, **_add_kwargs(func, kwargs, request)) async def setup(): @@ -335,6 +333,7 @@ def _wrap_async_fixture( fixture_function: Callable[ AsyncFixtureParams, CoroutineType[Any, Any, AsyncFixtureReturnType] ], + event_loop: AbstractEventLoop, ) -> Callable[Concatenate[FixtureRequest, AsyncFixtureParams], AsyncFixtureReturnType]: @functools.wraps(fixture_function) # type: ignore[arg-type] @@ -344,10 +343,6 @@ def _async_fixture_wrapper( **kwargs: AsyncFixtureParams.kwargs, ): func = _perhaps_rebind_fixture_func(fixture_function, request.instance) - event_loop_fixture_id = _get_event_loop_fixture_id_for_async_fixture( - request, func - ) - event_loop = request.getfixturevalue(event_loop_fixture_id) async def setup(): res = await func(*args, **_add_kwargs(func, kwargs, request)) @@ -374,18 +369,6 @@ async def setup(): return _async_fixture_wrapper -def _get_event_loop_fixture_id_for_async_fixture( - request: FixtureRequest, func: Any -) -> str: - default_loop_scope = cast( - _ScopeName, request.config.getini("asyncio_default_fixture_loop_scope") - ) - loop_scope = ( - getattr(func, "_loop_scope", None) or default_loop_scope or request.scope - ) - return f"_{loop_scope}_event_loop" - - def _create_task_in_context( loop: asyncio.AbstractEventLoop, coro: AbstractCoroutine[Any, Any, _T], @@ -794,7 +777,9 @@ def pytest_fixture_setup(fixturedef: FixtureDef, request) -> object | None: or default_loop_scope or fixturedef.scope ) - synchronizer = _fixture_synchronizer(fixturedef) + event_loop_fixture_id = f"_{loop_scope}_event_loop" + event_loop = request.getfixturevalue(event_loop_fixture_id) + synchronizer = _fixture_synchronizer(fixturedef, event_loop) _make_asyncio_fixture_function(synchronizer, loop_scope) with MonkeyPatch.context() as c: if "request" not in fixturedef.argnames: