summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorScott Shawcroft <scott@adafruit.com>2020-07-24 12:37:09 -0700
committerGitHub <noreply@github.com>2020-07-24 12:37:09 -0700
commita6e048686fe6999c4231582b7d9e69a7dc679257 (patch)
tree24d31afd3d901bb0cc15a000226af520a8628a6c
parenta5725941a038af87c36afe8bc6a5a5e983e597a5 (diff)
parente9b4e0bd35b6c3024a662580e547df62ab95acd1 (diff)
Merge pull request #3178 from WarriorOfWire/async_def_coroutine_sim
add coroutine behavior for generators
-rw-r--r--locale/circuitpython.pot6
-rw-r--r--mpy-cross/mpconfigport.h1
-rw-r--r--py/compile.c2
-rw-r--r--py/emitglue.c2
-rw-r--r--py/obj.h2
-rw-r--r--py/objgenerator.c19
-rw-r--r--py/runtime0.h1
-rw-r--r--tests/basics/async_coroutine.py13
-rw-r--r--tests/basics/async_coroutine.py.exp1
-rwxr-xr-xtests/run-tests2
10 files changed, 43 insertions, 6 deletions
diff --git a/locale/circuitpython.pot b/locale/circuitpython.pot
index d156b6868..4d3a87706 100644
--- a/locale/circuitpython.pot
+++ b/locale/circuitpython.pot
@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2020-07-17 18:03-0700\n"
+"POT-Creation-Date: 2020-07-21 18:43-0700\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -234,6 +234,10 @@ msgstr ""
msgid "'continue' outside loop"
msgstr ""
+#: py/objgenerator.c
+msgid "'coroutine' object is not an iterator"
+msgstr ""
+
#: py/compile.c
msgid "'data' requires at least 2 arguments"
msgstr ""
diff --git a/mpy-cross/mpconfigport.h b/mpy-cross/mpconfigport.h
index 0b07a5b44..464c9113d 100644
--- a/mpy-cross/mpconfigport.h
+++ b/mpy-cross/mpconfigport.h
@@ -40,6 +40,7 @@
#define MICROPY_FLOAT_IMPL (MICROPY_FLOAT_IMPL_DOUBLE)
#define MICROPY_CPYTHON_COMPAT (1)
+#define MICROPY_PY_ASYNC_AWAIT (1)
#define MICROPY_USE_INTERNAL_PRINTF (0)
#define MICROPY_PY_BUILTINS_STR_UNICODE (1)
diff --git a/py/compile.c b/py/compile.c
index 470811005..9b0d29998 100644
--- a/py/compile.c
+++ b/py/compile.c
@@ -1890,7 +1890,7 @@ STATIC void compile_async_stmt(compiler_t *comp, mp_parse_node_struct_t *pns) {
// async def
compile_funcdef(comp, pns0);
scope_t *fscope = (scope_t*)pns0->nodes[4];
- fscope->scope_flags |= MP_SCOPE_FLAG_GENERATOR;
+ fscope->scope_flags |= MP_SCOPE_FLAG_GENERATOR | MP_SCOPE_FLAG_ASYNC;
} else if (MP_PARSE_NODE_STRUCT_KIND(pns0) == PN_for_stmt) {
// async for
compile_async_for_stmt(comp, pns0);
diff --git a/py/emitglue.c b/py/emitglue.c
index 3a3174b0f..7635a73d6 100644
--- a/py/emitglue.c
+++ b/py/emitglue.c
@@ -152,7 +152,7 @@ mp_obj_t mp_make_function_from_raw_code(const mp_raw_code_t *rc, mp_obj_t def_ar
// check for generator functions and if so wrap in generator object
if ((rc->scope_flags & MP_SCOPE_FLAG_GENERATOR) != 0) {
- fun = mp_obj_new_gen_wrap(fun);
+ fun = mp_obj_new_gen_wrap(fun, (rc->scope_flags & MP_SCOPE_FLAG_ASYNC) != 0);
}
return fun;
diff --git a/py/obj.h b/py/obj.h
index 8536e3333..e603d4a49 100644
--- a/py/obj.h
+++ b/py/obj.h
@@ -665,7 +665,7 @@ mp_obj_t mp_obj_new_fun_bc(mp_obj_t def_args, mp_obj_t def_kw_args, const byte *
mp_obj_t mp_obj_new_fun_native(mp_obj_t def_args_in, mp_obj_t def_kw_args, const void *fun_data, const mp_uint_t *const_table);
mp_obj_t mp_obj_new_fun_viper(size_t n_args, void *fun_data, mp_uint_t type_sig);
mp_obj_t mp_obj_new_fun_asm(size_t n_args, void *fun_data, mp_uint_t type_sig);
-mp_obj_t mp_obj_new_gen_wrap(mp_obj_t fun);
+mp_obj_t mp_obj_new_gen_wrap(mp_obj_t fun, bool is_coroutine);
mp_obj_t mp_obj_new_closure(mp_obj_t fun, size_t n_closed, const mp_obj_t *closed);
mp_obj_t mp_obj_new_tuple(size_t n, const mp_obj_t *items);
mp_obj_t mp_obj_new_list(size_t n, mp_obj_t *items);
diff --git a/py/objgenerator.c b/py/objgenerator.c
index 6ffcfae46..df421b60c 100644
--- a/py/objgenerator.c
+++ b/py/objgenerator.c
@@ -42,11 +42,13 @@
typedef struct _mp_obj_gen_wrap_t {
mp_obj_base_t base;
mp_obj_t *fun;
+ bool coroutine_generator;
} mp_obj_gen_wrap_t;
typedef struct _mp_obj_gen_instance_t {
mp_obj_base_t base;
mp_obj_dict_t *globals;
+ bool coroutine_generator;
mp_code_state_t code_state;
} mp_obj_gen_instance_t;
@@ -64,6 +66,7 @@ STATIC mp_obj_t gen_wrap_call(mp_obj_t self_in, size_t n_args, size_t n_kw, cons
n_state * sizeof(mp_obj_t) + n_exc_stack * sizeof(mp_exc_stack_t));
o->base.type = &mp_type_gen_instance;
+ o->coroutine_generator = self->coroutine_generator;
o->globals = self_fun->globals;
o->code_state.fun_bc = self_fun;
o->code_state.ip = 0;
@@ -78,10 +81,11 @@ const mp_obj_type_t mp_type_gen_wrap = {
.unary_op = mp_generic_unary_op,
};
-mp_obj_t mp_obj_new_gen_wrap(mp_obj_t fun) {
+mp_obj_t mp_obj_new_gen_wrap(mp_obj_t fun, bool is_coroutine) {
mp_obj_gen_wrap_t *o = m_new_obj(mp_obj_gen_wrap_t);
o->base.type = &mp_type_gen_wrap;
o->fun = MP_OBJ_TO_PTR(fun);
+ o->coroutine_generator = is_coroutine;
return MP_OBJ_FROM_PTR(o);
}
@@ -91,6 +95,12 @@ mp_obj_t mp_obj_new_gen_wrap(mp_obj_t fun) {
STATIC void gen_instance_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
(void)kind;
mp_obj_gen_instance_t *self = MP_OBJ_TO_PTR(self_in);
+#if MICROPY_PY_ASYNC_AWAIT
+ if (self->coroutine_generator) {
+ mp_printf(print, "<coroutine object '%q' at %p>", mp_obj_fun_get_name(MP_OBJ_FROM_PTR(self->code_state.fun_bc)), self);
+ return;
+ }
+#endif
mp_printf(print, "<generator object '%q' at %p>", mp_obj_fun_get_name(MP_OBJ_FROM_PTR(self->code_state.fun_bc)), self);
}
@@ -194,6 +204,13 @@ STATIC mp_obj_t gen_resume_and_raise(mp_obj_t self_in, mp_obj_t send_value, mp_o
}
STATIC mp_obj_t gen_instance_iternext(mp_obj_t self_in) {
+#if MICROPY_PY_ASYNC_AWAIT
+ // This translate is literally too much for m0 boards
+ mp_obj_gen_instance_t *self = MP_OBJ_TO_PTR(self_in);
+ if (self->coroutine_generator) {
+ mp_raise_TypeError(translate("'coroutine' object is not an iterator"));
+ }
+#endif
return gen_resume_and_raise(self_in, mp_const_none, MP_OBJ_NULL);
}
diff --git a/py/runtime0.h b/py/runtime0.h
index a8089ea64..fb35c8a9f 100644
--- a/py/runtime0.h
+++ b/py/runtime0.h
@@ -33,6 +33,7 @@
#define MP_SCOPE_FLAG_VARKEYWORDS (0x02)
#define MP_SCOPE_FLAG_GENERATOR (0x04)
#define MP_SCOPE_FLAG_DEFKWARGS (0x08)
+#define MP_SCOPE_FLAG_ASYNC (0x10)
// types for native (viper) function signature
#define MP_NATIVE_TYPE_OBJ (0x00)
diff --git a/tests/basics/async_coroutine.py b/tests/basics/async_coroutine.py
new file mode 100644
index 000000000..791f6df14
--- /dev/null
+++ b/tests/basics/async_coroutine.py
@@ -0,0 +1,13 @@
+async def f():
+ pass
+
+try:
+ f() # Should not crash
+except Exception as e:
+ print('failed to invoke')
+
+try:
+ next(f())
+ print('This should fail because async def returns a coroutine, and next() is not allowed')
+except Exception as e:
+ print('pass')
diff --git a/tests/basics/async_coroutine.py.exp b/tests/basics/async_coroutine.py.exp
new file mode 100644
index 000000000..2ae28399f
--- /dev/null
+++ b/tests/basics/async_coroutine.py.exp
@@ -0,0 +1 @@
+pass
diff --git a/tests/run-tests b/tests/run-tests
index 151d48095..e28600361 100755
--- a/tests/run-tests
+++ b/tests/run-tests
@@ -362,7 +362,7 @@ def run_tests(pyb, tests, args, base_path=".", num_threads=1):
if args.emit == 'native':
skip_tests.update({'basics/%s.py' % t for t in 'gen_yield_from gen_yield_from_close gen_yield_from_ducktype gen_yield_from_exc gen_yield_from_executing gen_yield_from_iter gen_yield_from_send gen_yield_from_stopped gen_yield_from_throw gen_yield_from_throw2 gen_yield_from_throw3 generator1 generator2 generator_args generator_close generator_closure generator_exc generator_pend_throw generator_return generator_send'.split()}) # require yield
skip_tests.update({'basics/%s.py' % t for t in 'bytes_gen class_store_class globals_del string_join gen_stack_overflow'.split()}) # require yield
- skip_tests.update({'basics/async_%s.py' % t for t in 'def await await2 for for2 with with2'.split()}) # require yield
+ skip_tests.update({'basics/async_%s.py' % t for t in 'def await await2 for for2 with with2 coroutine'.split()}) # require yield
skip_tests.update({'basics/%s.py' % t for t in 'try_reraise try_reraise2'.split()}) # require raise_varargs
skip_tests.update({'basics/%s.py' % t for t in 'with_break with_continue with_return'.split()}) # require complete with support
skip_tests.add('basics/array_construct2.py') # requires generators