summaryrefslogtreecommitdiffstats
path: root/Python/compile.c
diff options
context:
space:
mode:
authorYury Selivanov <yselivanov@sprymix.com>2015-05-12 02:57:16 (GMT)
committerYury Selivanov <yselivanov@sprymix.com>2015-05-12 02:57:16 (GMT)
commit7544508f0245173bff5866aa1598c8f6cce1fc5f (patch)
treebf80850d9cd46fc811f04b8c2484fb50775c697d /Python/compile.c
parent4e6bf4b3da03b132b0698f30ee931a350585b117 (diff)
downloadcpython-7544508f0245173bff5866aa1598c8f6cce1fc5f.zip
cpython-7544508f0245173bff5866aa1598c8f6cce1fc5f.tar.gz
cpython-7544508f0245173bff5866aa1598c8f6cce1fc5f.tar.bz2
PEP 0492 -- Coroutines with async and await syntax. Issue #24017.
Diffstat (limited to 'Python/compile.c')
-rw-r--r--Python/compile.c288
1 files changed, 273 insertions, 15 deletions
diff --git a/Python/compile.c b/Python/compile.c
index efd4807..7820f39 100644
--- a/Python/compile.c
+++ b/Python/compile.c
@@ -92,6 +92,7 @@ enum {
COMPILER_SCOPE_MODULE,
COMPILER_SCOPE_CLASS,
COMPILER_SCOPE_FUNCTION,
+ COMPILER_SCOPE_ASYNC_FUNCTION,
COMPILER_SCOPE_LAMBDA,
COMPILER_SCOPE_COMPREHENSION,
};
@@ -193,6 +194,8 @@ static int inplace_binop(struct compiler *, operator_ty);
static int expr_constant(struct compiler *, expr_ty);
static int compiler_with(struct compiler *, stmt_ty, int);
+static int compiler_async_with(struct compiler *, stmt_ty, int);
+static int compiler_async_for(struct compiler *, stmt_ty);
static int compiler_call_helper(struct compiler *c, Py_ssize_t n,
asdl_seq *args,
asdl_seq *keywords);
@@ -673,7 +676,9 @@ compiler_set_qualname(struct compiler *c)
parent = (struct compiler_unit *)PyCapsule_GetPointer(capsule, CAPSULE_NAME);
assert(parent);
- if (u->u_scope_type == COMPILER_SCOPE_FUNCTION || u->u_scope_type == COMPILER_SCOPE_CLASS) {
+ if (u->u_scope_type == COMPILER_SCOPE_FUNCTION
+ || u->u_scope_type == COMPILER_SCOPE_ASYNC_FUNCTION
+ || u->u_scope_type == COMPILER_SCOPE_CLASS) {
assert(u->u_name);
mangled = _Py_Mangle(parent->u_private, u->u_name);
if (!mangled)
@@ -687,6 +692,7 @@ compiler_set_qualname(struct compiler *c)
if (!force_global) {
if (parent->u_scope_type == COMPILER_SCOPE_FUNCTION
+ || parent->u_scope_type == COMPILER_SCOPE_ASYNC_FUNCTION
|| parent->u_scope_type == COMPILER_SCOPE_LAMBDA) {
dot_locals_str = _PyUnicode_FromId(&dot_locals);
if (dot_locals_str == NULL)
@@ -927,7 +933,9 @@ PyCompile_OpcodeStackEffect(int opcode, int oparg)
return 0;
case SETUP_WITH:
return 7;
- case WITH_CLEANUP:
+ case WITH_CLEANUP_START:
+ return 1;
+ case WITH_CLEANUP_FINISH:
return -1; /* XXX Sometimes more */
case RETURN_VALUE:
return -1;
@@ -1048,6 +1056,16 @@ PyCompile_OpcodeStackEffect(int opcode, int oparg)
return -1;
case DELETE_DEREF:
return 0;
+ case GET_AWAITABLE:
+ return 0;
+ case SETUP_ASYNC_WITH:
+ return 6;
+ case BEFORE_ASYNC_WITH:
+ return 1;
+ case GET_AITER:
+ return 0;
+ case GET_ANEXT:
+ return 1;
default:
return PY_INVALID_STACK_EFFECT;
}
@@ -1642,19 +1660,43 @@ error:
}
static int
-compiler_function(struct compiler *c, stmt_ty s)
+compiler_function(struct compiler *c, stmt_ty s, int is_async)
{
PyCodeObject *co;
PyObject *qualname, *first_const = Py_None;
- arguments_ty args = s->v.FunctionDef.args;
- expr_ty returns = s->v.FunctionDef.returns;
- asdl_seq* decos = s->v.FunctionDef.decorator_list;
+ arguments_ty args;
+ expr_ty returns;
+ identifier name;
+ asdl_seq* decos;
+ asdl_seq *body;
stmt_ty st;
Py_ssize_t i, n, arglength;
int docstring, kw_default_count = 0;
int num_annotations;
+ int scope_type;
+
+
+ if (is_async) {
+ assert(s->kind == AsyncFunctionDef_kind);
+
+ args = s->v.AsyncFunctionDef.args;
+ returns = s->v.AsyncFunctionDef.returns;
+ decos = s->v.AsyncFunctionDef.decorator_list;
+ name = s->v.AsyncFunctionDef.name;
+ body = s->v.AsyncFunctionDef.body;
+
+ scope_type = COMPILER_SCOPE_ASYNC_FUNCTION;
+ } else {
+ assert(s->kind == FunctionDef_kind);
+
+ args = s->v.FunctionDef.args;
+ returns = s->v.FunctionDef.returns;
+ decos = s->v.FunctionDef.decorator_list;
+ name = s->v.FunctionDef.name;
+ body = s->v.FunctionDef.body;
- assert(s->kind == FunctionDef_kind);
+ scope_type = COMPILER_SCOPE_FUNCTION;
+ }
if (!compiler_decorators(c, decos))
return 0;
@@ -1672,12 +1714,12 @@ compiler_function(struct compiler *c, stmt_ty s)
return 0;
assert((num_annotations & 0xFFFF) == num_annotations);
- if (!compiler_enter_scope(c, s->v.FunctionDef.name,
- COMPILER_SCOPE_FUNCTION, (void *)s,
+ if (!compiler_enter_scope(c, name,
+ scope_type, (void *)s,
s->lineno))
return 0;
- st = (stmt_ty)asdl_seq_GET(s->v.FunctionDef.body, 0);
+ st = (stmt_ty)asdl_seq_GET(body, 0);
docstring = compiler_isdocstring(st);
if (docstring && c->c_optimize < 2)
first_const = st->v.Expr.value->v.Str.s;
@@ -1688,10 +1730,10 @@ compiler_function(struct compiler *c, stmt_ty s)
c->u->u_argcount = asdl_seq_LEN(args->args);
c->u->u_kwonlyargcount = asdl_seq_LEN(args->kwonlyargs);
- n = asdl_seq_LEN(s->v.FunctionDef.body);
+ n = asdl_seq_LEN(body);
/* if there was a docstring, we need to skip the first statement */
for (i = docstring; i < n; i++) {
- st = (stmt_ty)asdl_seq_GET(s->v.FunctionDef.body, i);
+ st = (stmt_ty)asdl_seq_GET(body, i);
VISIT_IN_SCOPE(c, stmt, st);
}
co = assemble(c, 1);
@@ -1711,12 +1753,19 @@ compiler_function(struct compiler *c, stmt_ty s)
Py_DECREF(qualname);
Py_DECREF(co);
+ if (is_async) {
+ co->co_flags |= CO_COROUTINE;
+ /* An async function is always a generator, even
+ if there is no 'yield' expressions in it. */
+ co->co_flags |= CO_GENERATOR;
+ }
+
/* decorators */
for (i = 0; i < asdl_seq_LEN(decos); i++) {
ADDOP_I(c, CALL_FUNCTION, 1);
}
- return compiler_nameop(c, s->v.FunctionDef.name, Store);
+ return compiler_nameop(c, name, Store);
}
static int
@@ -1989,6 +2038,92 @@ compiler_for(struct compiler *c, stmt_ty s)
return 1;
}
+
+static int
+compiler_async_for(struct compiler *c, stmt_ty s)
+{
+ static PyObject *stopiter_error = NULL;
+ basicblock *try, *except, *end, *after_try, *try_cleanup,
+ *after_loop, *after_loop_else;
+
+ if (stopiter_error == NULL) {
+ stopiter_error = PyUnicode_InternFromString("StopAsyncIteration");
+ if (stopiter_error == NULL)
+ return 0;
+ }
+
+ try = compiler_new_block(c);
+ except = compiler_new_block(c);
+ end = compiler_new_block(c);
+ after_try = compiler_new_block(c);
+ try_cleanup = compiler_new_block(c);
+ after_loop = compiler_new_block(c);
+ after_loop_else = compiler_new_block(c);
+
+ if (try == NULL || except == NULL || end == NULL
+ || after_try == NULL || try_cleanup == NULL)
+ return 0;
+
+ ADDOP_JREL(c, SETUP_LOOP, after_loop);
+ if (!compiler_push_fblock(c, LOOP, try))
+ return 0;
+
+ VISIT(c, expr, s->v.AsyncFor.iter);
+ ADDOP(c, GET_AITER);
+ ADDOP_O(c, LOAD_CONST, Py_None, consts);
+ ADDOP(c, YIELD_FROM);
+
+ compiler_use_next_block(c, try);
+
+
+ ADDOP_JREL(c, SETUP_EXCEPT, except);
+ if (!compiler_push_fblock(c, EXCEPT, try))
+ return 0;
+
+ ADDOP(c, GET_ANEXT);
+ ADDOP_O(c, LOAD_CONST, Py_None, consts);
+ ADDOP(c, YIELD_FROM);
+ VISIT(c, expr, s->v.AsyncFor.target);
+ ADDOP(c, POP_BLOCK);
+ compiler_pop_fblock(c, EXCEPT, try);
+ ADDOP_JREL(c, JUMP_FORWARD, after_try);
+
+
+ compiler_use_next_block(c, except);
+ ADDOP(c, DUP_TOP);
+ ADDOP_O(c, LOAD_GLOBAL, stopiter_error, names);
+ ADDOP_I(c, COMPARE_OP, PyCmp_EXC_MATCH);
+ ADDOP_JABS(c, POP_JUMP_IF_FALSE, try_cleanup);
+
+ ADDOP(c, POP_TOP);
+ ADDOP(c, POP_TOP);
+ ADDOP(c, POP_TOP);
+ ADDOP(c, POP_EXCEPT); /* for SETUP_EXCEPT */
+ ADDOP(c, POP_BLOCK); /* for SETUP_LOOP */
+ ADDOP_JABS(c, JUMP_ABSOLUTE, after_loop_else);
+
+
+ compiler_use_next_block(c, try_cleanup);
+ ADDOP(c, END_FINALLY);
+
+ compiler_use_next_block(c, after_try);
+ VISIT_SEQ(c, stmt, s->v.AsyncFor.body);
+ ADDOP_JABS(c, JUMP_ABSOLUTE, try);
+
+ ADDOP(c, POP_BLOCK); /* for SETUP_LOOP */
+ compiler_pop_fblock(c, LOOP, try);
+
+ compiler_use_next_block(c, after_loop);
+ ADDOP_JABS(c, JUMP_ABSOLUTE, end);
+
+ compiler_use_next_block(c, after_loop_else);
+ VISIT_SEQ(c, stmt, s->v.For.orelse);
+
+ compiler_use_next_block(c, end);
+
+ return 1;
+}
+
static int
compiler_while(struct compiler *c, stmt_ty s)
{
@@ -2515,7 +2650,7 @@ compiler_visit_stmt(struct compiler *c, stmt_ty s)
switch (s->kind) {
case FunctionDef_kind:
- return compiler_function(c, s);
+ return compiler_function(c, s, 0);
case ClassDef_kind:
return compiler_class(c, s);
case Return_kind:
@@ -2594,7 +2729,14 @@ compiler_visit_stmt(struct compiler *c, stmt_ty s)
return compiler_continue(c);
case With_kind:
return compiler_with(c, s, 0);
+ case AsyncFunctionDef_kind:
+ return compiler_function(c, s, 1);
+ case AsyncWith_kind:
+ return compiler_async_with(c, s, 0);
+ case AsyncFor_kind:
+ return compiler_async_for(c, s);
}
+
return 1;
}
@@ -3471,6 +3613,102 @@ expr_constant(struct compiler *c, expr_ty e)
}
}
+
+/*
+ Implements the async with statement.
+
+ The semantics outlined in that PEP are as follows:
+
+ async with EXPR as VAR:
+ BLOCK
+
+ It is implemented roughly as:
+
+ context = EXPR
+ exit = context.__aexit__ # not calling it
+ value = await context.__aenter__()
+ try:
+ VAR = value # if VAR present in the syntax
+ BLOCK
+ finally:
+ if an exception was raised:
+ exc = copy of (exception, instance, traceback)
+ else:
+ exc = (None, None, None)
+ if not (await exit(*exc)):
+ raise
+ */
+static int
+compiler_async_with(struct compiler *c, stmt_ty s, int pos)
+{
+ basicblock *block, *finally;
+ withitem_ty item = asdl_seq_GET(s->v.AsyncWith.items, pos);
+
+ assert(s->kind == AsyncWith_kind);
+
+ block = compiler_new_block(c);
+ finally = compiler_new_block(c);
+ if (!block || !finally)
+ return 0;
+
+ /* Evaluate EXPR */
+ VISIT(c, expr, item->context_expr);
+
+ ADDOP(c, BEFORE_ASYNC_WITH);
+ ADDOP(c, GET_AWAITABLE);
+ ADDOP_O(c, LOAD_CONST, Py_None, consts);
+ ADDOP(c, YIELD_FROM);
+
+ ADDOP_JREL(c, SETUP_ASYNC_WITH, finally);
+
+ /* SETUP_ASYNC_WITH pushes a finally block. */
+ compiler_use_next_block(c, block);
+ if (!compiler_push_fblock(c, FINALLY_TRY, block)) {
+ return 0;
+ }
+
+ if (item->optional_vars) {
+ VISIT(c, expr, item->optional_vars);
+ }
+ else {
+ /* Discard result from context.__aenter__() */
+ ADDOP(c, POP_TOP);
+ }
+
+ pos++;
+ if (pos == asdl_seq_LEN(s->v.AsyncWith.items))
+ /* BLOCK code */
+ VISIT_SEQ(c, stmt, s->v.AsyncWith.body)
+ else if (!compiler_async_with(c, s, pos))
+ return 0;
+
+ /* End of try block; start the finally block */
+ ADDOP(c, POP_BLOCK);
+ compiler_pop_fblock(c, FINALLY_TRY, block);
+
+ ADDOP_O(c, LOAD_CONST, Py_None, consts);
+ compiler_use_next_block(c, finally);
+ if (!compiler_push_fblock(c, FINALLY_END, finally))
+ return 0;
+
+ /* Finally block starts; context.__exit__ is on the stack under
+ the exception or return information. Just issue our magic
+ opcode. */
+ ADDOP(c, WITH_CLEANUP_START);
+
+ ADDOP(c, GET_AWAITABLE);
+ ADDOP_O(c, LOAD_CONST, Py_None, consts);
+ ADDOP(c, YIELD_FROM);
+
+ ADDOP(c, WITH_CLEANUP_FINISH);
+
+ /* Finally block ends. */
+ ADDOP(c, END_FINALLY);
+ compiler_pop_fblock(c, FINALLY_END, finally);
+ return 1;
+}
+
+
/*
Implements the with statement from PEP 343.
@@ -3544,7 +3782,8 @@ compiler_with(struct compiler *c, stmt_ty s, int pos)
/* Finally block starts; context.__exit__ is on the stack under
the exception or return information. Just issue our magic
opcode. */
- ADDOP(c, WITH_CLEANUP);
+ ADDOP(c, WITH_CLEANUP_START);
+ ADDOP(c, WITH_CLEANUP_FINISH);
/* Finally block ends. */
ADDOP(c, END_FINALLY);
@@ -3595,6 +3834,8 @@ compiler_visit_expr(struct compiler *c, expr_ty e)
case Yield_kind:
if (c->u->u_ste->ste_type != FunctionBlock)
return compiler_error(c, "'yield' outside function");
+ if (c->u->u_scope_type == COMPILER_SCOPE_ASYNC_FUNCTION)
+ return compiler_error(c, "'yield' inside async function");
if (e->v.Yield.value) {
VISIT(c, expr, e->v.Yield.value);
}
@@ -3606,11 +3847,28 @@ compiler_visit_expr(struct compiler *c, expr_ty e)
case YieldFrom_kind:
if (c->u->u_ste->ste_type != FunctionBlock)
return compiler_error(c, "'yield' outside function");
+
+ if (c->u->u_scope_type == COMPILER_SCOPE_ASYNC_FUNCTION)
+ return compiler_error(c, "'yield from' inside async function");
+
VISIT(c, expr, e->v.YieldFrom.value);
ADDOP(c, GET_ITER);
ADDOP_O(c, LOAD_CONST, Py_None, consts);
ADDOP(c, YIELD_FROM);
break;
+ case Await_kind:
+ if (c->u->u_ste->ste_type != FunctionBlock)
+ return compiler_error(c, "'await' outside function");
+
+ /* this check won't be triggered while we have AWAIT token */
+ if (c->u->u_scope_type != COMPILER_SCOPE_ASYNC_FUNCTION)
+ return compiler_error(c, "'await' outside async function");
+
+ VISIT(c, expr, e->v.Await.value);
+ ADDOP(c, GET_AWAITABLE);
+ ADDOP_O(c, LOAD_CONST, Py_None, consts);
+ ADDOP(c, YIELD_FROM);
+ break;
case Compare_kind:
return compiler_compare(c, e);
case Call_kind: