Skip to content

Commit bfccb77

Browse files
committed
asyncio test fixes and asyncio library updates
1 parent 843fdbb commit bfccb77

10 files changed

Lines changed: 183 additions & 53 deletions

File tree

extmod/moduselect.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -383,6 +383,6 @@ const mp_obj_module_t mp_module_uselect = {
383383
.globals = (mp_obj_dict_t *)&mp_module_select_globals,
384384
};
385385

386-
MP_REGISTER_MODULE(MP_QSTR_uselect, mp_module_uselect);
386+
MP_REGISTER_MODULE(MP_QSTR_select, mp_module_uselect);
387387

388388
#endif // MICROPY_PY_USELECT

ports/unix/mpconfigport.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636

3737
// CIRCUITPY
3838
#define CIRCUITPY_MICROPYTHON_ADVANCED (1)
39+
#define MICROPY_PY_ASYNC_AWAIT (1)
3940

4041
// If the variant did not set a feature level then configure a set of features.
4142
#ifndef MICROPY_CONFIG_ROM_LEVEL

py/emitglue.c

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,7 @@ mp_obj_t mp_make_function_from_raw_code(const mp_raw_code_t *rc, const mp_module
191191
if ((rc->scope_flags & MP_SCOPE_FLAG_GENERATOR) != 0) {
192192
((mp_obj_base_t *)MP_OBJ_TO_PTR(fun))->type = &mp_type_native_gen_wrap;
193193
}
194+
// CIRCUITPY: no support for mp_type_native_coro_wrap, native coroutine objects (yet).
194195
break;
195196
#endif
196197
#if MICROPY_EMIT_INLINE_ASM
@@ -203,7 +204,11 @@ mp_obj_t mp_make_function_from_raw_code(const mp_raw_code_t *rc, const mp_module
203204
assert(rc->kind == MP_CODE_BYTECODE);
204205
fun = mp_obj_new_fun_bc(def_args, rc->fun_data, context, rc->children);
205206
// check for generator functions and if so change the type of the object
206-
if ((rc->scope_flags & MP_SCOPE_FLAG_GENERATOR) != 0) {
207+
// A generator is MP_SCOPE_FLAG_ASYNC | MP_SCOPE_FLAG_GENERATOR,
208+
// so check for ASYNC first.
209+
if ((rc->scope_flags & MP_SCOPE_FLAG_ASYNC) != 0) {
210+
((mp_obj_base_t *)MP_OBJ_TO_PTR(fun))->type = &mp_type_coro_wrap;
211+
} else if ((rc->scope_flags & MP_SCOPE_FLAG_GENERATOR) != 0) {
207212
((mp_obj_base_t *)MP_OBJ_TO_PTR(fun))->type = &mp_type_gen_wrap;
208213
}
209214

py/obj.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -738,6 +738,10 @@ extern const mp_obj_type_t mp_type_super;
738738
extern const mp_obj_type_t mp_type_gen_wrap;
739739
extern const mp_obj_type_t mp_type_native_gen_wrap;
740740
extern const mp_obj_type_t mp_type_gen_instance;
741+
// CIRCUITPY
742+
extern const mp_obj_type_t mp_type_coro_wrap;
743+
// CIRCUITPY
744+
extern const mp_obj_type_t mp_type_coro_instance;
741745
extern const mp_obj_type_t mp_type_fun_builtin_0;
742746
extern const mp_obj_type_t mp_type_fun_builtin_1;
743747
extern const mp_obj_type_t mp_type_fun_builtin_2;

py/objgenerator.c

Lines changed: 68 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -55,23 +55,23 @@ typedef struct _mp_obj_gen_instance_t {
5555
// mp_const_none: Not-running, no exception.
5656
// MP_OBJ_NULL: Running, no exception.
5757
// other: Not running, pending exception.
58-
bool coroutine_generator;
5958
mp_obj_t pend_exc;
6059
mp_code_state_t code_state;
6160
} mp_obj_gen_instance_t;
6261

6362
STATIC mp_obj_t gen_wrap_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp_obj_t *args) {
64-
// A generating function is just a bytecode function with type mp_type_gen_wrap
63+
// A generating or coroutine function is just a bytecode function
64+
// with type mp_type_gen_wrap or mp_type_coro_wrap.
6565
mp_obj_fun_bc_t *self_fun = MP_OBJ_TO_PTR(self_in);
6666

6767
// bytecode prelude: get state size and exception stack size
6868
const uint8_t *ip = self_fun->bytecode;
6969
MP_BC_PRELUDE_SIG_DECODE(ip);
7070

71-
// allocate the generator object, with room for local stack and exception stack
71+
// allocate the generator or coroutine object, with room for local stack and exception stack
7272
mp_obj_gen_instance_t *o = mp_obj_malloc_var(mp_obj_gen_instance_t, byte,
7373
n_state * sizeof(mp_obj_t) + n_exc_stack * sizeof(mp_exc_stack_t),
74-
&mp_type_gen_instance);
74+
self_fun->base.type == &mp_type_gen_wrap ? &mp_type_gen_instance : &mp_type_coro_instance);
7575

7676
o->pend_exc = mp_const_none;
7777
o->code_state.fun_bc = self_fun;
@@ -93,6 +93,19 @@ const mp_obj_type_t mp_type_gen_wrap = {
9393
),
9494
};
9595

96+
const mp_obj_type_t mp_type_coro_wrap = {
97+
{ &mp_type_type },
98+
.flags = MP_TYPE_FLAG_BINDS_SELF | MP_TYPE_FLAG_EXTENDED,
99+
.name = MP_QSTR_coroutine,
100+
#if MICROPY_PY_FUNCTION_ATTRS
101+
.attr = mp_obj_fun_bc_attr,
102+
#endif
103+
MP_TYPE_EXTENDED_FIELDS(
104+
.call = gen_wrap_call,
105+
.unary_op = mp_generic_unary_op,
106+
),
107+
};
108+
96109
/******************************************************************************/
97110
// native generator wrapper
98111

@@ -167,28 +180,33 @@ const mp_obj_type_t mp_type_native_gen_wrap = {
167180
STATIC void gen_instance_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
168181
(void)kind;
169182
mp_obj_gen_instance_t *self = MP_OBJ_TO_PTR(self_in);
170-
#if MICROPY_PY_ASYNC_AWAIT
171-
if (self->coroutine_generator) {
172-
mp_printf(print, "<%q object '%q' at %p>", MP_QSTR_coroutine, mp_obj_fun_get_name(MP_OBJ_FROM_PTR(self->code_state.fun_bc)), self);
173-
} else {
174-
mp_printf(print, "<%q object '%q' at %p>", MP_QSTR_generator, mp_obj_fun_get_name(MP_OBJ_FROM_PTR(self->code_state.fun_bc)), self);
175-
}
176-
#else
177183
mp_printf(print, "<generator object '%q' at %p>", mp_obj_fun_get_name(MP_OBJ_FROM_PTR(self->code_state.fun_bc)), self);
178-
#endif
179184
}
180185

186+
// CIRCUITPY
187+
#if MICROPY_PY_ASYNC_AWAIT
188+
STATIC void coro_instance_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
189+
(void)kind;
190+
mp_obj_gen_instance_t *self = MP_OBJ_TO_PTR(self_in);
191+
mp_printf(print, "<coroutine object '%q' at %p>", mp_obj_fun_get_name(MP_OBJ_FROM_PTR(self->code_state.fun_bc)), self);
192+
}
193+
#endif
194+
181195
mp_vm_return_kind_t mp_obj_gen_resume(mp_obj_t self_in, mp_obj_t send_value, mp_obj_t throw_value, mp_obj_t *ret_val) {
182196
MP_STACK_CHECK();
183-
mp_check_self(mp_obj_is_type(self_in, &mp_type_gen_instance));
197+
// CIRCUITPY
198+
// note that self may have as its type either gen or coro,
199+
// both of which are stored as an mp_obj_gen_instance_t .
200+
mp_check_self(
201+
mp_obj_is_type(self_in, &mp_type_gen_instance) ||
202+
mp_obj_is_type(self_in, &mp_type_coro_instance));
184203
mp_obj_gen_instance_t *self = MP_OBJ_TO_PTR(self_in);
185204
if (self->code_state.ip == 0) {
186205
// Trying to resume an already stopped generator.
187206
// This is an optimised "raise StopIteration(None)".
188207
*ret_val = mp_const_none;
189208
return MP_VM_RETURN_NORMAL;
190209
}
191-
192210
// Ensure the generator cannot be reentered during execution
193211
if (self->pend_exc == MP_OBJ_NULL) {
194212
mp_raise_ValueError(MP_ERROR_TEXT("generator already executing"));
@@ -285,6 +303,7 @@ mp_vm_return_kind_t mp_obj_gen_resume(mp_obj_t self_in, mp_obj_t send_value, mp_
285303
STATIC mp_obj_t gen_resume_and_raise(mp_obj_t self_in, mp_obj_t send_value, mp_obj_t throw_value, bool raise_stop_iteration) {
286304
mp_obj_t ret;
287305
switch (mp_obj_gen_resume(self_in, send_value, throw_value, &ret)) {
306+
288307
case MP_VM_RETURN_NORMAL:
289308
default:
290309
// A normal return is a StopIteration, either raise it or return
@@ -307,12 +326,6 @@ STATIC mp_obj_t gen_resume_and_raise(mp_obj_t self_in, mp_obj_t send_value, mp_o
307326
}
308327

309328
STATIC mp_obj_t gen_instance_iternext(mp_obj_t self_in) {
310-
#if MICROPY_PY_ASYNC_AWAIT
311-
mp_obj_gen_instance_t *self = MP_OBJ_TO_PTR(self_in);
312-
if (self->coroutine_generator) {
313-
mp_raise_TypeError(MP_ERROR_TEXT("'coroutine' object is not an iterator"));
314-
}
315-
#endif
316329
return gen_resume_and_raise(self_in, mp_const_none, MP_OBJ_NULL, false);
317330
}
318331

@@ -322,21 +335,12 @@ STATIC mp_obj_t gen_instance_send(mp_obj_t self_in, mp_obj_t send_value) {
322335
STATIC MP_DEFINE_CONST_FUN_OBJ_2(gen_instance_send_obj, gen_instance_send);
323336

324337
#if MICROPY_PY_ASYNC_AWAIT
325-
STATIC mp_obj_t gen_instance_await(mp_obj_t self_in) {
326-
mp_obj_gen_instance_t *self = MP_OBJ_TO_PTR(self_in);
327-
if (!self->coroutine_generator) {
328-
// Pretend like a generator does not have this coroutine behavior.
329-
// Pay no attention to the dir() behind the curtain
330-
mp_raise_msg_varg(&mp_type_AttributeError, MP_ERROR_TEXT("type object '%q' has no attribute '%q'"),
331-
MP_QSTR_generator, MP_QSTR___await__);
332-
}
333-
// You can directly call send on a coroutine generator or you can __await__ then send on the return of that.
334-
return self;
338+
STATIC mp_obj_t coro_instance_await(mp_obj_t self_in) {
339+
return self_in;
335340
}
336-
STATIC MP_DEFINE_CONST_FUN_OBJ_1(gen_instance_await_obj, gen_instance_await);
341+
STATIC MP_DEFINE_CONST_FUN_OBJ_1(coro_instance_await_obj, coro_instance_await);
337342
#endif
338343

339-
STATIC mp_obj_t gen_instance_close(mp_obj_t self_in);
340344
STATIC mp_obj_t gen_instance_throw(size_t n_args, const mp_obj_t *args) {
341345
// The signature of this function is: throw(type[, value[, traceback]])
342346
// CPython will pass all given arguments through the call chain and process them
@@ -408,9 +412,6 @@ STATIC const mp_rom_map_elem_t gen_instance_locals_dict_table[] = {
408412
#if MICROPY_PY_GENERATOR_PEND_THROW
409413
{ MP_ROM_QSTR(MP_QSTR_pend_throw), MP_ROM_PTR(&gen_instance_pend_throw_obj) },
410414
#endif
411-
#if MICROPY_PY_ASYNC_AWAIT
412-
{ MP_ROM_QSTR(MP_QSTR___await__), MP_ROM_PTR(&gen_instance_await_obj) },
413-
#endif
414415
};
415416

416417
STATIC MP_DEFINE_CONST_DICT(gen_instance_locals_dict, gen_instance_locals_dict_table);
@@ -427,3 +428,35 @@ const mp_obj_type_t mp_type_gen_instance = {
427428
.iternext = gen_instance_iternext,
428429
),
429430
};
431+
432+
#if MICROPY_PY_ASYNC_AWAIT
433+
// CIRCUITPY
434+
// coroutine instance locals dict and type
435+
// same as generator, but with addition of __await()__.
436+
STATIC const mp_rom_map_elem_t coro_instance_locals_dict_table[] = {
437+
{ MP_ROM_QSTR(MP_QSTR_close), MP_ROM_PTR(&gen_instance_close_obj) },
438+
{ MP_ROM_QSTR(MP_QSTR_send), MP_ROM_PTR(&gen_instance_send_obj) },
439+
{ MP_ROM_QSTR(MP_QSTR_throw), MP_ROM_PTR(&gen_instance_throw_obj) },
440+
#if MICROPY_PY_GENERATOR_PEND_THROW
441+
{ MP_ROM_QSTR(MP_QSTR_pend_throw), MP_ROM_PTR(&gen_instance_pend_throw_obj) },
442+
#endif
443+
#if MICROPY_PY_ASYNC_AWAIT
444+
{ MP_ROM_QSTR(MP_QSTR___await__), MP_ROM_PTR(&coro_instance_await_obj) },
445+
#endif
446+
};
447+
448+
STATIC MP_DEFINE_CONST_DICT(coro_instance_locals_dict, coro_instance_locals_dict_table);
449+
450+
const mp_obj_type_t mp_type_coro_instance = {
451+
{ &mp_type_type },
452+
.flags = MP_TYPE_FLAG_EXTENDED,
453+
.name = MP_QSTR_coroutine,
454+
.print = coro_instance_print,
455+
.locals_dict = (mp_obj_dict_t *)&coro_instance_locals_dict,
456+
MP_TYPE_EXTENDED_FIELDS(
457+
.unary_op = mp_generic_unary_op,
458+
.getiter = mp_identity_getiter,
459+
.iternext = gen_instance_iternext,
460+
),
461+
};
462+
#endif

py/runtime.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1415,7 +1415,8 @@ mp_vm_return_kind_t mp_resume(mp_obj_t self_in, mp_obj_t send_value, mp_obj_t th
14151415
assert((send_value != MP_OBJ_NULL) ^ (throw_value != MP_OBJ_NULL));
14161416
const mp_obj_type_t *type = mp_obj_get_type(self_in);
14171417

1418-
if (type == &mp_type_gen_instance) {
1418+
// CIRCUITPY distinguishes generators and coroutines.
1419+
if (type == &mp_type_gen_instance || type == &mp_type_coro_instance) {
14191420
return mp_obj_gen_resume(self_in, send_value, throw_value, ret_val);
14201421
}
14211422

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
# Test Event class
2+
3+
try:
4+
import uasyncio as asyncio
5+
except ImportError:
6+
print("SKIP")
7+
raise SystemExit
8+
9+
10+
import micropython
11+
12+
try:
13+
micropython.schedule
14+
except AttributeError:
15+
print("SKIP")
16+
raise SystemExit
17+
18+
19+
try:
20+
# Unix port can't select/poll on user-defined types.
21+
import uselect as select
22+
23+
poller = select.poll()
24+
poller.register(asyncio.ThreadSafeFlag())
25+
except TypeError:
26+
print("SKIP")
27+
raise SystemExit
28+
29+
30+
async def task(id, flag):
31+
print("task", id)
32+
await flag.wait()
33+
print("task", id, "done")
34+
35+
36+
def set_from_schedule(flag):
37+
print("schedule")
38+
flag.set()
39+
print("schedule done")
40+
41+
42+
async def main():
43+
flag = asyncio.ThreadSafeFlag()
44+
45+
# Set the flag from within the loop.
46+
t = asyncio.create_task(task(1, flag))
47+
print("yield")
48+
await asyncio.sleep(0)
49+
print("set event")
50+
flag.set()
51+
print("yield")
52+
await asyncio.sleep(0)
53+
print("wait task")
54+
await t
55+
56+
# Set the flag from scheduler context.
57+
print("----")
58+
t = asyncio.create_task(task(2, flag))
59+
print("yield")
60+
await asyncio.sleep(0)
61+
print("set event")
62+
micropython.schedule(set_from_schedule, flag)
63+
print("yield")
64+
await asyncio.sleep(0)
65+
print("wait task")
66+
await t
67+
68+
# Flag already set.
69+
print("----")
70+
print("set event")
71+
flag.set()
72+
t = asyncio.create_task(task(3, flag))
73+
print("yield")
74+
await asyncio.sleep(0)
75+
print("wait task")
76+
await t
77+
78+
79+
asyncio.run(main())
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
yield
2+
task 1
3+
set event
4+
yield
5+
wait task
6+
task 1 done
7+
----
8+
yield
9+
task 2
10+
set event
11+
yield
12+
schedule
13+
schedule done
14+
wait task
15+
task 2 done
16+
----
17+
set event
18+
yield
19+
task 3
20+
task 3 done
21+
wait task

tests/extmod/websocket_basic.py.exp

Lines changed: 0 additions & 14 deletions
This file was deleted.

0 commit comments

Comments
 (0)