-
Notifications
You must be signed in to change notification settings - Fork 159
Fix event_loop is run after other fixtures #156
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
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 all Basically 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