diff --git a/pytest_asyncio/plugin.py b/pytest_asyncio/plugin.py index 28c97cc9..0d9d12c5 100644 --- a/pytest_asyncio/plugin.py +++ b/pytest_asyncio/plugin.py @@ -611,6 +611,19 @@ def pytest_pycollect_makeitem_convert_async_functions_to_subclass( hook_result.force_result(updated_node_collection) +@pytest.hookimpl(trylast=True) +def pytest_collection_modifyitems(items: list[Item]) -> None: + for i, item in enumerate(items): + if ( + isinstance(item, Function) + and not isinstance(item, PytestAsyncioFunction) + and item.get_closest_marker("asyncio") + ): + specialized_item_class = PytestAsyncioFunction.item_subclass_for(item) + if specialized_item_class: + items[i] = specialized_item_class._from_function(item) + + @contextlib.contextmanager def _temporary_event_loop_policy(policy: AbstractEventLoopPolicy) -> Iterator[None]: old_loop_policy = _get_event_loop_policy() diff --git a/tests/test_asyncio_mark.py b/tests/test_asyncio_mark.py index a04240b4..39d25b5c 100644 --- a/tests/test_asyncio_mark.py +++ b/tests/test_asyncio_mark.py @@ -186,3 +186,33 @@ async def test_a(session_loop_fixture): result = pytester.runpytest("--asyncio-mode=auto") result.assert_outcomes(passed=1) + + +def test_asyncio_mark_added_via_collection_modifyitems_is_recognized( + pytester: Pytester, +): + """Regression test for #810. + + When a user plugin adds the asyncio marker via pytest_collection_modifyitems, + pytest-asyncio should recognize the async function and run it correctly instead + of raising "The test is not an async function". + """ + pytester.makeini("[pytest]\nasyncio_default_fixture_loop_scope = function") + pytester.makeconftest( + dedent("""\ + import inspect + + def pytest_collection_modifyitems(items): + for item in items: + if inspect.iscoroutinefunction(getattr(item, "obj", None)): + item.add_marker("asyncio") + """) + ) + pytester.makepyfile( + dedent("""\ + async def test_foo(): + pass + """) + ) + result = pytester.runpytest("--asyncio-mode=strict") + result.assert_outcomes(passed=1)