Skip to content

Commit bf849ff

Browse files
committed
async def syntax rigor and __await__ magic method
Some examples of improved compliance with CPython that currently have divergent behavior in CircuitPython are listed below: * yield from is not allowed in async methods ``` >>> async def f(): ... yield from 'abc' ... Traceback (most recent call last): File "<stdin>", line 2, in f SyntaxError: 'yield from' inside async function ``` * await only works on awaitable expressions ``` >>> async def f(): ... await 'not awaitable' ... >>> f().send(None) Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 2, in f AttributeError: 'str' object has no attribute '__await__' ``` * only __await__()able expressions are awaitable Okay this one actually does not work in circuitpython at all today. This is how CPython works though and pretending __await__ does not exist will only bite users who write both. ``` >>> class c: ... pass ... >>> def f(self): ... yield ... yield ... return 'f to pay respects' ... >>> c.__await__ = f # could just as easily have put it on the class but this shows how it's wired >>> async def g(): ... awaitable_thing = c() ... partial = await awaitable_thing ... return 'press ' + partial ... >>> q = g() >>> q.send(None) >>> q.send(None) >>> q.send(None) Traceback (most recent call last): File "<stdin>", line 1, in <module> StopIteration: press f to pay respects ```
1 parent 5cadf52 commit bf849ff

File tree

2 files changed

+30
-1
lines changed

2 files changed

+30
-1
lines changed

py/compile.c

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2632,6 +2632,12 @@ STATIC void compile_yield_expr(compiler_t *comp, mp_parse_node_struct_t *pns) {
26322632
EMIT_ARG(yield, MP_EMIT_YIELD_VALUE);
26332633
} else if (MP_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[0], PN_yield_arg_from)) {
26342634
pns = (mp_parse_node_struct_t*)pns->nodes[0];
2635+
#if MICROPY_PY_ASYNC_AWAIT
2636+
if(comp->scope_cur->scope_flags & MP_SCOPE_FLAG_ASYNC) {
2637+
compile_syntax_error(comp, (mp_parse_node_t)pns, translate("'yield from' inside async function"));
2638+
return;
2639+
}
2640+
#endif
26352641
compile_node(comp, pns->nodes[0]);
26362642
compile_yield_from(comp);
26372643
} else {
@@ -2648,7 +2654,8 @@ STATIC void compile_atom_expr_await(compiler_t *comp, mp_parse_node_struct_t *pn
26482654
}
26492655
compile_require_async_context(comp, pns);
26502656
compile_atom_expr_normal(comp, pns);
2651-
compile_yield_from(comp);
2657+
2658+
compile_await_object_method(comp, MP_QSTR___await__);
26522659
}
26532660
#endif
26542661

py/objgenerator.c

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,25 @@ STATIC mp_obj_t gen_instance_send(mp_obj_t self_in, mp_obj_t send_value) {
225225

226226
STATIC MP_DEFINE_CONST_FUN_OBJ_2(gen_instance_send_obj, gen_instance_send);
227227

228+
#if MICROPY_PY_ASYNC_AWAIT
229+
STATIC mp_obj_t gen_instance_await(mp_obj_t self_in) {
230+
mp_obj_gen_instance_t *self = MP_OBJ_TO_PTR(self_in);
231+
if ( !self->coroutine_generator ) {
232+
// Pretend like a generator does not have this coroutine behavior.
233+
// Pay no attention to the dir() behind the curtain
234+
nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_AttributeError,
235+
translate("type object 'generator' has no attribute '__await__'")));
236+
}
237+
mp_obj_t ret = gen_resume_and_raise(self_in, mp_const_none, MP_OBJ_NULL);
238+
if (ret == MP_OBJ_STOP_ITERATION) {
239+
nlr_raise(mp_obj_new_exception(&mp_type_StopIteration));
240+
} else {
241+
return ret;
242+
}
243+
}
244+
STATIC MP_DEFINE_CONST_FUN_OBJ_1(gen_instance_await_obj, gen_instance_await);
245+
#endif
246+
228247
STATIC mp_obj_t gen_instance_close(mp_obj_t self_in);
229248
STATIC mp_obj_t gen_instance_throw(size_t n_args, const mp_obj_t *args) {
230249
mp_obj_t exc = (n_args == 2) ? args[1] : args[2];
@@ -280,6 +299,9 @@ STATIC const mp_rom_map_elem_t gen_instance_locals_dict_table[] = {
280299
#if MICROPY_PY_GENERATOR_PEND_THROW
281300
{ MP_ROM_QSTR(MP_QSTR_pend_throw), MP_ROM_PTR(&gen_instance_pend_throw_obj) },
282301
#endif
302+
#if MICROPY_PY_ASYNC_AWAIT
303+
{ MP_ROM_QSTR(MP_QSTR___await__), MP_ROM_PTR(&gen_instance_await_obj) },
304+
#endif
283305
};
284306

285307
STATIC MP_DEFINE_CONST_DICT(gen_instance_locals_dict, gen_instance_locals_dict_table);

0 commit comments

Comments
 (0)