77import gc
88from functools import wraps
99
10+
1011class tracecontext :
11- """Contex manager that traces its enter and exit."""
12+ """Context manager that traces its enter and exit."""
1213 def __init__ (self , output , value ):
1314 self .output = output
1415 self .value = value
@@ -19,6 +20,36 @@ def __enter__(self):
1920 def __exit__ (self , * exc_info ):
2021 self .output .append (- self .value )
2122
23+ class asynctracecontext :
24+ """Asynchronous context manager that traces its aenter and aexit."""
25+ def __init__ (self , output , value ):
26+ self .output = output
27+ self .value = value
28+
29+ async def __aenter__ (self ):
30+ self .output .append (self .value )
31+
32+ async def __aexit__ (self , * exc_info ):
33+ self .output .append (- self .value )
34+
35+ def asyncio_run (main ):
36+ import asyncio
37+ import asyncio .events
38+ import asyncio .coroutines
39+ assert asyncio .events ._get_running_loop () is None
40+ assert asyncio .coroutines .iscoroutine (main )
41+ loop = asyncio .events .new_event_loop ()
42+ try :
43+ asyncio .events .set_event_loop (loop )
44+ return loop .run_until_complete (main )
45+ finally :
46+ try :
47+ loop .run_until_complete (loop .shutdown_asyncgens ())
48+ finally :
49+ asyncio .events .set_event_loop (None )
50+ loop .close ()
51+
52+
2253# A very basic example. If this fails, we're in deep trouble.
2354def basic ():
2455 return 1
@@ -591,6 +622,19 @@ def run_test(self, func, jumpFrom, jumpTo, expected, error=None,
591622 sys .settrace (None )
592623 self .compare_jump_output (expected , output )
593624
625+ def run_async_test (self , func , jumpFrom , jumpTo , expected , error = None ,
626+ event = 'line' , decorated = False ):
627+ tracer = JumpTracer (func , jumpFrom , jumpTo , event , decorated )
628+ sys .settrace (tracer .trace )
629+ output = []
630+ if error is None :
631+ asyncio_run (func (output ))
632+ else :
633+ with self .assertRaisesRegex (* error ):
634+ asyncio_run (func (output ))
635+ sys .settrace (None )
636+ self .compare_jump_output (expected , output )
637+
594638 def jump_test (jumpFrom , jumpTo , expected , error = None , event = 'line' ):
595639 """Decorator that creates a test that makes a jump
596640 from one place to another in the following code.
@@ -603,6 +647,18 @@ def test(self):
603647 return test
604648 return decorator
605649
650+ def async_jump_test (jumpFrom , jumpTo , expected , error = None , event = 'line' ):
651+ """Decorator that creates a test that makes a jump
652+ from one place to another in the following asynchronous code.
653+ """
654+ def decorator (func ):
655+ @wraps (func )
656+ def test (self ):
657+ self .run_async_test (func , jumpFrom , jumpTo , expected ,
658+ error = error , event = event , decorated = True )
659+ return test
660+ return decorator
661+
606662 ## The first set of 'jump' tests are for things that are allowed:
607663
608664 @jump_test (1 , 3 , [3 ])
@@ -698,12 +754,24 @@ def test_jump_forwards_out_of_with_block(output):
698754 output .append (2 )
699755 output .append (3 )
700756
757+ @async_jump_test (2 , 3 , [1 , 3 ])
758+ async def test_jump_forwards_out_of_async_with_block (output ):
759+ async with asynctracecontext (output , 1 ):
760+ output .append (2 )
761+ output .append (3 )
762+
701763 @jump_test (3 , 1 , [1 , 2 , 1 , 2 , 3 , - 2 ])
702764 def test_jump_backwards_out_of_with_block (output ):
703765 output .append (1 )
704766 with tracecontext (output , 2 ):
705767 output .append (3 )
706768
769+ @async_jump_test (3 , 1 , [1 , 2 , 1 , 2 , 3 , - 2 ])
770+ async def test_jump_backwards_out_of_async_with_block (output ):
771+ output .append (1 )
772+ async with asynctracecontext (output , 2 ):
773+ output .append (3 )
774+
707775 @jump_test (2 , 5 , [5 ])
708776 def test_jump_forwards_out_of_try_finally_block (output ):
709777 try :
@@ -767,6 +835,14 @@ def test_jump_across_with(output):
767835 with tracecontext (output , 4 ):
768836 output .append (5 )
769837
838+ @async_jump_test (2 , 4 , [1 , 4 , 5 , - 4 ])
839+ async def test_jump_across_async_with (output ):
840+ output .append (1 )
841+ async with asynctracecontext (output , 2 ):
842+ output .append (3 )
843+ async with asynctracecontext (output , 4 ):
844+ output .append (5 )
845+
770846 @jump_test (4 , 5 , [1 , 3 , 5 , 6 ])
771847 def test_jump_out_of_with_block_within_for_block (output ):
772848 output .append (1 )
@@ -776,6 +852,15 @@ def test_jump_out_of_with_block_within_for_block(output):
776852 output .append (5 )
777853 output .append (6 )
778854
855+ @async_jump_test (4 , 5 , [1 , 3 , 5 , 6 ])
856+ async def test_jump_out_of_async_with_block_within_for_block (output ):
857+ output .append (1 )
858+ for i in [1 ]:
859+ async with asynctracecontext (output , 3 ):
860+ output .append (4 )
861+ output .append (5 )
862+ output .append (6 )
863+
779864 @jump_test (4 , 5 , [1 , 2 , 3 , 5 , - 2 , 6 ])
780865 def test_jump_out_of_with_block_within_with_block (output ):
781866 output .append (1 )
@@ -785,6 +870,15 @@ def test_jump_out_of_with_block_within_with_block(output):
785870 output .append (5 )
786871 output .append (6 )
787872
873+ @async_jump_test (4 , 5 , [1 , 2 , 3 , 5 , - 2 , 6 ])
874+ async def test_jump_out_of_async_with_block_within_with_block (output ):
875+ output .append (1 )
876+ with tracecontext (output , 2 ):
877+ async with asynctracecontext (output , 3 ):
878+ output .append (4 )
879+ output .append (5 )
880+ output .append (6 )
881+
788882 @jump_test (5 , 6 , [2 , 4 , 6 , 7 ])
789883 def test_jump_out_of_with_block_within_finally_block (output ):
790884 try :
@@ -795,6 +889,16 @@ def test_jump_out_of_with_block_within_finally_block(output):
795889 output .append (6 )
796890 output .append (7 )
797891
892+ @async_jump_test (5 , 6 , [2 , 4 , 6 , 7 ])
893+ async def test_jump_out_of_async_with_block_within_finally_block (output ):
894+ try :
895+ output .append (2 )
896+ finally :
897+ async with asynctracecontext (output , 4 ):
898+ output .append (5 )
899+ output .append (6 )
900+ output .append (7 )
901+
798902 @jump_test (8 , 11 , [1 , 3 , 5 , 11 , 12 ])
799903 def test_jump_out_of_complex_nested_blocks (output ):
800904 output .append (1 )
@@ -818,6 +922,14 @@ def test_jump_out_of_with_assignment(output):
818922 output .append (4 )
819923 output .append (5 )
820924
925+ @async_jump_test (3 , 5 , [1 , 2 , 5 ])
926+ async def test_jump_out_of_async_with_assignment (output ):
927+ output .append (1 )
928+ async with asynctracecontext (output , 2 ) \
929+ as x :
930+ output .append (4 )
931+ output .append (5 )
932+
821933 @jump_test (3 , 6 , [1 , 6 , 8 , 9 ])
822934 def test_jump_over_return_in_try_finally_block (output ):
823935 output .append (1 )
@@ -936,12 +1048,24 @@ def test_no_jump_forwards_into_with_block(output):
9361048 with tracecontext (output , 2 ):
9371049 output .append (3 )
9381050
1051+ @async_jump_test (1 , 3 , [], (ValueError , 'into' ))
1052+ async def test_no_jump_forwards_into_async_with_block (output ):
1053+ output .append (1 )
1054+ async with asynctracecontext (output , 2 ):
1055+ output .append (3 )
1056+
9391057 @jump_test (3 , 2 , [1 , 2 , - 1 ], (ValueError , 'into' ))
9401058 def test_no_jump_backwards_into_with_block (output ):
9411059 with tracecontext (output , 1 ):
9421060 output .append (2 )
9431061 output .append (3 )
9441062
1063+ @async_jump_test (3 , 2 , [1 , 2 , - 1 ], (ValueError , 'into' ))
1064+ async def test_no_jump_backwards_into_async_with_block (output ):
1065+ async with asynctracecontext (output , 1 ):
1066+ output .append (2 )
1067+ output .append (3 )
1068+
9451069 @jump_test (1 , 3 , [], (ValueError , 'into' ))
9461070 def test_no_jump_forwards_into_try_finally_block (output ):
9471071 output .append (1 )
@@ -1022,6 +1146,14 @@ def test_no_jump_between_with_blocks(output):
10221146 with tracecontext (output , 4 ):
10231147 output .append (5 )
10241148
1149+ @async_jump_test (3 , 5 , [1 , 2 , - 2 ], (ValueError , 'into' ))
1150+ async def test_no_jump_between_async_with_blocks (output ):
1151+ output .append (1 )
1152+ async with asynctracecontext (output , 2 ):
1153+ output .append (3 )
1154+ async with asynctracecontext (output , 4 ):
1155+ output .append (5 )
1156+
10251157 @jump_test (7 , 4 , [1 , 6 ], (ValueError , 'into' ))
10261158 def test_no_jump_into_for_block_before_else (output ):
10271159 output .append (1 )
0 commit comments