Skip to content

Commit bc300ce

Browse files
bpo-33041: Add tests for jumps in/out of 'async with' blocks. (#6110)
1 parent bbf7bb7 commit bc300ce

File tree

1 file changed

+117
-0
lines changed

1 file changed

+117
-0
lines changed

‎Lib/test/test_sys_settrace.py‎

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
import difflib
77
import gc
88
from functools import wraps
9+
import asyncio
10+
911

1012
class 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.
2339
def 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

Comments
 (0)