diff --git a/pytest_asyncio/plugin.py b/pytest_asyncio/plugin.py index 38b3e28a..4f4dbcee 100644 --- a/pytest_asyncio/plugin.py +++ b/pytest_asyncio/plugin.py @@ -442,8 +442,25 @@ def _can_substitute(item: Function) -> bool: """Returns whether the specified function can be replaced by this class""" raise NotImplementedError() + def setup(self) -> None: + runner_fixture_id = f"_{self._loop_scope}_scoped_runner" + if runner_fixture_id not in self.fixturenames: + self.fixturenames.append(runner_fixture_id) + return super().setup() + + def runtest(self) -> None: + runner_fixture_id = f"_{self._loop_scope}_scoped_runner" + runner = self._request.getfixturevalue(runner_fixture_id) + context = contextvars.copy_context() + synchronized_obj = _synchronize_coroutine( + getattr(*self._synchronization_target_attr), runner, context + ) + with MonkeyPatch.context() as c: + c.setattr(*self._synchronization_target_attr, synchronized_obj) + super().runtest() + @functools.cached_property - def loop_scope(self) -> _ScopeName: + def _loop_scope(self) -> _ScopeName: """ Return the scope of the asyncio event loop this item is run in. @@ -457,17 +474,6 @@ def loop_scope(self) -> _ScopeName: default_loop_scope = _get_default_test_loop_scope(self.config) return _get_marked_loop_scope(marker, default_loop_scope) - def runtest(self) -> None: - runner_fixture_id = f"_{self.loop_scope}_scoped_runner" - runner = self._request.getfixturevalue(runner_fixture_id) - context = contextvars.copy_context() - synchronized_obj = _synchronize_coroutine( - getattr(*self._synchronization_target_attr), runner, context - ) - with MonkeyPatch.context() as c: - c.setattr(*self._synchronization_target_attr, synchronized_obj) - super().runtest() - @property def _synchronization_target_attr(self) -> tuple[object, str]: """ @@ -531,6 +537,16 @@ class AsyncHypothesisTest(PytestAsyncioFunction): @hypothesis.given. """ + def setup(self) -> None: + if not getattr(self.obj, "hypothesis", False) and getattr( + self.obj, "is_hypothesis_test", False + ): + pytest.fail( + f"test function `{self!r}` is using Hypothesis, but pytest-asyncio " + "only works with Hypothesis 3.64.0 or later." + ) + return super().setup() + @staticmethod def _can_substitute(item: Function) -> bool: func = item.obj @@ -693,24 +709,6 @@ def inner(*args, **kwargs): return inner -def pytest_runtest_setup(item: pytest.Item) -> None: - marker = item.get_closest_marker("asyncio") - if marker is None or not is_async_test(item): - return - runner_fixture_id = f"_{item.loop_scope}_scoped_runner" - fixturenames = item.fixturenames # type: ignore[attr-defined] - if runner_fixture_id not in fixturenames: - fixturenames.append(runner_fixture_id) - obj = getattr(item, "obj", None) - if not getattr(obj, "hypothesis", False) and getattr( - obj, "is_hypothesis_test", False - ): - pytest.fail( - f"test function `{item!r}` is using Hypothesis, but pytest-asyncio " - "only works with Hypothesis 3.64.0 or later." - ) - - @pytest.hookimpl(wrapper=True) def pytest_fixture_setup(fixturedef: FixtureDef, request) -> object | None: asyncio_mode = _get_asyncio_mode(request.config)