Fix event_loop is run after other fixtures - #156
Conversation
|
I did something that seems to fix the test, however I'm really not sure the behaviour is wanted: diff --git a/pytest_asyncio/plugin.py b/pytest_asyncio/plugin.py
index 9860e04..c0b65da 100644
--- a/pytest_asyncio/plugin.py
+++ b/pytest_asyncio/plugin.py
@@ -125,14 +125,18 @@ def pytest_pyfunc_call(pyfuncitem):
if 'asyncio' in pyfuncitem.keywords:
if getattr(pyfuncitem.obj, 'is_hypothesis_test', False):
pyfuncitem.obj.hypothesis.inner_test = wrap_in_sync(
- pyfuncitem.obj.hypothesis.inner_test
+ pyfuncitem.obj.hypothesis.inner_test,
+ _loop=pyfuncitem.funcargs['event_loop']
)
else:
- pyfuncitem.obj = wrap_in_sync(pyfuncitem.obj)
+ pyfuncitem.obj = wrap_in_sync(
+ pyfuncitem.obj,
+ _loop=pyfuncitem.funcargs['event_loop']
+ )
yield
-def wrap_in_sync(func):
+def wrap_in_sync(func, _loop):
"""Return a sync wrapper around an async function executing it in the
current event loop."""
@@ -140,9 +144,15 @@ def wrap_in_sync(func):
def inner(**kwargs):
coro = func(**kwargs)
if coro is not None:
- task = asyncio.ensure_future(coro)
try:
- asyncio.get_event_loop().run_until_complete(task)
+ loop = asyncio.get_event_loop()
+ except RuntimeError as exc:
+ if 'no current event loop' not in str(exc):
+ raise
+ loop = _loop
+ task = asyncio.ensure_future(coro, loop=loop)
+ try:
+ loop.run_until_complete(task)
except BaseException:
# run_until_complete doesn't get the result from exceptions
# that are not subclasses of `Exception`. Consume allBasically the wrapper will attach the loop from the |
|
Hi, I think the behavior is fine (second time that I look to the source of pytest-asyncio s don't take my word for it :D), but I would take it up a notch to "An asynchronous test function should run on the event loop that was used for its fixtures". So I tried the following changes, and tests continue to pass but can't keep thinking that is probably too easy :). diff --git a/pytest_asyncio/plugin.py b/pytest_asyncio/plugin.py
index 9860e04..08cf9d2 100644
--- a/pytest_asyncio/plugin.py
+++ b/pytest_asyncio/plugin.py
@@ -125,14 +125,18 @@ def pytest_pyfunc_call(pyfuncitem):
if 'asyncio' in pyfuncitem.keywords:
if getattr(pyfuncitem.obj, 'is_hypothesis_test', False):
pyfuncitem.obj.hypothesis.inner_test = wrap_in_sync(
- pyfuncitem.obj.hypothesis.inner_test
+ pyfuncitem.obj.hypothesis.inner_test,
+ _loop=pyfuncitem.funcargs['event_loop'],
)
else:
- pyfuncitem.obj = wrap_in_sync(pyfuncitem.obj)
+ pyfuncitem.obj = wrap_in_sync(
+ pyfuncitem.obj,
+ _loop=pyfuncitem.funcargs['event_loop'],
+ )
yield
-def wrap_in_sync(func):
+def wrap_in_sync(func, _loop):
"""Return a sync wrapper around an async function executing it in the
current event loop."""
@@ -140,9 +144,9 @@ def wrap_in_sync(func):
def inner(**kwargs):
coro = func(**kwargs)
if coro is not None:
- task = asyncio.ensure_future(coro)
+ task = asyncio.ensure_future(coro, loop=_loop)
try:
- asyncio.get_event_loop().run_until_complete(task)
+ _loop.run_until_complete(task)
except BaseException:
# run_until_complete doesn't get the result from exceptions
# that are not subclasses of `Exception`. Consume all |
|
@rsebille The behavior and your comment differ I think: |
|
@rsebille I forgot to add that I appreciate that you took time to look into this and any future support :) |
|
I totally assume (slaps ensues) that asynchronous fixtures were explicitly run in the @simonfagerholm No problem, thank to you for taking the time on this. |
|
Thanks for this, I will look at this over the weekend! |
|
@Tinche Thats great! In my digging I found out that the test is supposed to test for problems with pytest-aiohttp.
As it fails on pytest-asyncio==0.11 and passes on this PR, it seems the unittest did not protect against that problem. import pytest
from aiohttp import web
async def hello(request):
return web.Response(body=b'Hello, world')
def create_app(loop):
app = web.Application(loop=loop)
app.router.add_route('GET', '/', hello)
return app
async def test_hello(aiohttp_client):
client = await aiohttp_client(create_app)
resp = await client.get('/')
assert resp.status == 200
text = await resp.text()
assert 'Hello, world' in text
@pytest.fixture
async def async_fixture():
yield 'Hi from async_fixture()!'
@pytest.mark.asyncio
async def test_async_fixture_fixed(async_fixture):
assert async_fixture == 'Hi from async_fixture()!' |
|
Yeah, I guess that unittest from #64 was useless. On @simonfagerholm's branch, and applied the linked diff (which I'm ok with). So I tried installing If you could add a changelog entry, I'll release this, with thanks :) |
|
@Tinche Pushed a commit of my diff above. Thanks for your help in the investigation, do you have any ideas on how to test the compatibility with aiohttp? I somewhat dislike the inclusion of aiohttp in the tests, but it might not be possible in other ways. |
|
Actually I don't really have a good idea. It would be ideal if they started depending on us, since I feel they are a higher level component... |
|
Thanks a lot @simonfagerholm ! |
Resolves #154, resolves #157, resolves #158
Also adds unittests for the use case.
Not ready for merge, breaks
test_asyncio_marker_without_loop, which was added in #64.pytest-asyncio/tests/test_simple.py
Lines 116 to 128 in a7e5795
Discussion for possible fix of broken test below