pytest-dev/pytest-asyncio

Session scoped event loop not actually session scope

theunkn0wn1 opened this issue · 1 comments

I am using a downstream library that requires that coroutines spawned from its objects are run on the same event loop as the library objects were instantiated on.
I intend to instantiate these library objects as async generator fixtures, to make test code super simple.
as such, I set the event loop scope to session expecting only one loop to ever be created per pytest invocation.
However, In practice the fixture is receiving a different event loop object than the test is, resulting in failure.

pytest configuration:

[tool.pytest.ini_options]
asyncio_default_fixture_loop_scope="session"

Minimum reproducible example:

from asyncio import get_running_loop
import pytest
import pytest_asyncio

@pytest.mark.asyncio
@pytest_asyncio.fixture(scope="session", loop_scope="session")
async def fx_debug() -> int:
    # define a fixture that yields the ID if its event loop
    loop = get_running_loop()
    yield id(loop)


@pytest.mark.asyncio
async def test_assert_event_loop_is_equal(fx_debug):
    # assert the ID of our event loop matches that of the fixture
    assert id(get_running_loop()) == fx_debug  

output:

================================================================================================================================================================================================== test session starts ==================================================================================================================================================================================================
platform linux -- Python 3.12.5, pytest-8.3.3, pluggy-1.5.0
rootdir: /home/redacted/projects/redacted
configfile: pyproject.toml
plugins: asyncio-0.24.0
asyncio: mode=Mode.STRICT, default_loop_scope=session
collected 1 item                                                                                                                                                                                                                                                                                                                                                                                                        

tests/test_psu_config.py F                                                                                                                                                                                                                                                                                                                                                                                        [100%]

======================================================================================================================================================================================================= FAILURES ========================================================================================================================================================================================================
____________________________________________________________________________________________________________________________________________________________________________________________ test_assert_event_loop_is_equal ____________________________________________________________________________________________________________________________________________________________________________________________

fx_debug = 137275045990176

    @mark.unit_test
    @mark.asyncio
    async def test_assert_event_loop_is_equal(fx_debug):
>       assert id(get_running_loop()) == fx_debug
E       assert 137275045989840 == 137275045990176
E        +  where 137275045989840 = id(<_UnixSelectorEventLoop running=True closed=False debug=False>)
E        +    where <_UnixSelectorEventLoop running=True closed=False debug=False> = get_running_loop()

The asyncio_default_fixture_loop_scope configuration option only affects async fixtures, but not async tests.
A similar option for async tests is tracked in #793.

That means the fixture runs in a session-scoped loop, whereas the test runs the default function-scoped loop.
Adding the loop_scope="session" keyword argument to @pytest.mark.asyncio when marking the test should solve the issue.

See also: https://pytest-asyncio.readthedocs.io/en/v0.24.0/how-to-guides/run_session_tests_in_same_loop.html

Side note: @pytest.mark.asyncio has no effect on async fixtures and should be omitted.

Let me know if these pointers solve your issue!