Skip to content

Commit 346ff55

Browse files
committed
Support async/await fixtures
Redo of pytest-dev#35 but straight off of master
1 parent 4f5f656 commit 346ff55

File tree

2 files changed

+69
-4
lines changed

2 files changed

+69
-4
lines changed

pytest_twisted.py

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,13 @@
11
import inspect
2+
import sys
3+
4+
ASYNC_AWAIT = sys.version_info >= (3, 5)
5+
6+
if ASYNC_AWAIT:
7+
import asyncio
8+
else:
9+
asyncio = None
10+
211

312
import decorator
413
import greenlet
@@ -83,28 +92,48 @@ def stop_twisted_greenlet():
8392
_instances.gr_twisted.switch()
8493

8594

95+
def is_coroutine(maybe_coroutine):
96+
if ASYNC_AWAIT:
97+
return asyncio.iscoroutine(maybe_coroutine)
98+
99+
return False
100+
101+
102+
@defer.inlineCallbacks
86103
def _pytest_pyfunc_call(pyfuncitem):
87104
testfunction = pyfuncitem.obj
88105
if pyfuncitem._isyieldedfunction():
89-
return testfunction(*pyfuncitem._args)
106+
defer.returnValue(testfunction(*pyfuncitem._args))
90107
else:
91108
funcargs = pyfuncitem.funcargs
92109
if hasattr(pyfuncitem, "_fixtureinfo"):
93110
testargs = {}
94111
for arg in pyfuncitem._fixtureinfo.argnames:
95-
testargs[arg] = funcargs[arg]
112+
maybe_coroutine = funcargs[arg]
113+
if is_coroutine(maybe_coroutine):
114+
maybe_coroutine = yield defer.ensureDeferred(
115+
maybe_coroutine,
116+
)
117+
testargs[arg] = maybe_coroutine
96118
else:
97119
testargs = funcargs
98-
return testfunction(**testargs)
120+
result = yield testfunction(**testargs)
121+
defer.returnValue(result)
99122

100123

101124
def pytest_pyfunc_call(pyfuncitem):
102125
if _instances.gr_twisted is not None:
103126
if _instances.gr_twisted.dead:
104127
raise RuntimeError("twisted reactor has stopped")
105128

129+
@defer.inlineCallbacks
106130
def in_reactor(d, f, *args):
107-
return defer.maybeDeferred(f, *args).chainDeferred(d)
131+
try:
132+
result = yield f(*args)
133+
except Exception as e:
134+
d.callback(failure.Failure(e))
135+
else:
136+
d.callback(result)
108137

109138
d = defer.Deferred()
110139
_instances.reactor.callLater(

testing/test_basic.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33

44
import pytest
55

6+
import pytest_twisted
7+
68

79
def assert_outcomes(run_result, outcomes):
810
formatted_output = format_run_result_output_for_assert(run_result)
@@ -37,6 +39,13 @@ def skip_if_reactor_not(expected_reactor):
3739
)
3840

3941

42+
def skip_if_no_async_await():
43+
return pytest.mark.skipif(
44+
not pytest_twisted.ASYNC_AWAIT,
45+
reason="async/await syntax not support on Python <3.5",
46+
)
47+
48+
4049
@pytest.fixture
4150
def cmd_opts(request):
4251
reactor = request.config.getoption("reactor", "default")
@@ -165,6 +174,33 @@ def test_succeed(foo):
165174
assert_outcomes(rr, {"passed": 2, "failed": 1})
166175

167176

177+
@skip_if_no_async_await()
178+
def test_async_fixture(testdir, cmd_opts):
179+
test_file = """
180+
from twisted.internet import reactor, defer
181+
import pytest
182+
import pytest_twisted
183+
184+
@pytest.fixture(scope="function", params=["fs", "imap", "web"])
185+
async def foo(request):
186+
d1, d2 = defer.Deferred(), defer.Deferred()
187+
reactor.callLater(0.01, d1.callback, 1)
188+
reactor.callLater(0.02, d2.callback, request.param)
189+
await d1
190+
return d2,
191+
192+
@pytest_twisted.inlineCallbacks
193+
def test_succeed(foo):
194+
x = yield foo[0]
195+
print('+++', x)
196+
if x == "web":
197+
raise RuntimeError("baz")
198+
"""
199+
testdir.makepyfile(test_file)
200+
rr = testdir.run(sys.executable, "-m", "pytest", "-v", *cmd_opts)
201+
assert_outcomes(rr, {"passed": 2, "failed": 1})
202+
203+
168204
@skip_if_reactor_not("default")
169205
def test_blockon_in_hook(testdir, cmd_opts):
170206
conftest_file = """

0 commit comments

Comments
 (0)