8000 bpo-39965: Correctly raise SyntaxError if await is used outside async… · python/cpython@9023581 · GitHub
[go: up one dir, main page]

Skip to content

Commit 9023581

Browse files
authored
bpo-39965: Correctly raise SyntaxError if await is used outside async functions when PyCF_ALLOW_TOP_LEVEL_AWAIT is set (GH-19010)
1 parent 33238ec commit 9023581

File tree

3 files changed

+49
-4
lines changed

3 files changed

+49
-4
lines changed

Lib/test/test_builtin.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -421,6 +421,44 @@ async def arange(n):
421421
finally:
422422
asyncio.set_event_loop_policy(policy)
423423

424+
def test_compile_top_level_await_invalid_cases(self):
425+
# helper function just to check we can run top=level async-for
426+
async def arange(n):
427+
for i in range(n):
428+
yield i
429+
430+
modes = ('single', 'exec')
431+
code_samples = [
432+
'''def f(): await arange(10)\n''',
433+
'''def f(): [x async for x in arange(10)]\n''',
434+
'''def f(): [await x async for x in arange(10)]\n''',
435+
'''def f():
436+
async for i in arange(1):
437+
a = 1
438+
''',
439+
'''def f():
440+
async with asyncio.Lock() as l:
441+
a = 1
442+
'''
443+
]
444+
policy = maybe_get_event_loop_policy()
445+
try:
446+
for mode, code_sample in product(modes, code_samples):
447+
source = dedent(code_sample)
448+
with self.assertRaises(
449+
SyntaxError, msg=f"source={source} mode={mode}"):
450+
compile(source, '?', mode)
451+
452+
with self.assertRaises(
453+
SyntaxError, msg=f"source={source} mode={mode}"):
454+
co = compile(source,
455+
'?',
456+
mode,
457+
flags=ast.PyCF_ALLOW_TOP_LEVEL_AWAIT)
458+
finally:
459+
asyncio.set_event_loop_policy(policy)
460+
461+
424462
def test_compile_async_generator(self):
425463
"""
426464
With the PyCF_ALLOW_TOP_LEVEL_AWAIT flag added in 3.8, we want to
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Correctly raise ``SyntaxError`` if *await* is used inside non-async
2+
functions and ``PyCF_ALLOW_TOP_LEVEL_AWAIT`` is set (like in the asyncio
3+
REPL). Patch by Pablo Galindo.

Python/compile.c

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,10 @@
4141
#define COMP_SETCOMP 2
4242
#define COMP_DICTCOMP 3
4343

44+
#define IS_TOP_LEVEL_AWAIT(c) ( \
45+
(c->c_flags->cf_flags & PyCF_ALLOW_TOP_LEVEL_AWAIT) \
46+
&& (c->u->u_ste->ste_type == ModuleBlock))
47+
4448
struct instr {
4549
unsigned i_jabs : 1;
4650
unsigned i_jrel : 1;
@@ -2743,7 +2747,7 @@ static int
27432747
compiler_async_for(struct compiler *c, stmt_ty s)
27442748
{
27452749
basicblock *start, *except, *end;
2746-
if (c->c_flags->cf_flags & PyCF_ALLOW_TOP_LEVEL_AWAIT){
2750+
if (IS_TOP_LEVEL_AWAIT(c)){
27472751
c->u->u_ste->ste_coroutine = 1;
27482752
} else if (c->u->u_scope_type != COMPILER_SCOPE_ASYNC_FUNCTION) {
27492753
return compiler_error(c, "'async for' outside async function");
@@ -4789,7 +4793,7 @@ compiler_async_with(struct compiler *c, stmt_ty s, int pos)
47894793
withitem_ty item = asdl_seq_GET(s->v.AsyncWith.items, pos);
47904794

47914795
assert(s->kind == AsyncWith_kind);
4792-
if (c->c_flags->cf_flags & PyCF_ALLOW_TOP_LEVEL_AWAIT){
4796+
if (IS_TOP_LEVEL_AWAIT(c)){
47934797
c->u->u_ste->ste_coroutine = 1;
47944798
} else if (c->u->u_scope_type != COMPILER_SCOPE_ASYNC_FUNCTION){
47954799
return compiler_error(c, "'async with' outside async function");
@@ -5007,7 +5011,7 @@ compiler_visit_expr1(struct compiler *c, expr_ty e)
50075011
ADDOP(c, YIELD_FROM);
50085012
break;
50095013
case Await_kind:
5010-
if (!(c->c_flags->cf_flags & PyCF_ALLOW_TOP_LEVEL_AWAIT)){
5014+
if (!IS_TOP_LEVEL_AWAIT(c)){
50115015
if (c->u->u_ste->ste_type != FunctionBlock){
50125016
return compiler_error(c, "'await' outside function");
50135017
}
@@ -5836,7 +5840,7 @@ compute_code_flags(struct compiler *c)
58365840
/* (Only) inherit compilerflags in PyCF_MASK */
58375841
flags |= (c->c_flags->cf_flags & PyCF_MASK);
58385842

5839-
if ((c->c_flags->cf_flags & PyCF_ALLOW_TOP_LEVEL_AWAIT) &&
5843+
if ((IS_TOP_LEVEL_AWAIT(c)) &&
58405844
ste->ste_coroutine &&
58415845
!ste->ste_generator) {
58425846
flags |= CO_COROUTINE;

0 commit comments

Comments
 (0)
0