Skip to content

Async fixtures request wrong event loop with 0.23.0a0 #670

Closed
@2e0byo

Description

@2e0byo

Interesting things seem to happen with the current logic in 0.23.0a when collecting async fixtures.

Running a venv with almost nothing installed:

# requirements.txt
pytest
pytest-asyncio==0.23.0a0
# pyproject.toml
[tool.pytest.ini_options]
asyncio_mode = "auto"

A single dir tests contains an __init__.py and the test_async_fixtures.py file; pytest is invoked as pytest tests/

This fails:

# tests/test_async_fixture.py
import asyncio

import pytest

@pytest.fixture(scope="package")
def event_loop_policy() -> asyncio.AbstractEventLoopPolicy:
    return asyncio.DefaultEventLoopPolicy()


@pytest.fixture(scope="package")
async def async_fixture():
    yield 7


async def test_async_fixture(async_fixture):
    assert async_fixture == 7

Complaining:

ScopeMismatch: You tried to access the function scoped fixture event_loop with a package scoped request object, involved factories:
tests/test_fixture.py:13:  def async_fixture()

Hmm. As a guess, is async_fixture implicitly requesting the wrong event loop? What happens if I mark it?

@pytest.fixture(scope="package")
@pytest.mark.asyncio(scope="package")
async def async_fixture():
    yield 7

No, the same as before (I have no idea if one is supposed to be able to mark fixtures like this).

What about marking the whole module explicitly?

import asyncio

import pytest

pytestmark = pytest.mark.asyncio(scope="package")
...

Ah! now we get a more interesting error:

file /tmp/t/tests/test_fixture.py, line 20
  async def test_async_fixture(async_fixture):
      assert async_fixture == 7
E       fixture 'tests/__init__.py::<event_loop>' not found
>       available fixtures: _session_event_loop, async_fixture, cache, capfd, capfdbinary, caplog, capsys, capsysbinary, doctest_namespace, event_loop, event_loop_policy, monkeypatch, pytestconfig, record_property, record_testsuite_property, record_xml_attribute, recwarn, tests/test_fixture.py::<event_loop>, tmp_path, tmp_path_factory, tmpdir, tmpdir_factory, unused_tcp_port, unused_tcp_port_factory, unused_udp_port, unused_udp_port_factory
>       use 'pytest --fixtures [testpath]' for help on them.

What about marking the test function with an explicit scope?

@pytest.mark.asyncio(scope="package")
async def test_async_fixture(async_fixture):
    assert async_fixture == 7

This raises the new 'Multiple asyncio event loops with different scopes' error.

Traceback
.venv/lib/python3.11/site-packages/_pytest/runner.py:341: in from_call
    result: Optional[TResult] = func()
.venv/lib/python3.11/site-packages/_pytest/runner.py:372: in <lambda>
    call = CallInfo.from_call(lambda: list(collector.collect()), "collect")
.venv/lib/python3.11/site-packages/_pytest/python.py:534: in collect
    return super().collect()
.venv/lib/python3.11/site-packages/_pytest/python.py:455: in collect
    res = ihook.pytest_pycollect_makeitem(
.venv/lib/python3.11/site-packages/pluggy/_hooks.py:493: in __call__
    return self._hookexec(self.name, self._hookimpls, kwargs, firstresult)
.venv/lib/python3.11/site-packages/pluggy/_manager.py:115: in _hookexec
    return self._inner_hookexec(hook_name, methods, kwargs, firstresult)
.venv/lib/python3.11/site-packages/pytest_asyncio/plugin.py:525: in pytest_pycollect_makeitem_convert_async_functions_to_subclass
    node_or_list_of_nodes = hook_result.get_result()
.venv/lib/python3.11/site-packages/_pytest/python.py:271: in pytest_pycollect_makeitem
    return list(collector._genfunctions(name, obj))
.venv/lib/python3.11/site-packages/_pytest/python.py:498: in _genfunctions
    self.ihook.pytest_generate_tests.call_extra(methods, dict(metafunc=metafunc))
.venv/lib/python3.11/site-packages/pluggy/_hooks.py:552: in call_extra
    return self._hookexec(self.name, hookimpls, kwargs, firstresult)
.venv/lib/python3.11/site-packages/pluggy/_manager.py:115: in _hookexec
    return self._inner_hookexec(hook_name, methods, kwargs, firstresult)
.venv/lib/python3.11/site-packages/pytest_asyncio/plugin.py:674: in pytest_generate_tests
    raise MultipleEventLoopsRequestedError(
E   pytest_asyncio.plugin.MultipleEventLoopsRequestedError: Multiple asyncio event loops with different scopes have been requested
E   by tests/test_fixture.py::test_async_fixture. The test explicitly requests the event_loop fixture, while
E   another event loop with package scope is provided by tests/__init__.py.
E   Remove "event_loop" from the requested fixture in your test to run the test
E   in a package-scoped event loop or remove the scope argument from the "asyncio"
E   mark to run the test in a function-scoped event loop.

Assuming I'm using the new code correctly, it looks like there's a problem with async fixtures and policies. At any rate here's a failing test case.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions