Skip to content

Commit f35c2c3

Browse files
committed
Sync wrapper for hypothesis tests uses the event loop provided by pytest-asyncio instead of creating a new loop.
1 parent f35d06b commit f35c2c3

File tree

1 file changed

+20
-20
lines changed

1 file changed

+20
-20
lines changed

pytest_asyncio/plugin.py

+20-20
Original file line numberDiff line numberDiff line change
@@ -145,10 +145,17 @@ def pytest_pyfunc_call(pyfuncitem):
145145
function call.
146146
"""
147147
for marker_name, fixture_name in _markers_2_fixtures.items():
148-
if marker_name in pyfuncitem.keywords \
149-
and not getattr(pyfuncitem.obj, 'is_hypothesis_test', False):
148+
if marker_name in pyfuncitem.keywords:
150149
event_loop = pyfuncitem.funcargs[fixture_name]
151-
150+
if getattr(pyfuncitem.obj, 'is_hypothesis_test', False):
151+
# Wrap the async function to execute in the pytest-asyncio event loop and yield to delegate
152+
# test execution to the hypothesis plugin
153+
pyfuncitem.obj.hypothesis.inner_test = wrap_in_sync(
154+
pyfuncitem.obj.hypothesis.inner_test, event_loop
155+
)
156+
# It is important to yield, not return. Otherwise the event_loop fixture will get cleaned up too early
157+
# when using a combination of hypothesis and pytest.mark.parametrize
158+
yield None
152159
funcargs = pyfuncitem.funcargs
153160
testargs = {arg: funcargs[arg]
154161
for arg in pyfuncitem._fixtureinfo.argnames}
@@ -159,19 +166,16 @@ def pytest_pyfunc_call(pyfuncitem):
159166
return True
160167

161168

162-
def wrap_in_sync(func):
163-
"""Return a sync wrapper around an async function."""
169+
def wrap_in_sync(func, loop):
170+
"""Return a sync wrapper around an async function executing it in the
171+
specified event loop."""
164172

165173
@functools.wraps(func)
166174
def inner(**kwargs):
167-
loop = asyncio.get_event_loop_policy().new_event_loop()
168-
try:
169-
coro = func(**kwargs)
170-
if coro is not None:
171-
future = asyncio.ensure_future(coro, loop=loop)
172-
loop.run_until_complete(future)
173-
finally:
174-
loop.close()
175+
coro = func(**kwargs)
176+
if coro is not None:
177+
future = asyncio.ensure_future(coro, loop=loop)
178+
loop.run_until_complete(future)
175179

176180
return inner
177181

@@ -181,13 +185,9 @@ def pytest_runtest_setup(item):
181185
if marker in item.keywords and fixture not in item.fixturenames:
182186
# inject an event loop fixture for all async tests
183187
item.fixturenames.append(fixture)
184-
if item.get_closest_marker("asyncio") is not None:
185-
if hasattr(item.obj, 'hypothesis'):
186-
# If it's a Hypothesis test, we insert the wrap_in_sync decorator
187-
item.obj.hypothesis.inner_test = wrap_in_sync(
188-
item.obj.hypothesis.inner_test
189-
)
190-
elif getattr(item.obj, 'is_hypothesis_test', False):
188+
if item.get_closest_marker("asyncio") is not None \
189+
and not getattr(item.obj, 'hypothesis', False) \
190+
and getattr(item.obj, 'is_hypothesis_test', False):
191191
pytest.fail(
192192
'test function `%r` is using Hypothesis, but pytest-asyncio '
193193
'only works with Hypothesis 3.64.0 or later.' % item

0 commit comments

Comments
 (0)