-
Notifications
You must be signed in to change notification settings - Fork 152
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Dynamically calling async fixture causes a runtime error saying "This event loop is already running" #112
Comments
I'd be happy to put in a pull request. I mostly want to use this space for ideas, because I've tried everything I can think of. Ideas welcome! I'll put my couple attempts below. |
My thinking is that it fails because plugin.py calls Causes infinite loop:
Causes
Another infinite loop:
|
Funny enough I got one workaround to work while I was going back through the iPython thread:
Will start working on a proper pull request. |
…time error' by failing over to a ThreadPoolExecutor in cases where the expected event loop is already running. Notably this happens when calling because it dynamically calls a fixture that needs to be setup on an event loop that's already running pytest async tests
…time error' by failing over to a ThreadPoolExecutor in cases where the expected event loop is already running. Notably this happens when calling 'request.getfixturevalue(argname)' because it dynamically calls a fixture that needs to be setup on an event loop that's already running pytest async tests
…time error' by failing over to a ThreadPoolExecutor in cases where the expected event loop is already running. Notably this happens when calling 'request.getfixturevalue(argname)' because it dynamically calls a fixture that needs to be setup on an event loop that's already running pytest async tests
Any news? 😢 |
any updates on this? |
Sorry this doesn't help solve this for pytest-asyncio, but I struggled with this for a while, including trying custom fixtures with module scope and def run_aync_main(*args, **kwargs):
main_loop = asyncio.new_event_loop()
try:
main_loop.run_until_complete(any_async_entry_point(*args, *kwargs))
finally:
main_loop.stop()
main_loop.close() Any non-async function that calls a |
In our case it worked by simply making the dynamic fixture sync:
|
Got this error when I tried to upgrade to 0.14.0 from 0.10.0. Rolled back for now. |
Got this error in 0.15.1 also, any update ? |
got this error in 0.18.3 |
As of v0.18.3 this error could be caused by an unexpected interaction with other pytest plugins that manipulate the event loop. @ReznikovRoman Can you provide a reproducible example? |
I got the same issue today. Code@fixture
async def client():
async with httpx.AsyncClient() as http_client:
yield HavenClient(
http_client,
TEST_APIKEY,
)
@fixture
async def client_without_apikey():
async with httpx.AsyncClient() as http_client:
yield HavenClient(
http_client,
)
@pytest.mark.parametrize('client_fixture,expectation', [
('client', do_not_raise()),
('client_without_apikey', pytest.raises(errors.ApikeyNotSetError)),
])
async def test_get_user_settings(client_fixture, expectation, request: pytest.FixtureRequest):
client: HavenClient = request.getfixturevalue(client_fixture)
with expectation:
settings = await client.get_user_settings()
assert settings @pytest.mark.parametrize('client_fixture,expectation', [
('client', do_not_raise()),
('client_without_apikey', pytest.raises(errors.ApikeyNotSetError)),
])
async def test_get_user_settings(client_fixture, expectation, request: pytest.FixtureRequest):
> client: HavenClient = request.getfixturevalue(client_fixture) 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 Full outputpytest -k test_get_user_settings
======================================================================================== 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 11 items / 9 deselected / 2 selected
tests/test_client.py FF [100%]
============================================================================================= FAILURES ==============================================================================================
____________________________________________________________________________ test_get_user_settings[client-expectation0] ____________________________________________________________________________
client_fixture = 'client', expectation = <contextlib.suppress object at 0x7f33c81ee9b0>, request = <FixtureRequest for <Function test_get_user_settings[client-expectation0]>>
@pytest.mark.parametrize('client_fixture,expectation', [
('client', do_not_raise()),
('client_without_apikey', pytest.raises(errors.ApikeyNotSetError)),
])
async def test_get_user_settings(client_fixture, expectation, request: pytest.FixtureRequest):
> client: HavenClient = request.getfixturevalue(client_fixture)
tests/test_client.py:80:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
/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:293: in _asyncgen_fixture_wrapper
result = 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
____________________________________________________________________ test_get_user_settings[client_without_apikey-expectation1] _____________________________________________________________________
client_fixture = 'client_without_apikey', expectation = <_pytest.python_api.RaisesContext object at 0x7f33c81ee950>
request = <FixtureRequest for <Function test_get_user_settings[client_without_apikey-expectation1]>>
@pytest.mark.parametrize('client_fixture,expectation', [
('client', do_not_raise()),
('client_without_apikey', pytest.raises(errors.ApikeyNotSetError)),
])
async def test_get_user_settings(client_fixture, expectation, request: pytest.FixtureRequest):
> client: HavenClient = request.getfixturevalue(client_fixture)
tests/test_client.py:80:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
/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:293: in _asyncgen_fixture_wrapper
result = 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_client.py::test_get_user_settings[client-expectation0] - RuntimeError: This event loop is already running
FAILED tests/test_client.py::test_get_user_settings[client_without_apikey-expectation1] - RuntimeError: This event loop is already running
================================================================================== 2 failed, 9 deselected in 0.38s ==================================================================================
sys:1: RuntimeWarning: coroutine '_wrap_asyncgen.<locals>._asyncgen_fixture_wrapper.<locals>.setup' was never awaited
RuntimeWarning: Enable tracemalloc to get the object allocation traceback I'll try to get a clean example little bit later. |
I made a simple example
test_test.pyimport 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 outputpytest -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 |
Thanks for the example @suharnikov. I managed to reproduce the error. When a fixture is requested dynamically, it is looked up in pytest's fixture cache first. If it cannot be found in the cache pytest evaluates the fixture function. Since the async fixture coroutine has a synchronous wrapper around it that calls |
A fix would require that the fixture wrapper can decide dynamically whether it is run asynchronously in an event loop or synchronously. However, the fixture wrapper itself needs to be synchronous, because that's what pytest expects. If an event loop is already running, the fixture wrapper needs to submit a task to the event loop and block execution until that task has finished. I'm not aware of a way to await a task from a synchronous function. With the current state of pytest-asyncio, I don't see how this bug can be solved. Suggestions are welcome. #235 would probably solve this issue. |
I solved this problem by adding nest_asyncio.apply() on the top on contest.py: import nest_asyncio
nest_asyncio.apply() |
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:
Test fixture that can be used in both dynamic/fixed cases:
Successful fixed function argument fixture test:
Failed dynamic fixture test:
Failed test trace-back:
The text was updated successfully, but these errors were encountered: