pytest-asyncio: Dynamically calling async fixture causes a runtime error saying "This event loop is already running"

There seems to be a bug with how request.getfixturevalue(argname) interacts with pytest-asyncio. Calling the function leads to a runtime error saying the event loop is already running. If you change it from being dynamically called to being fixed in the function definition, it works as expected.

Platform Info:

platform win32 -- Python 3.7.2, pytest-4.2.0, py-1.7.0, pluggy-0.8.1
plugins: asyncio-0.11.0.dev0

Test fixture that can be used in both dynamic/fixed cases:

@pytest.fixture
async def async_fixture():
    yield 'Hi from async_fixture()!'

Successful fixed function argument fixture test:

@pytest.mark.asyncio
async def test_async_fixture_fixed(async_fixture):
    assert async_fixture == 'Hi from async_fixture()!'

Failed dynamic fixture test:

@pytest.mark.asyncio
async def test_async_fixture_dynamic(request):
    async_fixture = request.getfixturevalue('async_fixture')
    assert async_fixture == 'Hi from async_fixture()!'

Failed test trace-back:

================================== FAILURES ===================================
_________________________ test_async_fixture_dynamic __________________________

request = <FixtureRequest for <Function test_async_fixture_dynamic>>

    @pytest.mark.asyncio
    async def test_async_fixture_dynamic(request):
>       async_fixture = request.getfixturevalue('async_fixture')

tests\test_app_factory.py:52: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
venv\lib\site-packages\_pytest\fixtures.py:478: in getfixturevalue
    return self._get_active_fixturedef(argname).cached_result[0]
venv\lib\site-packages\_pytest\fixtures.py:501: in _get_active_fixturedef
    self._compute_fixture_value(fixturedef)
venv\lib\site-packages\_pytest\fixtures.py:586: in _compute_fixture_value
    fixturedef.execute(request=subrequest)
venv\lib\site-packages\_pytest\fixtures.py:881: in execute
    return hook.pytest_fixture_setup(fixturedef=self, request=request)
venv\lib\site-packages\pluggy\hooks.py:284: in __call__
    return self._hookexec(self, self.get_hookimpls(), kwargs)
venv\lib\site-packages\pluggy\manager.py:68: in _hookexec
    return self._inner_hookexec(hook, methods, kwargs)
venv\lib\site-packages\pluggy\manager.py:62: in <lambda>
    firstresult=hook.spec.opts.get("firstresult") if hook.spec else False,
venv\lib\site-packages\_pytest\fixtures.py:923: in pytest_fixture_setup
    result = call_fixture_func(fixturefunc, request, kwargs)
venv\lib\site-packages\_pytest\fixtures.py:782: in call_fixture_func
    res = fixturefunc(**kwargs)
..\pytest-asyncio\pytest_asyncio\plugin.py:97: in wrapper
    return loop.run_until_complete(setup())
C:\Users\ykuzm\AppData\Local\Programs\Python\Python37\lib\asyncio\base_events.py:571: in run_until_complete
    self.run_forever()
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <_WindowsSelectorEventLoop running=False closed=False debug=False>

    def run_forever(self):
        """Run until stop() is called."""
        self._check_closed()
        if self.is_running():
>           raise RuntimeError('This event loop is already running')
E           RuntimeError: This event loop is already running

About this issue

  • Original URL
  • State: open
  • Created 5 years ago
  • Reactions: 21
  • Comments: 16 (3 by maintainers)

Commits related to this issue

Most upvoted comments

I made a simple example

plugins: anyio-3.6.1, asyncio-0.18.3 asyncio: mode=auto

test_test.py
import pytest


@pytest.fixture
async def test_value():
    return 'test'


async def test_value_is_test(request: pytest.FixtureRequest):
    tv = request.getfixturevalue('test_value')
    assert tv == 'test'
Run output
pytest -k tests/test_test.py 
======================================================================================== test session starts ========================================================================================
platform linux -- Python 3.10.4, pytest-7.1.2, pluggy-1.0.0
rootdir: /workspaces/haven, configfile: pyproject.toml, testpaths: tests
plugins: anyio-3.6.1, asyncio-0.18.3
asyncio: mode=auto
collected 12 items / 11 deselected / 1 selected                                                                                                                                                     

tests/test_test.py F                                                                                                                                                                          [100%]

============================================================================================= FAILURES ==============================================================================================
________________________________________________________________________________________ test_value_is_test _________________________________________________________________________________________

request = <FixtureRequest for <Function test_value_is_test>>

    async def test_value_is_test(request: pytest.FixtureRequest):
>       tv = request.getfixturevalue('test_value')

tests/test_test.py:10: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
/home/vscode/.local/share/pdm/venvs/haven-eEiRBr5u-3.10/lib/python3.10/site-packages/_pytest/fixtures.py:554: in getfixturevalue
    fixturedef = self._get_active_fixturedef(argname)
/home/vscode/.local/share/pdm/venvs/haven-eEiRBr5u-3.10/lib/python3.10/site-packages/_pytest/fixtures.py:573: in _get_active_fixturedef
    self._compute_fixture_value(fixturedef)
/home/vscode/.local/share/pdm/venvs/haven-eEiRBr5u-3.10/lib/python3.10/site-packages/_pytest/fixtures.py:659: in _compute_fixture_value
    fixturedef.execute(request=subrequest)
/home/vscode/.local/share/pdm/venvs/haven-eEiRBr5u-3.10/lib/python3.10/site-packages/_pytest/fixtures.py:1057: in execute
    result = ihook.pytest_fixture_setup(fixturedef=self, request=request)
/home/vscode/.local/share/pdm/venvs/haven-eEiRBr5u-3.10/lib/python3.10/site-packages/pluggy/_hooks.py:265: in __call__
    return self._hookexec(self.name, self.get_hookimpls(), kwargs, firstresult)
/home/vscode/.local/share/pdm/venvs/haven-eEiRBr5u-3.10/lib/python3.10/site-packages/pluggy/_manager.py:80: in _hookexec
    return self._inner_hookexec(hook_name, methods, kwargs, firstresult)
/home/vscode/.local/share/pdm/venvs/haven-eEiRBr5u-3.10/lib/python3.10/site-packages/_pytest/fixtures.py:1111: in pytest_fixture_setup
    result = call_fixture_func(fixturefunc, request, kwargs)
/home/vscode/.local/share/pdm/venvs/haven-eEiRBr5u-3.10/lib/python3.10/site-packages/_pytest/fixtures.py:890: in call_fixture_func
    fixture_result = fixturefunc(**kwargs)
/home/vscode/.local/share/pdm/venvs/haven-eEiRBr5u-3.10/lib/python3.10/site-packages/pytest_asyncio/plugin.py:309: in _async_fixture_wrapper
    return event_loop.run_until_complete(setup())
/usr/local/lib/python3.10/asyncio/base_events.py:622: in run_until_complete
    self._check_running()
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <_UnixSelectorEventLoop running=False closed=False debug=False>

    def _check_running(self):
        if self.is_running():
>           raise RuntimeError('This event loop is already running')
E           RuntimeError: This event loop is already running

/usr/local/lib/python3.10/asyncio/base_events.py:582: RuntimeError
====================================================================================== short test summary info ======================================================================================
FAILED tests/test_test.py::test_value_is_test - RuntimeError: This event loop is already running
================================================================================= 1 failed, 11 deselected in 0.25s ==================================================================================
sys:1: RuntimeWarning: coroutine '_wrap_async.<locals>._async_fixture_wrapper.<locals>.setup' was never awaited

@seifertm

I solved this problem by adding nest_asyncio.apply() on the top on contest.py:


import nest_asyncio
nest_asyncio.apply()

In our case it worked by simply making the dynamic fixture sync:

def test_async_fixture_dynamic(request, event_loop):
    async_fixture = request.getfixturevalue('async_fixture')
    assert async_fixture == 'Hi from async_fixture()!'

any updates on this?

Any news? 😢