Skip to content

Commit fe21efc

Browse files
committed
embarrassingly passing tests
1 parent bf0338f commit fe21efc

File tree

2 files changed

+289
-56
lines changed

2 files changed

+289
-56
lines changed

pytest_twisted.py

Lines changed: 279 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -124,17 +124,22 @@ def init_twisted_greenlet():
124124
_config.external_reactor = True
125125

126126

127-
def stop_twisted_greenlet():
128-
if _instances.gr_twisted:
129-
_instances.reactor.stop()
130-
_instances.gr_twisted.switch()
127+
# def stop_twisted_greenlet():
128+
# if _instances.gr_twisted:
129+
# _instances.reactor.stop()
130+
# _instances.gr_twisted.switch()
131131

132132

133133
class _CoroutineWrapper:
134134
def __init__(self, coroutine, mark):
135+
# TODO: really an async def now, maybe, if that worked out
135136
self.coroutine = coroutine
137+
self.__name__ = self.coroutine.__name__ + 'ptcr'
136138
self.mark = mark
137139

140+
# def __call__(self):
141+
# print()
142+
138143

139144
def _marked_async_fixture(mark):
140145
@functools.wraps(pytest.fixture)
@@ -147,17 +152,28 @@ def fixture(*args, **kwargs):
147152
if scope != 'function':
148153
raise AsyncFixtureUnsupportedScopeError.from_scope(scope=scope)
149154

155+
# def marker(f):
156+
# @functools.wraps(f)
157+
# def w(*args, **kwargs):
158+
# return _CoroutineWrapper(
159+
# coroutine=f(*args, **kwargs),
160+
# mark=mark,
161+
# )
162+
#
163+
# return w
164+
150165
def marker(f):
151-
@functools.wraps(f)
152-
def w(*args, **kwargs):
153-
return _CoroutineWrapper(
154-
coroutine=f(*args, **kwargs),
155-
mark=mark,
156-
)
166+
f._pytest_twisted_coroutine_wrapper = _CoroutineWrapper(
167+
coroutine=f,
168+
mark=mark,
169+
)
157170

158-
return w
171+
return f
159172

160173
def decorator(f):
174+
# result = pytest.fixture(*args, **kwargs)(
175+
# _CoroutineWrapper(coroutine=f, mark=mark),
176+
# )
161177
result = pytest.fixture(*args, **kwargs)(marker(f))
162178

163179
return result
@@ -171,55 +187,266 @@ def decorator(f):
171187
async_yield_fixture = _marked_async_fixture('async_yield_fixture')
172188

173189

190+
def pytest_fixture_setup(fixturedef, request):
191+
maybe_wrapper = getattr(
192+
fixturedef.func,
193+
'_pytest_twisted_coroutine_wrapper',
194+
None,
195+
)
196+
if not isinstance(maybe_wrapper, _CoroutineWrapper):
197+
return None
198+
199+
if _instances.gr_twisted is not None:
200+
if _instances.gr_twisted.dead:
201+
raise RuntimeError("twisted reactor has stopped")
202+
203+
def in_reactor(d, f, *args):
204+
return defer.maybeDeferred(f, *args).chainDeferred(d)
205+
206+
d = defer.Deferred()
207+
_instances.reactor.callLater(
208+
0.0, in_reactor, d, _pytest_fixture_setup, fixturedef, request, maybe_wrapper
209+
)
210+
result = blockon_default(d)
211+
else:
212+
if not _instances.reactor.running:
213+
raise RuntimeError("twisted reactor is not running")
214+
result = blockingCallFromThread(
215+
_instances.reactor, _pytest_fixture_setup, fixturedef, request, maybe_wrapper
216+
)
217+
# return None
218+
return result
219+
220+
221+
async_yield_fixture_cache = {}
222+
223+
174224
@defer.inlineCallbacks
175-
def _pytest_pyfunc_call(pyfuncitem):
176-
testfunction = pyfuncitem.obj
225+
def _pytest_fixture_setup(fixturedef, request, wrapper):
226+
# return None
227+
#
228+
# if not isinstance(fixturedef.func, _CoroutineWrapper):
229+
# return None
230+
231+
fixture_function = fixturedef.func
232+
177233
async_generators = []
178-
funcargs = pyfuncitem.funcargs
179-
if hasattr(pyfuncitem, "_fixtureinfo"):
180-
testargs = {}
181-
for arg in pyfuncitem._fixtureinfo.argnames:
182-
if isinstance(funcargs[arg], _CoroutineWrapper):
183-
wrapper = funcargs[arg]
184-
185-
if wrapper.mark == 'async_fixture':
186-
arg_value = yield defer.ensureDeferred(
187-
wrapper.coroutine
188-
)
189-
elif wrapper.mark == 'async_yield_fixture':
190-
async_generators.append((arg, wrapper))
191-
arg_value = yield defer.ensureDeferred(
192-
wrapper.coroutine.__anext__(),
193-
)
194-
else:
195-
raise UnrecognizedCoroutineMarkError.from_mark(
196-
mark=wrapper.mark,
197-
)
198-
else:
199-
arg_value = funcargs[arg]
200-
201-
testargs[arg] = arg_value
234+
235+
kwargs = {
236+
name: request.getfixturevalue(name)
237+
for name in fixturedef.argnames
238+
}
239+
240+
if wrapper.mark == 'async_fixture':
241+
arg_value = yield defer.ensureDeferred(
242+
fixture_function(**kwargs)
243+
)
244+
elif wrapper.mark == 'async_yield_fixture':
245+
# async_generators.append((arg, wrapper))
246+
coroutine = fixture_function(**kwargs)
247+
# TODO: use request.addfinalizer() instead?
248+
async_yield_fixture_cache[request.param_index] = coroutine
249+
arg_value = yield defer.ensureDeferred(
250+
coroutine.__anext__(),
251+
)
202252
else:
203-
testargs = funcargs
204-
result = yield testfunction(**testargs)
253+
raise UnrecognizedCoroutineMarkError.from_mark(
254+
mark=wrapper.mark,
255+
)
205256

