Skip to content

Commit

Permalink
bpo-39965: Correctly raise SyntaxError if await is used outside async…
Browse files Browse the repository at this point in the history
… functions when PyCF_ALLOW_TOP_LEVEL_AWAIT is set (GH-19010)
  • Loading branch information
pablogsal committed Mar 15, 2020
1 parent 33238ec commit 9023581
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 4 deletions.
38 changes: 38 additions & 0 deletions Lib/test/test_builtin.py
Expand Up @@ -421,6 +421,44 @@ async def arange(n):
finally:
asyncio.set_event_loop_policy(policy)

def test_compile_top_level_await_invalid_cases(self):
# helper function just to check we can run top=level async-for
async def arange(n):
for i in range(n):
yield i

modes = ('single', 'exec')
code_samples = [
'''def f(): await arange(10)\n''',
'''def f(): [x async for x in arange(10)]\n''',
'''def f(): [await x async for x in arange(10)]\n''',
'''def f():
async for i in arange(1):
a = 1
''',
'''def f():
async with asyncio.Lock() as l:
a = 1
'''
]
policy = maybe_get_event_loop_policy()
try:
for mode, code_sample in product(modes, code_samples):
source = dedent(code_sample)
with self.assertRaises(
SyntaxError, msg=f"source={source} mode={mode}"):
compile(source, '?', mode)

with self.assertRaises(
SyntaxError, msg=f"source={source} mode={mode}"):
co = compile(source,
'?',
mode,
flags=ast.PyCF_ALLOW_TOP_LEVEL_AWAIT)
finally:
asyncio.set_event_loop_policy(policy)


def test_compile_async_generator(self):
"""
With the PyCF_ALLOW_TOP_LEVEL_AWAIT flag added in 3.8, we want to
Expand Down
@@ -0,0 +1,3 @@
Correctly raise ``SyntaxError`` if *await* is used inside non-async
functions and ``PyCF_ALLOW_TOP_LEVEL_AWAIT`` is set (like in the asyncio
REPL). Patch by Pablo Galindo.
12 changes: 8 additions & 4 deletions Python/compile.c
Expand Up @@ -41,6 +41,10 @@
#define COMP_SETCOMP 2
#define COMP_DICTCOMP 3

#define IS_TOP_LEVEL_AWAIT(c) ( \
(c->c_flags->cf_flags & PyCF_ALLOW_TOP_LEVEL_AWAIT) \
&& (c->u->u_ste->ste_type == ModuleBlock))

struct instr {
unsigned i_jabs : 1;
unsigned i_jrel : 1;
Expand Down Expand Up @@ -2743,7 +2747,7 @@ static int
compiler_async_for(struct compiler *c, stmt_ty s)
{
basicblock *start, *except, *end;
if (c->c_flags->cf_flags & PyCF_ALLOW_TOP_LEVEL_AWAIT){
if (IS_TOP_LEVEL_AWAIT(c)){
c->u->u_ste->ste_coroutine = 1;
} else if (c->u->u_scope_type != COMPILER_SCOPE_ASYNC_FUNCTION) {
return compiler_error(c, "'async for' outside async function");
Expand Down Expand Up @@ -4789,7 +4793,7 @@ compiler_async_with(struct compiler *c, stmt_ty s, int pos)
withitem_ty item = asdl_seq_GET(s->v.AsyncWith.items, pos);

assert(s->kind == AsyncWith_kind);
if (c->c_flags->cf_flags & PyCF_ALLOW_TOP_LEVEL_AWAIT){
if (IS_TOP_LEVEL_AWAIT(c)){
c->u->u_ste->ste_coroutine = 1;
} else if (c->u->u_scope_type != COMPILER_SCOPE_ASYNC_FUNCTION){
return compiler_error(c, "'async with' outside async function");
Expand Down Expand Up @@ -5007,7 +5011,7 @@ compiler_visit_expr1(struct compiler *c, expr_ty e)
ADDOP(c, YIELD_FROM);
break;
case Await_kind:
if (!(c->c_flags->cf_flags & PyCF_ALLOW_TOP_LEVEL_AWAIT)){
if (!IS_TOP_LEVEL_AWAIT(c)){
if (c->u->u_ste->ste_type != FunctionBlock){
return compiler_error(c, "'await' outside function");
}
Expand Down Expand Up @@ -5836,7 +5840,7 @@ compute_code_flags(struct compiler *c)
/* (Only) inherit compilerflags in PyCF_MASK */
flags |= (c->c_flags->cf_flags & PyCF_MASK);

if ((c->c_flags->cf_flags & PyCF_ALLOW_TOP_LEVEL_AWAIT) &&
if ((IS_TOP_LEVEL_AWAIT(c)) &&
ste->ste_coroutine &&
!ste->ste_generator) {
flags |= CO_COROUTINE;
Expand Down

0 comments on commit 9023581

Please sign in to comment.