Skip to content

Commit a0ea1d7

Browse files
committed
Make decorators not require being called
1 parent fbc1ce8 commit a0ea1d7

File tree

3 files changed

+86
-17
lines changed

3 files changed

+86
-17
lines changed

README.rst

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -164,10 +164,6 @@ async/await fixtures
164164
pytest fixture semantics of setup, value, and teardown. At present only
165165
function and module scope are supported.
166166

167-
Note: You must *call* ``pytest_twisted.async_fixture()`` and
168-
``pytest_twisted.async_yield_fixture()``.
169-
This requirement may be removed in a future release.
170-
171167
.. code-block:: python
172168
173169
# No yield (coroutine function)

pytest_twisted.py

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,29 @@ def decorator_apply(dec, func):
119119
dict(decfunc=dec(func)), __wrapped__=func)
120120

121121

122+
def _optional_arguments():
123+
def decorator_decorator(d):
124+
# TODO: this should get the signature of d minus the f or something
125+
def decorator_wrapper(f=None, **decorator_arguments):
126+
"""this is decorator_wrapper"""
127+
if f is not None:
128+
if len(decorator_arguments) > 0 or not callable(f):
129+
raise Exception('positional options not allowed')
130+
131+
return d(f)
132+
133+
# TODO: this should get the signature of d minus the kwargs
134+
def decorator_closure_on_arguments(f):
135+
return d(f, **decorator_arguments)
136+
137+
return decorator_closure_on_arguments
138+
139+
return decorator_wrapper
140+
141+
return decorator_decorator
142+
143+
144+
@_optional_arguments()
122145
def inlineCallbacks(f):
123146
"""
124147
Mark as inline callbacks test for pytest-twisted processing and apply
@@ -135,6 +158,7 @@ def inlineCallbacks(f):
135158
return decorated
136159

137160

161+
@_optional_arguments()
138162
def ensureDeferred(f):
139163
"""
140164
Mark as async test for pytest-twisted processing.
@@ -177,7 +201,8 @@ def _set_mark(o, mark):
177201

178202
def _marked_async_fixture(mark):
179203
@functools.wraps(pytest.fixture)
180-
def fixture(*args, **kwargs):
204+
@_optional_arguments()
205+
def fixture(f, *args, **kwargs):
181206
try:
182207
scope = args[0]
183208
except IndexError:
@@ -197,13 +222,10 @@ def fixture(*args, **kwargs):
197222
# https://github.com/pytest-dev/pytest-twisted/issues/56
198223
raise AsyncFixtureUnsupportedScopeError.from_scope(scope=scope)
199224

200-
def decorator(f):
201-
_set_mark(f, mark)
202-
result = pytest.fixture(*args, **kwargs)(f)
203-
204-
return result
225+
_set_mark(f, mark)
226+
result = pytest.fixture(*args, **kwargs)(f)
205227

206-
return decorator
228+
return result
207229

208230
return fixture
209231

testing/test_basic.py

Lines changed: 57 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,16 @@ def test_more_fail():
226226
assert_outcomes(rr, {"failed": 1})
227227

228228

229-
def test_inlineCallbacks(testdir, cmd_opts):
229+
@pytest.fixture(
230+
name="empty_optional_call",
231+
params=["", "()"],
232+
ids=["no call", "empty call"],
233+
)
234+
def empty_optional_call_fixture(request):
235+
return request.param
236+
237+
238+
def test_inlineCallbacks(testdir, cmd_opts, empty_optional_call):
230239
test_file = """
231240
from twisted.internet import reactor, defer
232241
import pytest
@@ -236,19 +245,19 @@ def test_inlineCallbacks(testdir, cmd_opts):
236245
def foo(request):
237246
return request.param
238247
239-
@pytest_twisted.inlineCallbacks
248+
@pytest_twisted.inlineCallbacks{optional_call}
240249
def test_succeed(foo):
241250
yield defer.succeed(foo)
242251
if foo == "web":
243252
raise RuntimeError("baz")
244-
"""
253+
""".format(optional_call=empty_optional_call)
245254
testdir.makepyfile(test_file)
246255
rr = testdir.run(sys.executable, "-m", "pytest", "-v", *cmd_opts)
247256
assert_outcomes(rr, {"passed": 2, "failed": 1})
248257

249258

250259
@skip_if_no_async_await()
251-
def test_async_await(testdir, cmd_opts):
260+
def test_async_await(testdir, cmd_opts, empty_optional_call):
252261
test_file = """
253262
from twisted.internet import reactor, defer
254263
import pytest
@@ -258,12 +267,12 @@ def test_async_await(testdir, cmd_opts):
258267
def foo(request):
259268
return request.param
260269
261-
@pytest_twisted.ensureDeferred
270+
@pytest_twisted.ensureDeferred{optional_call}
262271
async def test_succeed(foo):
263272
await defer.succeed(foo)
264273
if foo == "web":
265274
raise RuntimeError("baz")
266-
"""
275+
""".format(optional_call=empty_optional_call)
267276
testdir.makepyfile(test_file)
268277
rr = testdir.run(sys.executable, "-m", "pytest", "-v", *cmd_opts)
269278
assert_outcomes(rr, {"passed": 2, "failed": 1})
@@ -376,6 +385,25 @@ def test_succeed_blue(foo):
376385
assert_outcomes(rr, {"passed": 2, "failed": 1})
377386

378387

388+
@skip_if_no_async_await()
389+
def test_async_fixture_no_arguments(testdir, cmd_opts, empty_optional_call):
390+
test_file = """
391+
from twisted.internet import reactor, defer
392+
import pytest
393+
import pytest_twisted
394+
395+
@pytest_twisted.async_fixture{optional_call}
396+
async def scope(request):
397+
return request.scope
398+
399+
def test_is_function_scope(scope):
400+
assert scope == "function"
401+
""".format(optional_call=empty_optional_call)
402+
testdir.makepyfile(test_file)
403+
rr = testdir.run(sys.executable, "-m", "pytest", "-v", *cmd_opts)
404+
assert_outcomes(rr, {"passed": 1})
405+
406+
379407
@skip_if_no_async_generators()
380408
def test_async_yield_fixture_concurrent_teardown(testdir, cmd_opts):
381409
test_file = """
@@ -453,6 +481,29 @@ def test_succeed(foo):
453481
assert_outcomes(rr, {"passed": 4, "failed": 1, "errors": 2})
454482

455483

484+
@skip_if_no_async_generators()
485+
def test_async_yield_fixture_no_arguments(
486+
testdir,
487+
cmd_opts,
488+
empty_optional_call,
489+
):
490+
test_file = """
491+
from twisted.internet import reactor, defer
492+
import pytest
493+
import pytest_twisted
494+
495+
@pytest_twisted.async_yield_fixture{optional_call}
496+
async def scope(request):
497+
yield request.scope
498+
499+
def test_is_function_scope(scope):
500+
assert scope == "function"
501+
""".format(optional_call=empty_optional_call)
502+
testdir.makepyfile(test_file)
503+
rr = testdir.run(sys.executable, "-m", "pytest", "-v", *cmd_opts)
504+
assert_outcomes(rr, {"passed": 1})
505+
506+
456507
@skip_if_no_async_generators()
457508
def test_async_yield_fixture_function_scope(testdir, cmd_opts):
458509
test_file = """

0 commit comments

Comments
 (0)