206-
async_generator_deferreds = [
207-
(arg, defer.ensureDeferred(g.coroutine.__anext__()))
208-
for arg, g in reversed(async_generators)
209-
]
257+
fixturedef.cached_result = (arg_value, request.param_index, None)
258+
259+
return arg_value
260+
261+
# async_generator_deferreds = [
262+
# (arg, defer.ensureDeferred(g.coroutine.__anext__()))
263+
# for arg, g in reversed(async_generators)
264+
# ]
265+
#
266+
# for arg, d in async_generator_deferreds:
267+
# try:
268+
# yield d
269+
# except StopAsyncIteration:
270+
# continue
271+
# else:
272+
# raise AsyncGeneratorFixtureDidNotStopError.from_generator(
273+
# generator=arg,
274+
# )
275+
276+
277+
# @defer.inlineCallbacks
278+
# def _pytest_fixture_post_finalizer(fixturedef, request, coroutine):
279+
# try:
280+
# yield defer.ensureDeferred(
281+
# coroutine.__anext__(),
282+
# )
283+
# except StopAsyncIteration:
284+
# # TODO: i don't remember why this makes sense...
285+
# pass
286+
# else:
287+
# raise AsyncGeneratorFixtureDidNotStopError.from_generator(
288+
# generator=coroutine,
289+
# )
290+
291+
292+
# TODO: but don't we want to do the finalizer? not wait until post it?
293+
def pytest_fixture_post_finalizer(fixturedef, request):
294+
maybe_coroutine = async_yield_fixture_cache.pop(request.param_index, None)
295+
296+
if maybe_coroutine is None:
297+
return None
298+
299+
coroutine = maybe_coroutine
300+
301+
to_be_torn_down.append(defer.ensureDeferred(coroutine.__anext__()))
302+
return None
303+
304+
# try:
305+
# if _instances.gr_twisted is not None:
306+
# if _instances.gr_twisted.dead:
307+
# raise RuntimeError("twisted reactor has stopped")
308+
#
309+
# def in_reactor(d, f, *args):
310+
# return defer.maybeDeferred(f, *args).chainDeferred(d)
311+
#
312+
# d = defer.Deferred()
313+
# _instances.reactor.callLater(
314+
# 0.0, in_reactor, d, _pytest_fixture_post_finalizer, fixturedef, request, coroutine
315+
# )
316+
# result = blockon_default(d)
317+
# else:
318+
# if not _instances.reactor.running:
319+
# raise RuntimeError("twisted reactor is not running")
320+
# result = blockingCallFromThread(
321+
# _instances.reactor, _pytest_fixture_post_finalizer, fixturedef, request, coroutine
322+
# )
323+
# except StopAsyncIteration as e:
324+
# print(e)
325+
#
326+
# # async_yield_fixture_cache.pop(request.param_index)
327+
#
328+
# # return None
329+
return result
210330

