summaryrefslogtreecommitdiff
path: root/py/objgenerator.c
diff options
context:
space:
mode:
authorKenny <3454741+WarriorOfWire@users.noreply.github.com>2020-08-05 19:55:40 -0700
committerKenny <3454741+WarriorOfWire@users.noreply.github.com>2020-10-10 15:45:08 -0700
commitbf849ff674a953ea36dbffc856a05158b2350077 (patch)
tree02489fad10981c914515a0edb65e649f83ceaadd /py/objgenerator.c
parent5cadf525bdf27ef4a3fab9b1c7242384f260003e (diff)
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 ```
Diffstat (limited to 'py/objgenerator.c')
-rw-r--r--py/objgenerator.c22
1 files changed, 22 insertions, 0 deletions
diff --git a/py/objgenerator.c b/py/objgenerator.c
index df421b60c..baa6a9849 100644
--- a/py/objgenerator.c
+++ b/py/objgenerator.c
@@ -225,6 +225,25 @@ STATIC mp_obj_t gen_instance_send(mp_obj_t self_in, mp_obj_t send_value) {
STATIC MP_DEFINE_CONST_FUN_OBJ_2(gen_instance_send_obj, gen_instance_send);
+#if MICROPY_PY_ASYNC_AWAIT
+STATIC mp_obj_t gen_instance_await(mp_obj_t self_in) {
+ mp_obj_gen_instance_t *self = MP_OBJ_TO_PTR(self_in);
+ if ( !self->coroutine_generator ) {
+ // Pretend like a generator does not have this coroutine behavior.
+ // Pay no attention to the dir() behind the curtain
+ nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_AttributeError,
+ translate("type object 'generator' has no attribute '__await__'")));
+ }
+ mp_obj_t ret = gen_resume_and_raise(self_in, mp_const_none, MP_OBJ_NULL);
+ if (ret == MP_OBJ_STOP_ITERATION) {
+ nlr_raise(mp_obj_new_exception(&mp_type_StopIteration));
+ } else {
+ return ret;
+ }
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(gen_instance_await_obj, gen_instance_await);
+#endif
+
STATIC mp_obj_t gen_instance_close(mp_obj_t self_in);
STATIC mp_obj_t gen_instance_throw(size_t n_args, const mp_obj_t *args) {
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[] = {
#if MICROPY_PY_GENERATOR_PEND_THROW
{ MP_ROM_QSTR(MP_QSTR_pend_throw), MP_ROM_PTR(&gen_instance_pend_throw_obj) },
#endif
+ #if MICROPY_PY_ASYNC_AWAIT
+ { MP_ROM_QSTR(MP_QSTR___await__), MP_ROM_PTR(&gen_instance_await_obj) },
+ #endif
};
STATIC MP_DEFINE_CONST_DICT(gen_instance_locals_dict, gen_instance_locals_dict_table);