From bc97f3731cae48a2e214084116a66fe4c000107b Mon Sep 17 00:00:00 2001 From: Zakir Jiwani <108548454+JiwaniZakir@users.noreply.github.com> Date: Mon, 16 Mar 2026 21:41:59 +0000 Subject: [PATCH 1/2] Fix asyncio marker detection for markers added via pytest_collection_modifyitems Add a trylast pytest_collection_modifyitems hook that converts Function items with a dynamically added asyncio marker to PytestAsyncioFunction. --- pytest_asyncio/plugin.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) 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() From 5129c8e056c628de556c7a9867f689dc3308c3e0 Mon Sep 17 00:00:00 2001 From: Zakir Jiwani Date: Wed, 18 Mar 2026 12:35:41 -0400 Subject: [PATCH 2/2] test: add regression test for issue #810 Add test_asyncio_mark_added_via_collection_modifyitems_is_recognized that fails on main (pytest raises 'The test is not an async function') but passes on this branch after the pytest_collection_modifyitems hook is in place. Reproduces the exact pattern from the issue: a user conftest that uses pytest_collection_modifyitems + inspect.iscoroutinefunction to add the asyncio marker to async test functions in strict mode. --- tests/test_asyncio_mark.py | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) 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)