Skip to content

Commit c1131f8

Browse files
alblascoTinche
authored andcommitted
1) A new test case that fails with 0.12.0, and pass with this commit.
test_async_fixtures_with_finalizer_scope.py: 2) Main problem is due to side effects of asyncio.get_event_loop(). See: https://github.com/python/cpython/blob/3.8/Lib/asyncio/events.py#L636 This method could either return an existing loop or create a new one. This commit replaces all asyncio.get_event_loop() with previous pytest-asyncio code (0.10.0), so plugin uses the loop provided by event_loop fixture (instead of calling asyncio.get_event_loop()) Except following block, that has not been modified in this commit (as it behaves similar to 0.10.0) https://github.com/pytest-dev/pytest-asyncio/blob/v0.12.0/pytest_asyncio/plugin.py#L54-L66 Changes are for using always the new loop provided by event_loop fixture. Instead of calling get_event_loop() that: - either returns global recorded loop (with set_event_loop()) in case there is one, - or otherwise creates and record a new one
1 parent 7a255bc commit c1131f8

File tree

2 files changed

+59
-11
lines changed

2 files changed

+59
-11
lines changed

pytest_asyncio/plugin.py

+21-11
Original file line numberDiff line numberDiff line change
@@ -69,13 +69,20 @@ def pytest_fixture_setup(fixturedef, request):
6969
# This is an async generator function. Wrap it accordingly.
7070
generator = fixturedef.func
7171

72+
strip_event_loop = False
73+
if 'event_loop' not in fixturedef.argnames:
74+
fixturedef.argnames += ('event_loop', )
75+
strip_event_loop = True
7276
strip_request = False
7377
if 'request' not in fixturedef.argnames:
7478
fixturedef.argnames += ('request', )
7579
strip_request = True
7680

7781
def wrapper(*args, **kwargs):
82+
loop = kwargs['event_loop']
7883
request = kwargs['request']
84+
if strip_event_loop:
85+
del kwargs['event_loop']
7986
if strip_request:
8087
del kwargs['request']
8188

@@ -96,21 +103,30 @@ async def async_finalizer():
96103
msg = "Async generator fixture didn't stop."
97104
msg += "Yield only once."
98105
raise ValueError(msg)
99-
asyncio.get_event_loop().run_until_complete(async_finalizer())
106+
loop.run_until_complete(async_finalizer())
100107

101108
request.addfinalizer(finalizer)
102-
return asyncio.get_event_loop().run_until_complete(setup())
109+
return loop.run_until_complete(setup())
103110

104111
fixturedef.func = wrapper
105112
elif inspect.iscoroutinefunction(fixturedef.func):
106113
coro = fixturedef.func
107114

115+
strip_event_loop = False
116+
if 'event_loop' not in fixturedef.argnames:
117+
fixturedef.argnames += ('event_loop', )
118+
strip_event_loop = True
119+
108120
def wrapper(*args, **kwargs):
121+
loop = kwargs['event_loop']
122+
if strip_event_loop:
123+
del kwargs['event_loop']
124+
109125
async def setup():
110126
res = await coro(*args, **kwargs)
111127
return res
112128

113-
return asyncio.get_event_loop().run_until_complete(setup())
129+
return loop.run_until_complete(setup())
114130

115131
fixturedef.func = wrapper
116132
yield
@@ -144,15 +160,9 @@ def wrap_in_sync(func, _loop):
144160
def inner(**kwargs):
145161
coro = func(**kwargs)
146162
if coro is not None:
163+
task = asyncio.ensure_future(coro, loop=_loop)
147164
try:
148-
loop = asyncio.get_event_loop()
149-
except RuntimeError as exc:
150-
if 'no current event loop' not in str(exc):
151-
raise
152-
loop = _loop
153-
task = asyncio.ensure_future(coro, loop=loop)
154-
try:
155-
loop.run_until_complete(task)
165+
_loop.run_until_complete(task)
156166
except BaseException:
157167
# run_until_complete doesn't get the result from exceptions
158168
# that are not subclasses of `Exception`. Consume all
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import asyncio
2+
import contextlib
3+
import functools
4+
import pytest
5+
6+
7+
@pytest.mark.asyncio
8+
async def test_module_scope(port):
9+
await asyncio.sleep(0.01)
10+
assert port
11+
12+
@pytest.fixture(scope="module")
13+
def event_loop():
14+
"""Change event_loop fixture to module level."""
15+
policy = asyncio.get_event_loop_policy()
16+
loop = policy.new_event_loop()
17+
yield loop
18+
loop.close()
19+
20+
21+
@pytest.fixture(scope="module")
22+
async def port(request, event_loop):
23+
def port_finalizer(finalizer):
24+
async def port_afinalizer():
25+
await finalizer(None, None, None)
26+
event_loop.run_until_complete(port_afinalizer())
27+
28+
context_manager = port_map()
29+
port = await context_manager.__aenter__()
30+
request.addfinalizer(functools.partial(port_finalizer, context_manager.__aexit__))
31+
return True
32+
33+
34+
@contextlib.asynccontextmanager
35+
async def port_map():
36+
worker = asyncio.create_task(asyncio.sleep(0.2))
37+
yield
38+
await worker

0 commit comments

Comments
 (0)