1
- import asyncio
1
+ import functools
2
2
from contextlib import (
3
3
asynccontextmanager , AbstractAsyncContextManager ,
4
4
AsyncExitStack , nullcontext , aclosing , contextmanager )
8
8
9
9
from test .test_contextlib import TestBaseExitStack
10
10
11
- support .requires_working_socket (module = True )
12
11
13
- def tearDownModule ():
14
- asyncio ._set_event_loop_policy (None )
12
+ def _run_async_fn (async_fn , / , * args , ** kwargs ):
13
+ coro = async_fn (* args , ** kwargs )
14
+ try :
15
+ coro .send (None )
16
+ except StopIteration as e :
17
+ return e .value
18
+ else :
19
+ raise AssertionError ("coroutine did not complete" )
20
+ finally :
21
+ coro .close ()
15
22
16
23
17
- class TestAbstractAsyncContextManager (unittest .IsolatedAsyncioTestCase ):
24
+ def _async_test (async_fn ):
25
+ """Decorator to turn an async function into a synchronous function"""
26
+ @functools .wraps (async_fn )
27
+ def wrapper (* args , ** kwargs ):
28
+ return _run_async_fn (async_fn , * args , ** kwargs )
18
29
30
+ return wrapper
31
+
32
+
33
+ class TestAbstractAsyncContextManager (unittest .TestCase ):
34
+
35
+ @_async_test
19
36
async def test_enter (self ):
20
37
class DefaultEnter (AbstractAsyncContextManager ):
21
38
async def __aexit__ (self , * args ):
@@ -27,6 +44,7 @@ async def __aexit__(self, *args):
27
44
async with manager as context :
28
45
self .assertIs (manager , context )
29
46
47
+ @_async_test
30
48
async def test_slots (self ):
31
49
class DefaultAsyncContextManager (AbstractAsyncContextManager ):
32
50
__slots__ = ()
@@ -38,6 +56,7 @@ async def __aexit__(self, *args):
38
56
manager = DefaultAsyncContextManager ()
39
57
manager .var = 42
40
58
59
+ @_async_test
41
60
async def test_async_gen_propagates_generator_exit (self ):
42
61
# A regression test for https://bugs.python.org/issue33786.
43
62
@@ -88,8 +107,9 @@ class NoneAexit(ManagerFromScratch):
88
107
self .assertFalse (issubclass (NoneAexit , AbstractAsyncContextManager ))
89
108
90
109
91
- class AsyncContextManagerTestCase (unittest .IsolatedAsyncioTestCase ):
110
+ class AsyncContextManagerTestCase (unittest .TestCase ):
92
111
112
+ @_async_test
93
113
async def test_contextmanager_plain (self ):
94
114
state = []
95
115
@asynccontextmanager
@@ -103,6 +123,7 @@ async def woohoo():
103
123
state .append (x )
104
124
self .assertEqual (state , [1 , 42 , 999 ])
105
125
126
+ @_async_test
106
127
async def test_contextmanager_finally (self ):
107
128
state = []
108
129
@asynccontextmanager
@@ -120,6 +141,7 @@ async def woohoo():
120
141
raise ZeroDivisionError ()
121
142
self .assertEqual (state , [1 , 42 , 999 ])
122
143
144
+ @_async_test
123
145
async def test_contextmanager_traceback (self ):
124
146
@asynccontextmanager
125
147
async def f ():
@@ -175,6 +197,7 @@ class StopAsyncIterationSubclass(StopAsyncIteration):
175
197
self .assertEqual (frames [0 ].name , 'test_contextmanager_traceback' )
176
198
self .assertEqual (frames [0 ].line , 'raise stop_exc' )
177
199
200
+ @_async_test
178
201
async def test_contextmanager_no_reraise (self ):
179
202
@asynccontextmanager
180
203
async def whee ():
@@ -184,6 +207,7 @@ async def whee():
184
207
# Calling __aexit__ should not result in an exception
185
208
self .assertFalse (await ctx .__aexit__ (TypeError , TypeError ("foo" ), None ))
186
209
210
+ @_async_test
187
211
async def test_contextmanager_trap_yield_after_throw (self ):
188
212
@asynccontextmanager
189
213
async def whoo ():
@@ -199,6 +223,7 @@ async def whoo():
199
223
# The "gen" attribute is an implementation detail.
200
224
self .assertFalse (ctx .gen .ag_suspended )
201
225
226
+ @_async_test
202
227
async def test_contextmanager_trap_no_yield (self ):
203
228
@asynccontextmanager
204
229
async def whoo ():
@@ -208,6 +233,7 @@ async def whoo():
208
233
with self .assertRaises (RuntimeError ):
209
234
await ctx .__aenter__ ()
210
235
236
+ @_async_test
211
237
async def test_contextmanager_trap_second_yield (self ):
212
238
@asynccontextmanager
213
239
async def whoo ():
@@ -221,6 +247,7 @@ async def whoo():
221
247
# The "gen" attribute is an implementation detail.
222
248
self .assertFalse (ctx .gen .ag_suspended )
223
249
250
+ @_async_test
224
251
async def test_contextmanager_non_normalised (self ):
225
252
@asynccontextmanager
226
253
async def whoo ():
@@ -234,6 +261,7 @@ async def whoo():
234
261
with self .assertRaises (SyntaxError ):
235
262
await ctx .__aexit__ (RuntimeError , None , None )
236
263
264
+ @_async_test
237
265
async def test_contextmanager_except (self ):
238
266
state = []
239
267
@asynccontextmanager
@@ -251,6 +279,7 @@ async def woohoo():
251
279
raise ZeroDivisionError (999 )
252
280
self .assertEqual (state , [1 , 42 , 999 ])
253
281
282
+ @_async_test
254
283
async def test_contextmanager_except_stopiter (self ):
255
284
@asynccontextmanager
256
285
async def woohoo ():
@@ -277,6 +306,7 @@ class StopAsyncIterationSubclass(StopAsyncIteration):
277
306
else :
278
307
self .fail (f'{ stop_exc } was suppressed' )
279
308
309
+ @_async_test
280
310
async def test_contextmanager_wrap_runtimeerror (self ):
281
311
@asynccontextmanager
282
312
async def woohoo ():
@@ -321,12 +351,14 @@ def test_contextmanager_doc_attrib(self):
321
351
self .assertEqual (baz .__doc__ , "Whee!" )
322
352
323
353
@support .requires_docstrings
354
+ @_async_test
324
355
async def test_instance_docstring_given_cm_docstring (self ):
325
356
baz = self ._create_contextmanager_attribs ()(None )
326
357
self .assertEqual (baz .__doc__ , "Whee!" )
327
358
async with baz :
328
359
pass # suppress warning
329
360
361
+ @_async_test
330
362
async def test_keywords (self ):
331
363
# Ensure no keyword arguments are inhibited
332
364
@asynccontextmanager
@@ -335,6 +367,7 @@ async def woohoo(self, func, args, kwds):
335
367
async with woohoo (self = 11 , func = 22 , args = 33 , kwds = 44 ) as target :
336
368
self .assertEqual (target , (11 , 22 , 33 , 44 ))
337
369
370
+ @_async_test
338
371
async def test_recursive (self ):
339
372
depth = 0
340
373
ncols = 0
@@ -361,6 +394,7 @@ async def recursive():
361
394
self .assertEqual (ncols , 10 )
362
395
self .assertEqual (depth , 0 )
363
396
397
+ @_async_test
364
398
async def test_decorator (self ):
365
399
entered = False
366
400
@@ -379,6 +413,7 @@ async def test():
379
413
await test ()
380
414
self .assertFalse (entered )
381
415
416
+ @_async_test
382
417
async def test_decorator_with_exception (self ):
383
418
entered = False
384
419
@@ -401,6 +436,7 @@ async def test():
401
436
await test ()
402
437
self .assertFalse (entered )
403
438
439
+ @_async_test
404
440
async def test_decorating_method (self ):
405
441
406
442
@asynccontextmanager
@@ -435,14 +471,15 @@ async def method(self, a, b, c=None):
435
471
self .assertEqual (test .b , 2 )
436
472
437
473
438
- class AclosingTestCase (unittest .IsolatedAsyncioTestCase ):
474
+ class AclosingTestCase (unittest .TestCase ):
439
475
440
476
@support .requires_docstrings
441
477
def test_instance_docs (self ):
442
478
cm_docstring = aclosing .__doc__
443
479
obj = aclosing (None )
444
480
self .assertEqual (obj .__doc__ , cm_docstring )
445
481
482
+ @_async_test
446
483
async def test_aclosing (self ):
447
484
state = []
448
485
class C :
@@ -454,6 +491,7 @@ async def aclose(self):
454
491
self .assertEqual (x , y )
455
492
self .assertEqual (state , [1 ])
456
493
494
+ @_async_test
457
495
async def test_aclosing_error (self ):
458
496
state = []
459
497
class C :
@@ -467,6 +505,7 @@ async def aclose(self):
467
505
1 / 0
468
506
self .assertEqual (state , [1 ])
469
507
508
+ @_async_test
470
509
async def test_aclosing_bpo41229 (self ):
471
510
state = []
472
511
@@ -492,45 +531,27 @@ async def agenfunc():
492
531
self .assertEqual (state , [1 ])
493
532
494
533
495
- class TestAsyncExitStack (TestBaseExitStack , unittest .IsolatedAsyncioTestCase ):
534
+ class TestAsyncExitStack (TestBaseExitStack , unittest .TestCase ):
496
535
class SyncAsyncExitStack (AsyncExitStack ):
497
- @staticmethod
498
- def run_coroutine (coro ):
499
- loop = asyncio .new_event_loop ()
500
- t = loop .create_task (coro )
501
- t .add_done_callback (lambda f : loop .stop ())
502
- loop .run_forever ()
503
-
504
- exc = t .exception ()
505
- if not exc :
506
- return t .result ()
507
- else :
508
- context = exc .__context__
509
-
510
- try :
511
- raise exc
512
- except :
513
- exc .__context__ = context
514
- raise exc
515
536
516
537
def close (self ):
517
- return self . run_coroutine (self .aclose () )
538
+ return _run_async_fn (self .aclose )
518
539
519
540
def __enter__ (self ):
520
- return self . run_coroutine (self .__aenter__ () )
541
+ return _run_async_fn (self .__aenter__ )
521
542
522
543
def __exit__ (self , * exc_details ):
523
- return self . run_coroutine (self .__aexit__ ( * exc_details ) )
544
+ return _run_async_fn (self .__aexit__ , * exc_details )
524
545
525
546
exit_stack = SyncAsyncExitStack
526
547
callback_error_internal_frames = [
527
- ('__exit__' , 'return self.run_coroutine(self.__aexit__(*exc_details))' ),
528
- ('run_coroutine' , 'raise exc' ),
529
- ('run_coroutine' , 'raise exc' ),
548
+ ('__exit__' , 'return _run_async_fn(self.__aexit__, *exc_details)' ),
549
+ ('_run_async_fn' , 'coro.send(None)' ),
530
550
('__aexit__' , 'raise exc' ),
531
551
('__aexit__' , 'cb_suppress = cb(*exc_details)' ),
532
552
]
533
553
554
+ @_async_test
534
555
async def test_async_callback (self ):
535
556
expected = [
536
557
((), {}),
@@ -573,6 +594,7 @@ async def _exit(*args, **kwds):
573
594
stack .push_async_callback (callback = _exit , arg = 3 )
574
595
self .assertEqual (result , [])
575
596
597
+ @_async_test
576
598
async def test_async_push (self ):
577
599
exc_raised = ZeroDivisionError
578
600
async def _expect_exc (exc_type , exc , exc_tb ):
@@ -608,6 +630,7 @@ async def __aexit__(self, *exc_details):
608
630
self .assertIs (stack ._exit_callbacks [- 1 ][1 ], _expect_exc )
609
631
1 / 0
610
632
633
+ @_async_test
611
634
async def test_enter_async_context (self ):
612
635
class TestCM (object ):
613
636
async def __aenter__ (self ):
@@ -629,6 +652,7 @@ async def _exit():
629
652
630
653
self .assertEqual (result , [1 , 2 , 3 , 4 ])
631
654
655
+ @_async_test
632
656
async def test_enter_async_context_errors (self ):
633
657
class LacksEnterAndExit :
634
658
pass
@@ -648,6 +672,7 @@ async def __aenter__(self):
648
672
await stack .enter_async_context (LacksExit ())
649
673
self .assertFalse (stack ._exit_callbacks )
650
674
675
+ @_async_test
651
676
async def test_async_exit_exception_chaining (self ):
652
677
# Ensure exception chaining matches the reference behaviour
653
678
async def raise_exc (exc ):
@@ -679,6 +704,7 @@ async def suppress_exc(*exc_details):
679
704
self .assertIsInstance (inner_exc , ValueError )
680
705
self .assertIsInstance (inner_exc .__context__ , ZeroDivisionError )
681
706
707
+ @_async_test
682
708
async def test_async_exit_exception_explicit_none_context (self ):
683
709
# Ensure AsyncExitStack chaining matches actual nested `with` statements
684
710
# regarding explicit __context__ = None.
@@ -713,6 +739,7 @@ async def my_cm_with_exit_stack():
713
739
else :
714
740
self .fail ("Expected IndexError, but no exception was raised" )
715
741
742
+ @_async_test
716
743
async def test_instance_bypass_async (self ):
717
744
class Example (object ): pass
718
745
cm = Example ()
@@ -725,7 +752,8 @@ class Example(object): pass
725
752
self .assertIs (stack ._exit_callbacks [- 1 ][1 ], cm )
726
753
727
754
728
- class TestAsyncNullcontext (unittest .IsolatedAsyncioTestCase ):
755
+ class TestAsyncNullcontext (unittest .TestCase ):
756
+ @_async_test
729
757
async def test_async_nullcontext (self ):
730
758
class C :
731
759
pass
0 commit comments