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
@@ -637,6 +653,19 @@ def run_test(self, func, jumpFrom, jumpTo, expected, error=None,
637653 sys .settrace (None )
638654 self .compare_jump_output (expected , output )
639655
656+ def run_async_test (self , func , jumpFrom , jumpTo , expected , error = None ,
657+ event = 'line' , decorated = False ):
658+ tracer = JumpTracer (func , jumpFrom , jumpTo , event , decorated )
659+ sys .settrace (tracer .trace )
660+ output = []
661+ if error is None :
662+ asyncio .run (func (output ))
663+ else :
664+ with self .assertRaisesRegex (* error ):
665+ asyncio .run (func (output ))
666+ sys .settrace (None )
667+ self .compare_jump_output (expected , output )
668+
640669 def jump_test (jumpFrom , jumpTo , expected , error = None , event = 'line' ):
641670 """Decorator that creates a test that makes a jump
642671 from one place to another in the following code.
@@ -649,6 +678,18 @@ def test(self):
649678 return test
650679 return decorator
651680
681+ def async_jump_test (jumpFrom , jumpTo , expected , error = None , event = 'line' ):
682+ """Decorator that creates a test that makes a jump
683+ from one place to another in the following asynchronous code.
684+ """
685+ def decorator (func ):
686+ @wraps (func )
687+ def test (self ):
688+ self .run_async_test (func , jumpFrom , jumpTo , expected ,
689+ error = error , event = event , decorated = True )
690+ return test
691+ return decorator
692+
652693 ## The first set of 'jump' tests are for things that are allowed:
653694
654695 @jump_test (1 , 3 , [3 ])
@@ -744,12 +785,24 @@ def test_jump_forwards_out_of_with_block(output):
744785 output .append (2 )
745786 output .append (3 )
746787
788+ @async_jump_test (2 , 3 , [1 , 3 ])
789+ async def test_jump_forwards_out_of_async_with_block (output ):
790+ async with asynctracecontext (output , 1 ):
791+ output .append (2 )
792+ output .append (3 )
793+
747794 @jump_test (3 , 1 , [1 , 2 , 1 , 2 , 3 , - 2 ])
748795 def test_jump_backwards_out_of_with_block (output ):
749796 output .append (1 )
750797 with tracecontext (output , 2 ):
751798 output .append (3 )
752799
800+ @async_jump_test (3 , 1 , [1 , 2 , 1 , 2 , 3 , - 2 ])
801+ async def test_jump_backwards_out_of_async_with_block (output ):
802+ output .append (1 )
803+ async with asynctracecontext (output , 2 ):
804+ output .append (3 )
805+
753806 @jump_test (2 , 5 , [5 ])
754807 def test_jump_forwards_out_of_try_finally_block (output ):
755808 try :
@@ -813,6 +866,14 @@ def test_jump_across_with(output):
813866 with tracecontext (output , 4 ):
814867 output .append (5 )
815868
869+ @async_jump_test (2 , 4 , [1 , 4 , 5 , - 4 ])
870+ async def test_jump_across_async_with (output ):
871+ output .append (1 )
872+ async with asynctracecontext (output , 2 ):
873+ output .append (3 )
874+ async with asynctracecontext (output , 4 ):
875+ output .append (5 )
876+
816877 @jump_test (4 , 5 , [1 , 3 , 5 , 6 ])
817878 def test_jump_out_of_with_block_within_for_block (output ):
818879 output .append (1 )
@@ -822,6 +883,15 @@ def test_jump_out_of_with_block_within_for_block(output):
822883 output .append (5 )
823884 output .append (6 )
824885
886+ @async_jump_test (4 , 5 , [1 , 3 , 5 , 6 ])
887+ async def test_jump_out_of_async_with_block_within_for_block (output ):
888+ output .append (1 )
889+ for i in [1 ]:
890+ async with asynctracecontext (output , 3 ):
891+ output .append (4 )
892+ output .append (5 )
893+ output .append (6 )
894+
825895 @jump_test (4 , 5 , [1 , 2 , 3 , 5 , - 2 , 6 ])
826896 def test_jump_out_of_with_block_within_with_block (output ):
827897 output .append (1 )
@@ -831,6 +901,15 @@ def test_jump_out_of_with_block_within_with_block(output):
831901 output .append (5 )
832902 output .append (6 )
833903
904+ @async_jump_test (4 , 5 , [1 , 2 , 3 , 5 , - 2 , 6 ])
905+ async def test_jump_out_of_async_with_block_within_with_block (output ):
906+ output .append (1 )
907+ with tracecontext (output , 2 ):
908+ async with asynctracecontext (output , 3 ):
909+ output .append (4 )
910+ output .append (5 )
911+ output .append (6 )
912+
834913 @jump_test (5 , 6 , [2 , 4 , 6 , 7 ])
835914 def test_jump_out_of_with_block_within_finally_block (output ):
836915 try :
@@ -841,6 +920,16 @@ def test_jump_out_of_with_block_within_finally_block(output):
841920 output .append (6 )
842921 output .append (7 )
843922
923+ @async_jump_test (5 , 6 , [2 , 4 , 6 , 7 ])
924+ async def test_jump_out_of_async_with_block_within_finally_block (output ):
925+ try :
926+ output .append (2 )
927+ finally :
928+ async with asynctracecontext (output , 4 ):
929+ output .append (5 )
930+ output .append (6 )
931+ output .append (7 )
932+
844933 @jump_test (8 , 11 , [1 , 3 , 5 , 11 , 12 ])
845934 def test_jump_out_of_complex_nested_blocks (output ):
846935 output .append (1 )
@@ -864,6 +953,14 @@ def test_jump_out_of_with_assignment(output):
864953 output .append (4 )
865954 output .append (5 )
866955
956+ @async_jump_test (3 , 5 , [1 , 2 , 5 ])
957+ async def test_jump_out_of_async_with_assignment (output ):
958+ output .append (1 )
959+ async with asynctracecontext (output , 2 ) \
960+ as x :
961+ output .append (4 )
962+ output .append (5 )
963+
867964 @jump_test (3 , 6 , [1 , 6 , 8 , 9 ])
868965 def test_jump_over_return_in_try_finally_block (output ):
869966 output .append (1 )
@@ -982,12 +1079,24 @@ def test_no_jump_forwards_into_with_block(output):
9821079 with tracecontext (output , 2 ):
9831080 output .append (3 )
9841081
1082+ @async_jump_test (1 , 3 , [], (ValueError , 'into' ))
1083+ async def test_no_jump_forwards_into_async_with_block (output ):
1084+ output .append (1 )
1085+ async with asynctracecontext (output , 2 ):
1086+ output .append (3 )
1087+
9851088 @jump_test (3 , 2 , [1 , 2 , - 1 ], (ValueError , 'into' ))
9861089 def test_no_jump_backwards_into_with_block (output ):
9871090 with tracecontext (output , 1 ):
9881091 output .append (2 )
9891092 output .append (3 )
9901093
1094+ @async_jump_test (3 , 2 , [1 , 2 , - 1 ], (ValueError , 'into' ))
1095+ async def test_no_jump_backwards_into_async_with_block (output ):
1096+ async with asynctracecontext (output , 1 ):
1097+ output .append (2 )
1098+ output .append (3 )
1099+
9911100 @jump_test (1 , 3 , [], (ValueError , 'into' ))
9921101 def test_no_jump_forwards_into_try_finally_block (output ):
9931102 output .append (1 )
@@ -1068,6 +1177,14 @@ def test_no_jump_between_with_blocks(output):
10681177 with tracecontext (output , 4 ):
10691178 output .append (5 )
10701179
1180+ @async_jump_test (3 , 5 , [1 , 2 , - 2 ], (ValueError , 'into' ))
1181+ async def test_no_jump_between_async_with_blocks (output ):
1182+ output .append (1 )
1183+ async with asynctracecontext (output , 2 ):
1184+ output .append (3 )
1185+ async with asynctracecontext (output , 4 ):
1186+ output .append (5 )
1187+
10711188 @jump_test (7 , 4 , [1 , 6 ], (ValueError , 'into' ))
10721189 def test_no_jump_into_for_block_before_else (output ):
10731190 output .append (1 )
0 commit comments