I'm not entirely sure how to classify what is happening here, as it is happening in a very particular test setup. I've managed to strip down the tests to the bare minimum to reproduce.
Directory Structure
$ tree
.
├── test_app.py
├── test_example.py
└── tox.ini
import pytest
import asyncio
@pytest.fixture()
async def clear_cache():
"""Test fixture to clear the backend aiocache cache.
The memory cache implementation shares state in a class member, not
an instance member, so we must clear between tests to ensure one test
does not pollute state for another test case.
"""
yield
await asyncio.sleep(0.1)
@pytest.mark.asyncio
@pytest.mark.usefixtures('clear_cache')
class TestSomething:
async def test_something(self):
await asyncio.sleep(0.1)
assert True
Test Output
$ tox
py3 recreate: /Users/edaniszewski/dev/tmp/testing/.tox/py3
py3 installdeps: pytest, pytest-asyncio==0.11.0, sanic
py3 installed: aiofiles==0.5.0,attrs==19.3.0,certifi==2020.4.5.1,chardet==3.0.4,h11==0.8.1,h2==3.2.0,hpack==3.0.0,hstspreload==2020.4.24,httptools==0.1.1,httpx==0.9.3,hyperframe==5.2.0,idna==2.9,importlib-metadata==1.6.0,more-itertools==8.2.0,multidict==4.7.5,packaging==20.3,pluggy==0.13.1,py==1.8.1,pyparsing==2.4.7,pytest==5.4.1,pytest-asyncio==0.11.0,rfc3986==1.4.0,sanic==19.12.2,six==1.14.0,sniffio==1.1.0,ujson==2.0.3,uvloop==0.14.0,wcwidth==0.1.9,websockets==8.1,zipp==3.1.0
py3 run-test-pre: PYTHONHASHSEED='3847369090'
py3 run-test: commands[0] | pytest -s .
==================================================== test session starts ====================================================
platform darwin -- Python 3.7.4, pytest-5.4.1, py-1.8.1, pluggy-0.13.1
cachedir: .tox/py3/.pytest_cache
rootdir: /Users/edaniszewski/dev/tmp/testing
plugins: asyncio-0.11.0
collected 2 items
test_app.py [2020-04-24 11:16:54 -0400] [37301] [INFO] Goin' Fast @ http://127.0.0.1:42101
[2020-04-24 11:16:54 -0400] [37301] [INFO] http://127.0.0.1:42101/
[2020-04-24 11:16:54 -0400] - (sanic.access)[INFO][127.0.0.1:51882]: GET http://127.0.0.1:42101/ 200 15
[2020-04-24 11:16:54 -0400] [37301] [INFO] Starting worker [37301]
[2020-04-24 11:16:54 -0400] [37301] [INFO] Stopping worker [37301]
[2020-04-24 11:16:54 -0400] [37301] [INFO] Server Stopped
.
test_example.py EE
========================================================== ERRORS ===========================================================
______________________________________ ERROR at setup of TestSomething.test_something _______________________________________
args = (), kwargs = {}, request = <SubRequest 'clear_cache' for <Function test_something>>
setup = <function pytest_fixture_setup.<locals>.wrapper.<locals>.setup at 0x10dec3cb0>
finalizer = <function pytest_fixture_setup.<locals>.wrapper.<locals>.finalizer at 0x10dec3d40>
def wrapper(*args, **kwargs):
request = kwargs['request']
if strip_request:
del kwargs['request']
gen_obj = generator(*args, **kwargs)
async def setup():
res = await gen_obj.__anext__()
return res
def finalizer():
"""Yield again, to finalize."""
async def async_finalizer():
try:
await gen_obj.__anext__()
except StopAsyncIteration:
pass
else:
msg = "Async generator fixture didn't stop."
msg += "Yield only once."
raise ValueError(msg)
asyncio.get_event_loop().run_until_complete(async_finalizer())
request.addfinalizer(finalizer)
> return asyncio.get_event_loop().run_until_complete(setup())
.tox/py3/lib/python3.7/site-packages/pytest_asyncio/plugin.py:102:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
uvloop/loop.pyx:1430: in uvloop.loop.Loop.run_until_complete
???
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
> ???
E RuntimeError: Event loop is closed
uvloop/loop.pyx:668: RuntimeError
_____________________________________ ERROR at teardown of TestSomething.test_something _____________________________________
def finalizer():
"""Yield again, to finalize."""
async def async_finalizer():
try:
await gen_obj.__anext__()
except StopAsyncIteration:
pass
else:
msg = "Async generator fixture didn't stop."
msg += "Yield only once."
raise ValueError(msg)
> asyncio.get_event_loop().run_until_complete(async_finalizer())
.tox/py3/lib/python3.7/site-packages/pytest_asyncio/plugin.py:99:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
uvloop/loop.pyx:1430: in uvloop.loop.Loop.run_until_complete
???
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
> ???
E RuntimeError: Event loop is closed
uvloop/loop.pyx:668: RuntimeError
===================================================== warnings summary ======================================================
test_app.py:5
/Users/edaniszewski/dev/tmp/testing/test_app.py:5: DeprecationWarning: Sanic(name=None) is deprecated and None value support for `name` will be removed in the next release. Please use Sanic(name='your_application_name') instead.
app = sanic.Sanic()
test_app.py::test_app
/Users/edaniszewski/dev/tmp/testing/.tox/py3/lib/python3.7/site-packages/httpx/client.py:234: UserWarning: Passing a 'verify' argument when making a request on a client is due to be deprecated. Instantiate a new client instead, passing any 'verify' arguments to the client itself.
"Passing a 'verify' argument when making a request on a client "
test_example.py::TestSomething::test_something
/Users/edaniszewski/dev/tmp/testing/.tox/py3/lib/python3.7/site-packages/pytest_asyncio/plugin.py:102: RuntimeWarning: coroutine 'pytest_fixture_setup.<locals>.wrapper.<locals>.setup' was never awaited
return asyncio.get_event_loop().run_until_complete(setup())
test_example.py::TestSomething::test_something
/Users/edaniszewski/dev/tmp/testing/.tox/py3/lib/python3.7/site-packages/pytest_asyncio/plugin.py:99: RuntimeWarning: coroutine 'pytest_fixture_setup.<locals>.wrapper.<locals>.finalizer.<locals>.async_finalizer' was never awaited
asyncio.get_event_loop().run_until_complete(async_finalizer())
-- Docs: https://docs.pytest.org/en/latest/warnings.html
================================================== short test summary info ==================================================
ERROR test_example.py::TestSomething::test_something - RuntimeError: Event loop is closed
ERROR test_example.py::TestSomething::test_something - RuntimeError: Event loop is closed
========================================== 1 passed, 4 warnings, 2 errors in 1.10s ==========================================
ERROR: InvocationError for command /Users/edaniszewski/dev/tmp/testing/.tox/py3/bin/pytest -s . (exited with code 1)
__________________________________________________________ summary __________________________________________________________
ERROR: py3: commands failed
Notes
it appears that having the sanic tests (test_app.py) run prior to the example test file is required for this error happen. renaming the file to test_zapp.py so it runs after test_example.py mitigates the issue.
within the sanic test file, it appears that it is important to use the app test_client. I'm not sure if other things will produce a similar effect, but based on my specific use case, commenting out tests which used the test_app appeared to mitigate the problem.
it appears to be important that the @pytest.mark.usefixtures decorator is used, and that the fixture is an async function.
the order in which the decorators (@pytest.mark.asyncio, @pytest.mark.usefixtures) does not appear to matter
downgrading to pytest-asyncio==0.10.0 mitigates the issue as well, so this seems to be related to something that changed with the 0.11.0 release.
I've tested with py3.6, py3.7, and py3.8 and had the same results for each version
I'm not entirely sure how to classify what is happening here, as it is happening in a very particular test setup. I've managed to strip down the tests to the bare minimum to reproduce.
Directory Structure
File Contents
tox.ini
test_app.py
test_example.py
Test Output
Notes
test_app.py
) run prior to the example test file is required for this error happen. renaming the file totest_zapp.py
so it runs aftertest_example.py
mitigates the issue.test_client
. I'm not sure if other things will produce a similar effect, but based on my specific use case, commenting out tests which used thetest_app
appeared to mitigate the problem.@pytest.mark.usefixtures
decorator is used, and that the fixture is an async function.@pytest.mark.asyncio
,@pytest.mark.usefixtures
) does not appear to matterpytest-asyncio==0.10.0
mitigates the issue as well, so this seems to be related to something that changed with the0.11.0
release.