asyncio test fixes and asyncio library updates
This commit is contained in:
parent
843fdbb250
commit
bfccb77ec1
@ -383,6 +383,6 @@ const mp_obj_module_t mp_module_uselect = {
|
||||
.globals = (mp_obj_dict_t *)&mp_module_select_globals,
|
||||
};
|
||||
|
||||
MP_REGISTER_MODULE(MP_QSTR_uselect, mp_module_uselect);
|
||||
MP_REGISTER_MODULE(MP_QSTR_select, mp_module_uselect);
|
||||
|
||||
#endif // MICROPY_PY_USELECT
|
||||
|
@ -1 +1 @@
|
||||
Subproject commit 596cc896e5c8815caa2a6f405560833193848149
|
||||
Subproject commit 510d4a3bb9326a31105622fe1905301a4f543a2c
|
@ -36,6 +36,7 @@
|
||||
|
||||
// CIRCUITPY
|
||||
#define CIRCUITPY_MICROPYTHON_ADVANCED (1)
|
||||
#define MICROPY_PY_ASYNC_AWAIT (1)
|
||||
|
||||
// If the variant did not set a feature level then configure a set of features.
|
||||
#ifndef MICROPY_CONFIG_ROM_LEVEL
|
||||
|
@ -191,6 +191,7 @@ mp_obj_t mp_make_function_from_raw_code(const mp_raw_code_t *rc, const mp_module
|
||||
if ((rc->scope_flags & MP_SCOPE_FLAG_GENERATOR) != 0) {
|
||||
((mp_obj_base_t *)MP_OBJ_TO_PTR(fun))->type = &mp_type_native_gen_wrap;
|
||||
}
|
||||
// CIRCUITPY: no support for mp_type_native_coro_wrap, native coroutine objects (yet).
|
||||
break;
|
||||
#endif
|
||||
#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
|
||||
assert(rc->kind == MP_CODE_BYTECODE);
|
||||
fun = mp_obj_new_fun_bc(def_args, rc->fun_data, context, rc->children);
|
||||
// check for generator functions and if so change the type of the object
|
||||
if ((rc->scope_flags & MP_SCOPE_FLAG_GENERATOR) != 0) {
|
||||
// A generator is MP_SCOPE_FLAG_ASYNC | MP_SCOPE_FLAG_GENERATOR,
|
||||
// so check for ASYNC first.
|
||||
if ((rc->scope_flags & MP_SCOPE_FLAG_ASYNC) != 0) {
|
||||
((mp_obj_base_t *)MP_OBJ_TO_PTR(fun))->type = &mp_type_coro_wrap;
|
||||
} else if ((rc->scope_flags & MP_SCOPE_FLAG_GENERATOR) != 0) {
|
||||
((mp_obj_base_t *)MP_OBJ_TO_PTR(fun))->type = &mp_type_gen_wrap;
|
||||
}
|
||||
|
||||
|
4
py/obj.h
4
py/obj.h
@ -738,6 +738,10 @@ extern const mp_obj_type_t mp_type_super;
|
||||
extern const mp_obj_type_t mp_type_gen_wrap;
|
||||
extern const mp_obj_type_t mp_type_native_gen_wrap;
|
||||
extern const mp_obj_type_t mp_type_gen_instance;
|
||||
// CIRCUITPY
|
||||
extern const mp_obj_type_t mp_type_coro_wrap;
|
||||
// CIRCUITPY
|
||||
extern const mp_obj_type_t mp_type_coro_instance;
|
||||
extern const mp_obj_type_t mp_type_fun_builtin_0;
|
||||
extern const mp_obj_type_t mp_type_fun_builtin_1;
|
||||
extern const mp_obj_type_t mp_type_fun_builtin_2;
|
||||
|
@ -55,23 +55,23 @@ typedef struct _mp_obj_gen_instance_t {
|
||||
// mp_const_none: Not-running, no exception.
|
||||
// MP_OBJ_NULL: Running, no exception.
|
||||
// other: Not running, pending exception.
|
||||
bool coroutine_generator;
|
||||
mp_obj_t pend_exc;
|
||||
mp_code_state_t code_state;
|
||||
} mp_obj_gen_instance_t;
|
||||
|
||||
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) {
|
||||
// A generating function is just a bytecode function with type mp_type_gen_wrap
|
||||
// A generating or coroutine function is just a bytecode function
|
||||
// with type mp_type_gen_wrap or mp_type_coro_wrap.
|
||||
mp_obj_fun_bc_t *self_fun = MP_OBJ_TO_PTR(self_in);
|
||||
|
||||
// bytecode prelude: get state size and exception stack size
|
||||
const uint8_t *ip = self_fun->bytecode;
|
||||
MP_BC_PRELUDE_SIG_DECODE(ip);
|
||||
|
||||
// allocate the generator object, with room for local stack and exception stack
|
||||
// allocate the generator or coroutine object, with room for local stack and exception stack
|
||||
mp_obj_gen_instance_t *o = mp_obj_malloc_var(mp_obj_gen_instance_t, byte,
|
||||
n_state * sizeof(mp_obj_t) + n_exc_stack * sizeof(mp_exc_stack_t),
|
||||
&mp_type_gen_instance);
|
||||
self_fun->base.type == &mp_type_gen_wrap ? &mp_type_gen_instance : &mp_type_coro_instance);
|
||||
|
||||
o->pend_exc = mp_const_none;
|
||||
o->code_state.fun_bc = self_fun;
|
||||
@ -93,6 +93,19 @@ const mp_obj_type_t mp_type_gen_wrap = {
|
||||
),
|
||||
};
|
||||
|
||||
const mp_obj_type_t mp_type_coro_wrap = {
|
||||
{ &mp_type_type },
|
||||
.flags = MP_TYPE_FLAG_BINDS_SELF | MP_TYPE_FLAG_EXTENDED,
|
||||
.name = MP_QSTR_coroutine,
|
||||
#if MICROPY_PY_FUNCTION_ATTRS
|
||||
.attr = mp_obj_fun_bc_attr,
|
||||
#endif
|
||||
MP_TYPE_EXTENDED_FIELDS(
|
||||
.call = gen_wrap_call,
|
||||
.unary_op = mp_generic_unary_op,
|
||||
),
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
// native generator wrapper
|
||||
|
||||
@ -167,20 +180,26 @@ const mp_obj_type_t mp_type_native_gen_wrap = {
|
||||
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, "<%q object '%q' at %p>", MP_QSTR_coroutine, mp_obj_fun_get_name(MP_OBJ_FROM_PTR(self->code_state.fun_bc)), self);
|
||||
} else {
|
||||
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);
|
||||
}
|
||||
#else
|
||||
mp_printf(print, "<generator object '%q' at %p>", mp_obj_fun_get_name(MP_OBJ_FROM_PTR(self->code_state.fun_bc)), self);
|
||||
#endif
|
||||
}
|
||||
|
||||
// CIRCUITPY
|
||||
#if MICROPY_PY_ASYNC_AWAIT
|
||||
STATIC void coro_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);
|
||||
mp_printf(print, "<coroutine object '%q' at %p>", mp_obj_fun_get_name(MP_OBJ_FROM_PTR(self->code_state.fun_bc)), self);
|
||||
}
|
||||
#endif
|
||||
|
||||
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) {
|
||||
MP_STACK_CHECK();
|
||||
mp_check_self(mp_obj_is_type(self_in, &mp_type_gen_instance));
|
||||
// CIRCUITPY
|
||||
// note that self may have as its type either gen or coro,
|
||||
// both of which are stored as an mp_obj_gen_instance_t .
|
||||
mp_check_self(
|
||||
mp_obj_is_type(self_in, &mp_type_gen_instance) ||
|
||||
mp_obj_is_type(self_in, &mp_type_coro_instance));
|
||||
mp_obj_gen_instance_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
if (self->code_state.ip == 0) {
|
||||
// Trying to resume an already stopped generator.
|
||||
@ -188,7 +207,6 @@ mp_vm_return_kind_t mp_obj_gen_resume(mp_obj_t self_in, mp_obj_t send_value, mp_
|
||||
*ret_val = mp_const_none;
|
||||
return MP_VM_RETURN_NORMAL;
|
||||
}
|
||||
|
||||
// Ensure the generator cannot be reentered during execution
|
||||
if (self->pend_exc == MP_OBJ_NULL) {
|
||||
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_
|
||||
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) {
|
||||
mp_obj_t ret;
|
||||
switch (mp_obj_gen_resume(self_in, send_value, throw_value, &ret)) {
|
||||
|
||||
case MP_VM_RETURN_NORMAL:
|
||||
default:
|
||||
// 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
|
||||
}
|
||||
|
||||
STATIC mp_obj_t gen_instance_iternext(mp_obj_t self_in) {
|
||||
#if MICROPY_PY_ASYNC_AWAIT
|
||||
mp_obj_gen_instance_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
if (self->coroutine_generator) {
|
||||
mp_raise_TypeError(MP_ERROR_TEXT("'coroutine' object is not an iterator"));
|
||||
}
|
||||
#endif
|
||||
return gen_resume_and_raise(self_in, mp_const_none, MP_OBJ_NULL, false);
|
||||
}
|
||||
|
||||
@ -322,21 +335,12 @@ 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
|
||||
mp_raise_msg_varg(&mp_type_AttributeError, MP_ERROR_TEXT("type object '%q' has no attribute '%q'"),
|
||||
MP_QSTR_generator, MP_QSTR___await__);
|
||||
STATIC mp_obj_t coro_instance_await(mp_obj_t self_in) {
|
||||
return self_in;
|
||||
}
|
||||
// You can directly call send on a coroutine generator or you can __await__ then send on the return of that.
|
||||
return self;
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_1(gen_instance_await_obj, gen_instance_await);
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_1(coro_instance_await_obj, coro_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) {
|
||||
// The signature of this function is: throw(type[, value[, traceback]])
|
||||
// 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[] = {
|
||||
#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);
|
||||
@ -427,3 +428,35 @@ const mp_obj_type_t mp_type_gen_instance = {
|
||||
.iternext = gen_instance_iternext,
|
||||
),
|
||||
};
|
||||
|
||||
#if MICROPY_PY_ASYNC_AWAIT
|
||||
// CIRCUITPY
|
||||
// coroutine instance locals dict and type
|
||||
// same as generator, but with addition of __await()__.
|
||||
STATIC const mp_rom_map_elem_t coro_instance_locals_dict_table[] = {
|
||||
{ MP_ROM_QSTR(MP_QSTR_close), MP_ROM_PTR(&gen_instance_close_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_send), MP_ROM_PTR(&gen_instance_send_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_throw), MP_ROM_PTR(&gen_instance_throw_obj) },
|
||||
#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(&coro_instance_await_obj) },
|
||||
#endif
|
||||
};
|
||||
|
||||
STATIC MP_DEFINE_CONST_DICT(coro_instance_locals_dict, coro_instance_locals_dict_table);
|
||||
|
||||
const mp_obj_type_t mp_type_coro_instance = {
|
||||
{ &mp_type_type },
|
||||
.flags = MP_TYPE_FLAG_EXTENDED,
|
||||
.name = MP_QSTR_coroutine,
|
||||
.print = coro_instance_print,
|
||||
.locals_dict = (mp_obj_dict_t *)&coro_instance_locals_dict,
|
||||
MP_TYPE_EXTENDED_FIELDS(
|
||||
.unary_op = mp_generic_unary_op,
|
||||
.getiter = mp_identity_getiter,
|
||||
.iternext = gen_instance_iternext,
|
||||
),
|
||||
};
|
||||
#endif
|
||||
|
@ -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
|
||||
assert((send_value != MP_OBJ_NULL) ^ (throw_value != MP_OBJ_NULL));
|
||||
const mp_obj_type_t *type = mp_obj_get_type(self_in);
|
||||
|
||||
if (type == &mp_type_gen_instance) {
|
||||
// CIRCUITPY distinguishes generators and coroutines.
|
||||
if (type == &mp_type_gen_instance || type == &mp_type_coro_instance) {
|
||||
return mp_obj_gen_resume(self_in, send_value, throw_value, ret_val);
|
||||
}
|
||||
|
||||
|
79
tests/extmod/uasyncio_threadsafeflag.py
Normal file
79
tests/extmod/uasyncio_threadsafeflag.py
Normal file
@ -0,0 +1,79 @@
|
||||
# Test Event class
|
||||
|
||||
try:
|
||||
import uasyncio as asyncio
|
||||
except ImportError:
|
||||
print("SKIP")
|
||||
raise SystemExit
|
||||
|
||||
|
||||
import micropython
|
||||
|
||||
try:
|
||||
micropython.schedule
|
||||
except AttributeError:
|
||||
print("SKIP")
|
||||
raise SystemExit
|
||||
|
||||
|
||||
try:
|
||||
# Unix port can't select/poll on user-defined types.
|
||||
import uselect as select
|
||||
|
||||
poller = select.poll()
|
||||
poller.register(asyncio.ThreadSafeFlag())
|
||||
except TypeError:
|
||||
print("SKIP")
|
||||
raise SystemExit
|
||||
|
||||
|
||||
async def task(id, flag):
|
||||
print("task", id)
|
||||
await flag.wait()
|
||||
print("task", id, "done")
|
||||
|
||||
|
||||
def set_from_schedule(flag):
|
||||
print("schedule")
|
||||
flag.set()
|
||||
print("schedule done")
|
||||
|
||||
|
||||
async def main():
|
||||
flag = asyncio.ThreadSafeFlag()
|
||||
|
||||
# Set the flag from within the loop.
|
||||
t = asyncio.create_task(task(1, flag))
|
||||
print("yield")
|
||||
await asyncio.sleep(0)
|
||||
print("set event")
|
||||
flag.set()
|
||||
print("yield")
|
||||
await asyncio.sleep(0)
|
||||
print("wait task")
|
||||
await t
|
||||
|
||||
# Set the flag from scheduler context.
|
||||
print("----")
|
||||
t = asyncio.create_task(task(2, flag))
|
||||
print("yield")
|
||||
await asyncio.sleep(0)
|
||||
print("set event")
|
||||
micropython.schedule(set_from_schedule, flag)
|
||||
print("yield")
|
||||
await asyncio.sleep(0)
|
||||
print("wait task")
|
||||
await t
|
||||
|
||||
# Flag already set.
|
||||
print("----")
|
||||
print("set event")
|
||||
flag.set()
|
||||
t = asyncio.create_task(task(3, flag))
|
||||
print("yield")
|
||||
await asyncio.sleep(0)
|
||||
print("wait task")
|
||||
await t
|
||||
|
||||
|
||||
asyncio.run(main())
|
21
tests/extmod/uasyncio_threadsafeflag.py.exp
Normal file
21
tests/extmod/uasyncio_threadsafeflag.py.exp
Normal file
@ -0,0 +1,21 @@
|
||||
yield
|
||||
task 1
|
||||
set event
|
||||
yield
|
||||
wait task
|
||||
task 1 done
|
||||
----
|
||||
yield
|
||||
task 2
|
||||
set event
|
||||
yield
|
||||
schedule
|
||||
schedule done
|
||||
wait task
|
||||
task 2 done
|
||||
----
|
||||
set event
|
||||
yield
|
||||
task 3
|
||||
task 3 done
|
||||
wait task
|
@ -1,14 +0,0 @@
|
||||
b'ping'
|
||||
b'ping'
|
||||
b'\x81\x04pong'
|
||||
b'pingpingpingpingpingpingpingpingpingpingpingpingpingpingpingpingpingpingpingpingpingpingpingpingpingpingpingpingpingpingpingping'
|
||||
b'\x81~\x00\x80pongpongpongpongpongpongpongpongpongpongpongpongpongpongpongpongpongpongpongpongpongpongpongpongpongpongpongpongpongpongpongpong'
|
||||
b'\x00\x00\x00\x00'
|
||||
b''
|
||||
b'\x81\x02\x88\x00'
|
||||
b'ping'
|
||||
b'pong'
|
||||
0
|
||||
1
|
||||
2
|
||||
ioctl: EINVAL: True
|
Loading…
Reference in New Issue
Block a user