Skip to content

Commit 773573e

Browse files
[3.7] bpo-33041: Add tests for jumps in/out of 'async with' blocks. (GH-6110). (GH-6140)
(cherry picked from commit bc300ce) Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
1 parent 429ca44 commit 773573e

1 file changed

Lines changed: 117 additions & 0 deletions

File tree

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
@@ -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

Comments
 (0)