66import difflib
77import gc
88from functools import wraps
9+ import asyncio
10+
911
1012class tracecontext :
1113 """Context manager that traces its enter and exit."""
@@ -19,6 +21,20 @@ def __enter__(self):
1921 def __exit__ (self , * exc_info ):
2022 self .output .append (- self .value )
2123
24+ class asynctracecontext :
25+ """Asynchronous context manager that traces its aenter and aexit."""
26+ def __init__ (self , output , value ):
27+ self .output = output
28+ self .value = value
29+
30+ async def __aenter__ (self ):
31+ self .output .append (self .value )
32+
33+ async def __aexit__ (self , * exc_info ):
34+ self .output .append (- self .value )
35+
36+
37+
2238# A very basic example. If this fails, we're in deep trouble.
2339def basic ():
2440 return 1
@@ -636,6 +652,19 @@ def run_test(self, func, jumpFrom, jumpTo, expected, error=None,
636652 sys .settrace (None )
637653 self .compare_jump_output (expected , output )
638654
655+ def run_async_test (self , func , jumpFrom , jumpTo , expected , error = None ,
656+ event = 'line' , decorated = False ):
657+ tracer = JumpTracer (func , jumpFrom , jumpTo , event , decorated )
658+ sys .settrace (tracer .trace )
659+ output = []
660+ if error is None :
661+ asyncio .run (func (output ))
662+ else :
663+ with self .assertRaisesRegex (* error ):
664+ asyncio .run (func (output ))
665+ sys .settrace (None )
666+ self .compare_jump_output (expected , output )
667+
639668 def jump_test (jumpFrom , jumpTo , expected , error = None , event = 'line' ):
640669 """Decorator that creates a test that makes a jump
641670 from one place to another in the following code.
@@ -648,6 +677,18 @@ def test(self):
648677 return test
649678 return decorator
650679
680+ def async_jump_test (jumpFrom , jumpTo , expected , error = None , event = 'line' ):
681+ """Decorator that creates a test that makes a jump
682+ from one place to another in the following asynchronous code.
683+ """
684+ def decorator (func ):
685+ @wraps (func )
686+ def test (self ):
687+ self .run_async_test (func , jumpFrom , jumpTo , expected ,
688+ error = error , event = event , decorated = True )
689+ return test
690+ return decorator
691+
651692 ## The first set of 'jump' tests are for things that are allowed:
652693
653694 @jump_test (1 , 3 , [3 ])
@@ -774,12 +815,24 @@ def test_jump_forwards_out_of_with_block(output):
774815 output .append (2 )
775816 output .append (3 )
776817
818+ @async_jump_test (2 , 3 , [1 , 3 ])
819+ async def test_jump_forwards_out_of_async_with_block (output ):
820+ async with asynctracecontext (output , 1 ):
821+ output .append (2 )
822+ output .append (3 )
823+
777824 @jump_test (3 , 1 , [1 , 2 , 1 , 2 , 3 , - 2 ])
778825 def test_jump_backwards_out_of_with_block (output ):
779826 output .append (1 )
780827 with tracecontext (output , 2 ):
781828 output .append (3 )
782829
830+ @async_jump_test (3 , 1 , [1 , 2 , 1 , 2 , 3 , - 2 ])
831+ async def test_jump_backwards_out_of_async_with_block (output ):
832+ output .append (1 )
833+ async with asynctracecontext (output , 2 ):
834+ output .append (3 )
835+
783836 @jump_test (2 , 5 , [5 ])
784837 def test_jump_forwards_out_of_try_finally_block (output ):
785838 try :
@@ -843,6 +896,14 @@ def test_jump_across_with(output):
843896 with tracecontext (output , 4 ):
844897 output .append (5 )
845898
899+ @async_jump_test (2 , 4 , [1 , 4 , 5 , - 4 ])
900+ async def test_jump_across_async_with (output ):
901+ output .append (1 )
902+ async with asynctracecontext (output , 2 ):
903+ output .append (3 )
904+ async with asynctracecontext (output , 4 ):
905+ output .append (5 )
906+
846907 @jump_test (4 , 5 , [1 , 3 , 5 , 6 ])
847908 def test_jump_out_of_with_block_within_for_block (output ):
848909 output .append (1 )
@@ -852,6 +913,15 @@ def test_jump_out_of_with_block_within_for_block(output):
852913 output .append (5 )
853914 output .append (6 )
854915
916+ @async_jump_test (4 , 5 , [1 , 3 , 5 , 6 ])
917+ async def test_jump_out_of_async_with_block_within_for_block (output ):
918+ output .append (1 )
919+ for i in [1 ]:
920+ async with asynctracecontext (output , 3 ):
921+ output .append (4 )
922+ output .append (5 )
923+ output .append (6 )
924+
855925 @jump_test (4 , 5 , [1 , 2 , 3 , 5 , - 2 , 6 ])
856926 def test_jump_out_of_with_block_within_with_block (output ):
857927 output .append (1 )
@@ -861,6 +931,15 @@ def test_jump_out_of_with_block_within_with_block(output):
861931 output .append (5 )
862932 output .append (6 )
863933
934+ @async_jump_test (4 , 5 , [1 , 2 , 3 , 5 , - 2 , 6 ])
935+ async def test_jump_out_of_async_with_block_within_with_block (output ):
936+ output .append (1 )
937+ with tracecontext (output , 2 ):
938+ async with asynctracecontext (output , 3 ):
939+ output .append (4 )
940+ output .append (5 )
941+ output .append (6 )
942+
864943 @jump_test (5 , 6 , [2 , 4 , 6 , 7 ])
865944 def test_jump_out_of_with_block_within_finally_block (output ):
866945 try :
@@ -871,6 +950,16 @@ def test_jump_out_of_with_block_within_finally_block(output):
871950 output .append (6 )
872951 output .append (7 )
873952
953+ @async_jump_test (5 , 6 , [2 , 4 , 6 , 7 ])
954+ async def test_jump_out_of_async_with_block_within_finally_block (output ):
955+ try :
956+ output .append (2 )
957+ finally :
958+ async with asynctracecontext (output , 4 ):
959+ output .append (5 )
960+ output .append (6 )
961+ output .append (7 )
962+
874963 @jump_test (8 , 11 , [1 , 3 , 5 , 11 , 12 ])
875964 def test_jump_out_of_complex_nested_blocks (output ):
876965 output .append (1 )
@@ -894,6 +983,14 @@ def test_jump_out_of_with_assignment(output):
894983 output .append (4 )
895984 output .append (5 )
896985
986+ @async_jump_test (3 , 5 , [1 , 2 , 5 ])
987+ async def test_jump_out_of_async_with_assignment (output ):
988+ output .append (1 )
989+ async with asynctracecontext (output , 2 ) \
990+ as x :
991+ output .append (4 )
992+ output .append (5 )
993+
897994 @jump_test (3 , 6 , [1 , 6 , 8 , 9 ])
898995 def test_jump_over_return_in_try_finally_block (output ):
899996 output .append (1 )
@@ -996,12 +1093,24 @@ def test_no_jump_forwards_into_with_block(output):
9961093 with tracecontext (output , 2 ):
9971094 output .append (3 )
9981095
1096+ @async_jump_test (1 , 3 , [], (ValueError , 'into' ))
1097+ async def test_no_jump_forwards_into_async_with_block (output ):
1098+ output .append (1 )
1099+ async with asynctracecontext (output , 2 ):
1100+ output .append (3 )
1101+
9991102 @jump_test (3 , 2 , [1 , 2 , - 1 ], (ValueError , 'into' ))
10001103 def test_no_jump_backwards_into_with_block (output ):
10011104 with tracecontext (output , 1 ):
10021105 output .append (2 )
10031106 output .append (3 )
10041107
1108+ @async_jump_test (3 , 2 , [1 , 2 , - 1 ], (ValueError , 'into' ))
1109+ async def test_no_jump_backwards_into_async_with_block (output ):
1110+ async with asynctracecontext (output , 1 ):
1111+ output .append (2 )
1112+ output .append (3 )
1113+
10051114 @jump_test (1 , 3 , [], (ValueError , 'into' ))
10061115 def test_no_jump_forwards_into_try_finally_block (output ):
10071116 output .append (1 )
@@ -1082,6 +1191,14 @@ def test_no_jump_between_with_blocks(output):
10821191 with tracecontext (output , 4 ):
10831192 output .append (5 )
10841193
1194+ @async_jump_test (3 , 5 , [1 , 2 , - 2 ], (ValueError , 'into' ))
1195+ async def test_no_jump_between_async_with_blocks (output ):
1196+ output .append (1 )
1197+ async with asynctracecontext (output , 2 ):
1198+ output .append (3 )
1199+ async with asynctracecontext (output , 4 ):
1200+ output .append (5 )
1201+
10851202 @jump_test (5 , 7 , [2 , 4 ], (ValueError , 'finally' ))
10861203 def test_no_jump_over_return_out_of_finally_block (output ):
10871204 try :
0 commit comments