@@ -124,17 +124,22 @@ def init_twisted_greenlet():
124
124
_config .external_reactor = True
125
125
126
126
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()
131
131
132
132
133
133
class _CoroutineWrapper :
134
134
def __init__ (self , coroutine , mark ):
135
+ # TODO: really an async def now, maybe, if that worked out
135
136
self .coroutine = coroutine
137
+ self .__name__ = self .coroutine .__name__ + 'ptcr'
136
138
self .mark = mark
137
139
140
+ # def __call__(self):
141
+ # print()
142
+
138
143
139
144
def _marked_async_fixture (mark ):
140
145
@functools .wraps (pytest .fixture )
@@ -147,17 +152,28 @@ def fixture(*args, **kwargs):
147
152
if scope != 'function' :
148
153
raise AsyncFixtureUnsupportedScopeError .from_scope (scope = scope )
149
154
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
+
150
165
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
+ )
157
170
158
- return w
171
+ return f
159
172
160
173
def decorator (f ):
174
+ # result = pytest.fixture(*args, **kwargs)(
175
+ # _CoroutineWrapper(coroutine=f, mark=mark),
176
+ # )
161
177
result = pytest .fixture (* args , ** kwargs )(marker (f ))
162
178
163
179
return result
@@ -171,55 +187,266 @@ def decorator(f):
171
187
async_yield_fixture = _marked_async_fixture ('async_yield_fixture' )
172
188
173
189
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
+
174
224
@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
+
177
233
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
+ )
202
252
else :
203
- testargs = funcargs
204
- result = yield testfunction (** testargs )
253
+ raise UnrecognizedCoroutineMarkError .from_mark (
254
+ mark = wrapper .mark ,
255
+ )
205
256
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
210
330
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))
216
381
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 ,
219
386
)
387
+ # blockingCallFromThread(
388
+ # _instances.reactor, tear_it_down, deferred
389
+ # )
390
+
391
+ return None
392
+
220
393
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 )
221
398
defer .returnValue (result )
222
399
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
+
223
450
224
451
def pytest_pyfunc_call (pyfuncitem ):
225
452
if _instances .gr_twisted is not None :
@@ -243,9 +470,10 @@ def in_reactor(d, f, *args):
243
470
return True
244
471
245
472
473
+ # TODO: switch to some plugin callback to guarantee order before other fixtures?
246
474
@pytest .fixture (scope = "session" , autouse = True )
247
475
def twisted_greenlet (request ):
248
- request .addfinalizer (stop_twisted_greenlet )
476
+ # request.addfinalizer(stop_twisted_greenlet)
249
477
return _instances .gr_twisted
250
478
251
479
0 commit comments