Skip to content

Commit 7b80a92

Browse files
committed
Add tests for async comprehensions in class and module scopes.
1 parent 7f0ccd6 commit 7b80a92

2 files changed

Lines changed: 72 additions & 1 deletion

File tree

Lib/test/test_builtin.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -542,6 +542,9 @@ async def sleep(delay, result=None):
542542
'''a = [x async for x in (x async for x in arange(5))][1]''',
543543
'''a, = [1 for x in {x async for x in arange(1)}]''',
544544
'''a = [await sleep(0, x) async for x in arange(2)][1]''',
545+
'''a = [await sleep(0, 1) for _ in [0]][0]''',
546+
'''a = {await sleep(0, 1) for _ in [0]}.pop()''',
547+
'''a = {0: await sleep(0, 1) for _ in [0]}[0]''',
545548
# gh-121637: Make sure we correctly handle the case where the
546549
# async code is optimized away
547550
'''assert not await sleep(0); a = 1''',
@@ -610,7 +613,17 @@ async def __aexit__(self, *exc_info):
610613
'''def f():
611614
async with Lock() as l:
612615
a = 1
613-
'''
616+
''',
617+
'''class C:
618+
[await x for x in y]
619+
''',
620+
'''class C:
621+
[x async for x in arange(10)]
622+
''',
623+
'''async def f():
624+
class C:
625+
[await x for x in y]
626+
''',
614627
]
615628
for mode, code_sample in product(modes, code_samples):
616629
source = dedent(code_sample)

Lib/test/test_coroutines.py

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -407,6 +407,64 @@ async def bar():
407407
with self.subTest(code=code), self.assertRaises(SyntaxError):
408408
compile(code, "<test>", "exec")
409409

410+
def test_async_comprehension_scope(self):
411+
# List/set/dict comprehensions with await or async for are allowed
412+
# only in async functions, or at module level with top-level await.
413+
allowed = [
414+
"async def f():\n [await x for x in y]",
415+
"async def f():\n {await x for x in y}",
416+
"async def f():\n {k: await x for k, x in y}",
417+
"async def f():\n [x async for x in y]",
418+
"async def f():\n {x async for x in y}",
419+
"async def f():\n {k: x async for k, x in y}",
420+
"async def f():\n [[await x for x in y] for y in z]",
421+
]
422+
for code in allowed:
423+
with self.subTest(code=code):
424+
compile(code, "<test>", "exec")
425+
426+
# Generator expressions with await are async genexps and may appear
427+
# outside async functions.
428+
for code in [
429+
"(await x for x in y)",
430+
"def f():\n (await x for x in y)",
431+
"class C:\n (await x for x in y)",
432+
]:
433+
with self.subTest(code=code):
434+
compile(code, "<test>", "exec")
435+
436+
err = "asynchronous comprehension outside of an asynchronous function"
437+
invalid = [
438+
"[await x for x in y]",
439+
"{await x for x in y}",
440+
"{k: await x for k, x in y}",
441+
"[x async for x in y]",
442+
"{x async for x in y}",
443+
"{k: x async for k, x in y}",
444+
"[[await x for x in y] for y in z]",
445+
"[[x async for x in y] for y in z]",
446+
"def f():\n [await x for x in y]",
447+
"def f():\n [x async for x in y]",
448+
"async def f():\n def g():\n [await x for x in y]",
449+
"class C:\n [await x for x in y]",
450+
"class C:\n {await x for x in y}",
451+
"class C:\n {k: await x for k, x in y}",
452+
"class C:\n [x async for x in y]",
453+
"class C:\n [[await x for x in y] for y in z]",
454+
"async def f():\n class C:\n x = [await y for y in z]",
455+
"async def f():\n class C:\n x = [y async for y in z]",
456+
]
457+
for code in invalid:
458+
with self.subTest(code=code):
459+
support.check_syntax_error(self, code, err)
460+
461+
support.check_syntax_error(
462+
self, "await x", "'await' outside function")
463+
support.check_syntax_error(
464+
self, "class C:\n await x", "'await' outside function")
465+
support.check_syntax_error(
466+
self, "def f():\n await x", "'await' outside async function")
467+
410468
def test_badsyntax_2(self):
411469
samples = [
412470
"""def foo():

0 commit comments

Comments
 (0)