1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# run-async
# Async coroutines capturing variables more than one level up. Calling an async
# function builds a coroutine (a separate frame-setup path from a sync call), so
# this exercises transitive cell threading on the coroutine path too.
# === Async coroutine reads a variable two levels up ===
def outer(a):
def mid():
async def inner():
return a * 3
return inner()
return mid()
assert (await outer(7)) == 21 # pyright: ignore
# === Async coroutine writes a grandparent local via nonlocal ===
def counter():
n = 0
def mid():
async def bump():
nonlocal n
n += 1
return n
return bump
return mid()
c = counter()
assert (await c()) == 1 # pyright: ignore
assert (await c()) == 2 # pyright: ignore
# === Each async closure instance captures its own cell ===
def make(n):
def mid():
async def add(x):
return x + n
return add
return mid()
assert (await make(3)(10)) == 13 # pyright: ignore
assert (await make(5)(10)) == 15 # pyright: ignore