211-
for arg, d in async_generator_deferreds:
212-
try:
213-
yield d
214-
except StopAsyncIteration:
215-
continue
331+
332+
@defer.inlineCallbacks
333+
def tear_it_down(deferred):
334+
try:
335+
yield deferred
336+
except StopAsyncIteration:
337+
return
338+
except Exception as e:
339+
e = e
340+
else:
341+
e = None
342+
343+
raise AsyncGeneratorFixtureDidNotStopError.from_generator(
344+
generator=deferred,
345+
)
346+
347+
348+
to_be_torn_down = []
349+
350+
# TODO: https://docs.pytest.org/en/latest/reference.html#_pytest.hookspec.pytest_runtest_protocol
351+
# claims it should also take a nextItem but that triggers a direct error
352+
353+
354+
@pytest.hookimpl(hookwrapper=True)
355+
def pytest_runtest_teardown(item):
356+
yield
357+
358+
# deferreds = []
359+
#
360+
# while len(to_be_torn_down) > 0:
361+
# coroutine = to_be_torn_down.pop(0)
362+
# deferreds.append(defer.ensureDeferred(coroutine.__anext__()))
363+
#
364+
# for deferred in deferreds:
365+
while len(to_be_torn_down) > 0:
366+
deferred = to_be_torn_down.pop(0)
367+
if _instances.gr_twisted is not None:
368+
if _instances.gr_twisted.dead:
369+
raise RuntimeError("twisted reactor has stopped")
370+
371+
def in_reactor(d, f, *args):
372+
# return f.chainDeferred(d)
373+
return defer.maybeDeferred(f, *args).chainDeferred(d)
374+
375+
d = defer.Deferred()
376+
_instances.reactor.callLater(
377+
0.0, in_reactor, d, tear_it_down, deferred
378+
)
379+
blockon_default(d)
380+
# blockon_default(tear_it_down(deferred))
216381
else:
217-
raise AsyncGeneratorFixtureDidNotStopError.from_generator(
218-
generator=arg,
382+
if not _instances.reactor.running:
383+
raise RuntimeError("twisted reactor is not running")
384+
blockingCallFromThread(
385+
_instances.reactor, tear_it_down, deferred,
219386
)
387+
# blockingCallFromThread(
388+
# _instances.reactor, tear_it_down, deferred
389+
# )
390+
391+
return None
392+
220393

394+
@defer.inlineCallbacks
395+
def _pytest_pyfunc_call(pyfuncitem):
396+
kwargs = {name: value for name, value in pyfuncitem.funcargs.items() if name in pyfuncitem._fixtureinfo.argnames}
397+
result = yield pyfuncitem.obj(**kwargs)
221398
defer.returnValue(result)
222399

400+
return
401+
# print()
402+
#
403+
# testfunction = pyfuncitem.obj
404+
# async_generators = []
405+
# funcargs = pyfuncitem.funcargs
406+
# if hasattr(pyfuncitem, "_fixtureinfo"):
407+
# testargs = {}
408+
# for arg in pyfuncitem._fixtureinfo.argnames:
409+
# if isinstance(funcargs[arg], _CoroutineWrapper):
410+
# wrapper = funcargs[arg]
411+
#
412+
# if wrapper.mark == 'async_fixture':
413+
# arg_value = yield defer.ensureDeferred(
414+
# wrapper.coroutine
415+
# )
416+
# elif wrapper.mark == 'async_yield_fixture':
417+
# async_generators.append((arg, wrapper))
418+
# arg_value = yield defer.ensureDeferred(
419+
# wrapper.coroutine.__anext__(),
420+
# )
421+
# else:
422+
# raise UnrecognizedCoroutineMarkError.from_mark(
423+
# mark=wrapper.mark,
424+
# )
425+
# else:
426+
# arg_value = funcargs[arg]
427+
#
428+
# testargs[arg] = arg_value
429+
# else:
430+
# testargs = funcargs
431+
# result = yield testfunction(**testargs)
432+
#
433+
# async_generator_deferreds = [
434+
# (arg, defer.ensureDeferred(g.coroutine.__anext__()))
435+
# for arg, g in reversed(async_generators)
436+
# ]
437+
#
438+
# for arg, d in async_generator_deferreds:
439+
# try:
440+
# yield d
441+
# except StopAsyncIteration:
442+
# continue
443+
# else:
444+
# raise AsyncGeneratorFixtureDidNotStopError.from_generator(
445+
# generator=arg,
446+
# )
447+
#
448+
# defer.returnValue(result)
449+
223450

224451
def pytest_pyfunc_call(pyfuncitem):
225452
if _instances.gr_twisted is not None:
@@ -243,9 +470,10 @@ def in_reactor(d, f, *args):
243470
return True
244471

245472

473+
# TODO: switch to some plugin callback to guarantee order before other fixtures?
246474
@pytest.fixture(scope="session", autouse=True)
247475
def twisted_greenlet(request):
248-
request.addfinalizer(stop_twisted_greenlet)
476+
# request.addfinalizer(stop_twisted_greenlet)
249477
return _instances.gr_twisted
250478

251479

0 commit comments

Comments
 (0)