diff --git a/examples/natmod/features0/Makefile b/examples/natmod/features0/Makefile new file mode 100644 index 0000000000..57490df90a --- /dev/null +++ b/examples/natmod/features0/Makefile @@ -0,0 +1,14 @@ +# Location of top-level MicroPython directory +MPY_DIR = ../../.. + +# Name of module +MOD = features0 + +# Source files (.c or .py) +SRC = features0.c + +# Architecture to build for (x86, x64, armv7m, xtensa, xtensawin) +ARCH = x64 + +# Include to get the rules for compiling and linking the module +include $(MPY_DIR)/py/dynruntime.mk diff --git a/examples/natmod/features0/features0.c b/examples/natmod/features0/features0.c new file mode 100644 index 0000000000..1b1867a3bc --- /dev/null +++ b/examples/natmod/features0/features0.c @@ -0,0 +1,40 @@ +/* This example demonstrates the following features in a native module: + - defining a simple function exposed to Python + - defining a local, helper C function + - getting and creating integer objects +*/ + +// Include the header file to get access to the MicroPython API +#include "py/dynruntime.h" + +// Helper function to compute factorial +STATIC mp_int_t factorial_helper(mp_int_t x) { + if (x == 0) { + return 1; + } + return x * factorial_helper(x - 1); +} + +// This is the function which will be called from Python, as factorial(x) +STATIC mp_obj_t factorial(mp_obj_t x_obj) { + // Extract the integer from the MicroPython input object + mp_int_t x = mp_obj_get_int(x_obj); + // Calculate the factorial + mp_int_t result = factorial_helper(x); + // Convert the result to a MicroPython integer object and return it + return mp_obj_new_int(result); +} +// Define a Python reference to the function above +STATIC MP_DEFINE_CONST_FUN_OBJ_1(factorial_obj, factorial); + +// This is the entry point and is called when the module is imported +mp_obj_t mpy_init(mp_obj_fun_bc_t *self, size_t n_args, size_t n_kw, mp_obj_t *args) { + // This must be first, it sets up the globals dict and other things + MP_DYNRUNTIME_INIT_ENTRY + + // Make the function available in the module's namespace + mp_store_global(MP_QSTR_factorial, MP_OBJ_FROM_PTR(&factorial_obj)); + + // This must be last, it restores the globals dict + MP_DYNRUNTIME_INIT_EXIT +} diff --git a/extmod/modframebuf.c b/extmod/modframebuf.c index 2524e21541..8b9eec4fc9 100644 --- a/extmod/modframebuf.c +++ b/extmod/modframebuf.c @@ -294,6 +294,9 @@ STATIC const mp_obj_type_t mp_type_framebuf; static mp_obj_framebuf_t *native_framebuf(mp_obj_t framebuf_obj) { mp_obj_t native_framebuf = mp_obj_cast_to_native_base(framebuf_obj, &mp_type_framebuf); mp_obj_assert_native_inited(native_framebuf); + if (native_framebuf == MP_OBJ_NULL) { + mp_raise_TypeError(NULL); + } return MP_OBJ_TO_PTR(native_framebuf); } diff --git a/extmod/modure.c b/extmod/modure.c index dead69586c..7836140778 100644 --- a/extmod/modure.c +++ b/extmod/modure.c @@ -272,8 +272,13 @@ MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(re_split_obj, 2, 3, re_split); #if MICROPY_PY_URE_SUB -STATIC mp_obj_t re_sub_helper(mp_obj_t self_in, size_t n_args, const mp_obj_t *args) { - mp_obj_re_t *self = MP_OBJ_TO_PTR(self_in); +STATIC mp_obj_t re_sub_helper(size_t n_args, const mp_obj_t *args) { + mp_obj_re_t *self; + if (mp_obj_is_type(args[0], &re_type)) { + self = MP_OBJ_TO_PTR(args[0]); + } else { + self = MP_OBJ_TO_PTR(mod_re_compile(1, args)); + } mp_obj_t replace = args[1]; mp_obj_t where = args[2]; mp_int_t count = 0; @@ -377,10 +382,7 @@ STATIC mp_obj_t re_sub_helper(mp_obj_t self_in, size_t n_args, const mp_obj_t *a return mp_obj_new_str_from_vstr(mp_obj_get_type(where), &vstr_return); } -STATIC mp_obj_t re_sub(size_t n_args, const mp_obj_t *args) { - return re_sub_helper(args[0], n_args, args); -} -MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(re_sub_obj, 3, 5, re_sub); +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(re_sub_obj, 3, 5, re_sub_helper); #endif @@ -439,33 +441,6 @@ STATIC mp_obj_t mod_re_compile(size_t n_args, const mp_obj_t *args) { } MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_re_compile_obj, 1, 2, mod_re_compile); -STATIC mp_obj_t mod_re_exec(bool is_anchored, uint n_args, const mp_obj_t *args) { - (void)n_args; - mp_obj_t self = mod_re_compile(1, args); - - const mp_obj_t args2[] = {self, args[1]}; - mp_obj_t match = ure_exec(is_anchored, 2, args2); - return match; -} - -STATIC mp_obj_t mod_re_match(size_t n_args, const mp_obj_t *args) { - return mod_re_exec(true, n_args, args); -} -MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_re_match_obj, 2, 4, mod_re_match); - -STATIC mp_obj_t mod_re_search(size_t n_args, const mp_obj_t *args) { - return mod_re_exec(false, n_args, args); -} -MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_re_search_obj, 2, 4, mod_re_search); - -#if MICROPY_PY_URE_SUB -STATIC mp_obj_t mod_re_sub(size_t n_args, const mp_obj_t *args) { - mp_obj_t self = mod_re_compile(1, args); - return re_sub_helper(MP_OBJ_TO_PTR(self), n_args, args); -} -MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_re_sub_obj, 3, 5, mod_re_sub); -#endif - #if !MICROPY_ENABLE_DYNRUNTIME STATIC const mp_rom_map_elem_t mp_module_re_globals_table[] = { #if CIRCUITPY @@ -474,10 +449,10 @@ STATIC const mp_rom_map_elem_t mp_module_re_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_ure) }, #endif { MP_ROM_QSTR(MP_QSTR_compile), MP_ROM_PTR(&mod_re_compile_obj) }, - { MP_ROM_QSTR(MP_QSTR_match), MP_ROM_PTR(&mod_re_match_obj) }, - { MP_ROM_QSTR(MP_QSTR_search), MP_ROM_PTR(&mod_re_search_obj) }, + { MP_ROM_QSTR(MP_QSTR_match), MP_ROM_PTR(&re_match_obj) }, + { MP_ROM_QSTR(MP_QSTR_search), MP_ROM_PTR(&re_search_obj) }, #if MICROPY_PY_URE_SUB - { MP_ROM_QSTR(MP_QSTR_sub), MP_ROM_PTR(&mod_re_sub_obj) }, + { MP_ROM_QSTR(MP_QSTR_sub), MP_ROM_PTR(&re_sub_obj) }, #endif #if MICROPY_PY_URE_DEBUG { MP_ROM_QSTR(MP_QSTR_DEBUG), MP_ROM_INT(FLAG_DEBUG) }, diff --git a/extmod/re1.5/compilecode.c b/extmod/re1.5/compilecode.c index 811742eea1..936f6ed28a 100644 --- a/extmod/re1.5/compilecode.c +++ b/extmod/re1.5/compilecode.c @@ -87,9 +87,6 @@ static const char *_compilecode(const char *re, ByteProg *prog, int sizecode) PC++; // Skip # of pair byte prog->len++; for (cnt = 0; *re != ']'; re++, cnt++) { - if (*re == '\\') { - ++re; - } if (!*re) return NULL; const char *b = re; if (*re == '\\') { diff --git a/locale/circuitpython.pot b/locale/circuitpython.pot index 2db71c8c47..0955d389c4 100644 --- a/locale/circuitpython.pot +++ b/locale/circuitpython.pot @@ -663,6 +663,10 @@ msgstr "" msgid "Cannot specify RTS or CTS in RS485 mode" msgstr "" +#: py/objslice.c +msgid "Cannot subclass slice" +msgstr "" + #: shared-module/bitbangio/SPI.c msgid "Cannot transfer without MOSI and MISO pins." msgstr "" @@ -3886,10 +3890,14 @@ msgstr "" msgid "sleep length must be non-negative" msgstr "" -#: extmod/ulab/code/ndarray.c py/objslice.c +#: extmod/ulab/code/ndarray.c msgid "slice step can't be zero" msgstr "" +#: py/objslice.c +msgid "slice step cannot be zero" +msgstr "" + #: py/nativeglue.c msgid "slice unsupported" msgstr "" diff --git a/ports/unix/Makefile b/ports/unix/Makefile index b414377fa3..8f6a5eb63c 100644 --- a/ports/unix/Makefile +++ b/ports/unix/Makefile @@ -13,8 +13,8 @@ include ../../py/mkenv.mk -include mpconfigport.mk include $(VARIANT_DIR)/mpconfigvariant.mk -# define main target -PROG = micropython +# This should be configured by the mpconfigvariant.mk +PROG ?= micropython # qstr definitions (must come before including py.mk) QSTR_DEFS = qstrdefsport.h diff --git a/ports/unix/variants/coverage/frzmpy/frzqstr.py b/ports/unix/variants/coverage/frzmpy/frzqstr.py new file mode 100644 index 0000000000..051f2a9c16 --- /dev/null +++ b/ports/unix/variants/coverage/frzmpy/frzqstr.py @@ -0,0 +1,3 @@ +# Checks for regression on MP_QSTR_NULL +def returns_NULL(): + return "NULL" diff --git a/py/compile.c b/py/compile.c index 5f66557382..ae38624c78 100644 --- a/py/compile.c +++ b/py/compile.c @@ -2809,6 +2809,7 @@ STATIC void compile_atom_expr_await(compiler_t *comp, mp_parse_node_struct_t *pn EMIT_ARG(call_method, 0, 0, 0); EMIT_ARG(load_const_tok, MP_TOKEN_KW_NONE); EMIT_ARG(yield, MP_EMIT_YIELD_FROM); + reserve_labels_for_native(comp, 3); } #endif diff --git a/py/objgenerator.c b/py/objgenerator.c index 3871dc462c..690f08a889 100644 --- a/py/objgenerator.c +++ b/py/objgenerator.c @@ -90,6 +90,7 @@ STATIC mp_obj_t native_gen_wrap_call(mp_obj_t self_in, size_t n_args, size_t n_k o->base.type = &mp_type_gen_instance; // Parse the input arguments and set up the code state + o->coroutine_generator = self->coroutine_generator; o->pend_exc = mp_const_none; o->code_state.fun_bc = self_fun; o->code_state.ip = (const byte *)prelude_offset; diff --git a/py/objslice.c b/py/objslice.c index 2201e01751..230125a8c3 100644 --- a/py/objslice.c +++ b/py/objslice.c @@ -88,6 +88,33 @@ STATIC void slice_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { } #endif +#if MICROPY_PY_BUILTINS_SLICE_ATTRS +STATIC mp_obj_t slice_make_new(const mp_obj_type_t *type, + size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) { + if (type != &mp_type_slice) { + mp_raise_NotImplementedError(translate("Cannot subclass slice")); + } + // check number of arguments + mp_arg_check_num(n_args, kw_args, 1, 3, false); + + // 1st argument is the pin + mp_obj_t start = mp_const_none; + mp_obj_t stop = mp_const_none; + mp_obj_t step = mp_const_none; + if (n_args == 1) { + stop = args[0]; + } else { + start = args[0]; + stop = args[1]; + if (n_args == 3) { + step = args[2]; + } + } + + return mp_obj_new_slice(start, stop, step); +} +#endif + #if MICROPY_PY_BUILTINS_SLICE_INDICES && !MICROPY_PY_BUILTINS_SLICE_ATTRS STATIC const mp_rom_map_elem_t slice_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_indices), MP_ROM_PTR(&slice_indices_obj) }, @@ -99,6 +126,9 @@ const mp_obj_type_t mp_type_slice = { { &mp_type_type }, .name = MP_QSTR_slice, .print = slice_print, + #if MICROPY_PY_BUILTINS_SLICE_INDICES + .make_new = slice_make_new, + #endif #if MICROPY_PY_BUILTINS_SLICE_ATTRS .attr = slice_attr, #elif MICROPY_PY_BUILTINS_SLICE_INDICES @@ -127,7 +157,7 @@ void mp_obj_slice_indices(mp_obj_t self_in, mp_int_t length, mp_bound_slice_t *r } else { step = mp_obj_get_int(self->step); if (step == 0) { - mp_raise_ValueError(MP_ERROR_TEXT("slice step can't be zero")); + mp_raise_ValueError(MP_ERROR_TEXT("slice step cannot be zero")); } } diff --git a/py/runtime.c b/py/runtime.c index 8a60b90713..9791379d79 100644 --- a/py/runtime.c +++ b/py/runtime.c @@ -30,15 +30,11 @@ #include #include - -#include "extmod/vfs.h" - #include "py/parsenum.h" #include "py/compile.h" #include "py/mperrno.h" #include "py/objstr.h" #include "py/objtuple.h" -#include "py/objtype.h" #include "py/objlist.h" #include "py/objtype.h" #include "py/objmodule.h" @@ -1066,27 +1062,27 @@ void mp_convert_member_lookup(mp_obj_t self, const mp_obj_type_t *type, mp_obj_t } dest[0] = ((mp_obj_static_class_method_t *)MP_OBJ_TO_PTR(member))->fun; dest[1] = MP_OBJ_FROM_PTR(type); + #if MICROPY_PY_BUILTINS_PROPERTY + // If self is MP_OBJ_NULL, we looking at the class itself, not an instance. + } else if (mp_obj_is_type(member, &mp_type_property) && mp_obj_is_native_type(type) && self != MP_OBJ_NULL) { + // object member is a property; delegate the load to the property + // Note: This is an optimisation for code size and execution time. + // The proper way to do it is have the functionality just below + // in a __get__ method of the property object, and then it would + // be called by the descriptor code down below. But that way + // requires overhead for the nested mp_call's and overhead for + // the code. + const mp_obj_t *proxy = mp_obj_property_get(member); + if (proxy[0] == mp_const_none) { + mp_raise_AttributeError(translate("unreadable attribute")); + } else { + dest[0] = mp_call_function_n_kw(proxy[0], 1, 0, &self); + } + #endif } else { // `member` is a value, so just return that value. dest[0] = member; } - #if MICROPY_PY_BUILTINS_PROPERTY - // If self is MP_OBJ_NULL, we looking at the class itself, not an instance. - } else if (mp_obj_is_type(member, &mp_type_property) && mp_obj_is_native_type(type) && self != MP_OBJ_NULL) { - // object member is a property; delegate the load to the property - // Note: This is an optimisation for code size and execution time. - // The proper way to do it is have the functionality just below - // in a __get__ method of the property object, and then it would - // be called by the descriptor code down below. But that way - // requires overhead for the nested mp_call's and overhead for - // the code. - const mp_obj_t *proxy = mp_obj_property_get(member); - if (proxy[0] == mp_const_none) { - mp_raise_AttributeError(translate("unreadable attribute")); - } else { - dest[0] = mp_call_function_n_kw(proxy[0], 1, 0, &self); - } - #endif } else { // `member` is a value, so just return that value. dest[0] = member; diff --git a/py/stream.c b/py/stream.c index bf3bf07fcb..b0038bba18 100644 --- a/py/stream.c +++ b/py/stream.c @@ -576,4 +576,7 @@ int mp_stream_posix_fsync(mp_obj_t stream) { return res; } +const mp_stream_p_t *mp_get_stream(mp_const_obj_t self) { + return mp_proto_get(MP_QSTR_protocol_stream, self); +} #endif diff --git a/py/stream.h b/py/stream.h index 7b392a3558..4283e685c8 100644 --- a/py/stream.h +++ b/py/stream.h @@ -99,9 +99,7 @@ MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mp_stream_ioctl_obj); #define MP_STREAM_OP_IOCTL (4) // Object is assumed to have a non-NULL stream protocol with valid r/w/ioctl methods -static inline const mp_stream_p_t *mp_get_stream(mp_const_obj_t self) { - return mp_proto_get(MP_QSTR_protocol_stream, self); -} +const mp_stream_p_t *mp_get_stream(mp_const_obj_t self); const mp_stream_p_t *mp_get_stream_raise(mp_obj_t self_in, int flags); mp_obj_t mp_stream_close(mp_obj_t stream); diff --git a/tests/basics/memoryview1.py b/tests/basics/memoryview1.py index bcf1b68b00..4c20c91f49 100644 --- a/tests/basics/memoryview1.py +++ b/tests/basics/memoryview1.py @@ -54,23 +54,6 @@ print(list(m[1:-1])) m[2] = 6 print(a) -# invalid attribute -try: - memoryview(b'a').noexist -except AttributeError: - print('AttributeError') - -try: - m4[1:3] = m2[1:3] -except ValueError: - print("ValueError") - -# invalid assignment on RHS -try: - memoryview(array.array('i'))[0:2] = b'1234' -except ValueError: - print('ValueError') - # invalid attribute try: memoryview(b'a').noexist diff --git a/tests/micropython/import_mpy_native_gc.py b/tests/micropython/import_mpy_native_gc.py index e8fac8f179..cd19bb035c 100644 --- a/tests/micropython/import_mpy_native_gc.py +++ b/tests/micropython/import_mpy_native_gc.py @@ -49,9 +49,9 @@ class UserFS: # by the required value of sys.implementation.mpy. features0_file_contents = { # -march=x64 -mcache-lookup-bc - 0xB05: b'M\x05\x0b\x1f \x84b\xe9/\x00\x00\x00SH\x8b\x1ds\x00\x00\x00\xbe\x02\x00\x00\x00\xffS\x18\xbf\x01\x00\x00\x00H\x85\xc0u\x0cH\x8bC \xbe\x02\x00\x00\x00[\xff\xe0H\x0f\xaf\xf8H\xff\xc8\xeb\xe6ATUSH\x8b\x1dA\x00\x00\x00H\x8b\x7f\x08L\x8bc(A\xff\xd4H\x8d5\x1f\x00\x00\x00H\x89\xc5H\x8b\x05-\x00\x00\x00\x0f\xb78\xffShH\x89\xefA\xff\xd4H\x8b\x03[]A\\\xc3\x00\x00\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x90\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x84@\x12factorial\x10\x00\x00\r \x01"\x9f\x1c\x01\x1e\xff', + 0xB05: b'M\x05\x0b\x1f \x84b\xe9/\x00\x00\x00SH\x8b\x1ds\x00\x00\x00\xbe\x02\x00\x00\x00\xffS\x18\xbf\x01\x00\x00\x00H\x85\xc0u\x0cH\x8bC \xbe\x02\x00\x00\x00[\xff\xe0H\x0f\xaf\xf8H\xff\xc8\xeb\xe6ATUSH\x8b\x1dA\x00\x00\x00H\x8b\x7f\x08H\x8bk(\xff\xd5H\x8d5 \x00\x00\x00I\x89\xc4H\x8b\x05.\x00\x00\x00\x0f\xb78\xffShL\x89\xe7\xff\xd5H\x8b\x03[]A\\\xc3\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x90\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x84@\x12factorial \x00\x00\r \x01"\x9f\x1c\x01\x1e\xff', # -march=armv7m - 0x1605: b"M\x05\x16\x1f \x84\x12\x1a\xe0\x00\x00\x13\xb5\nK\nJ{D\x9cX\x02!\xe3h\x98G\x03F\x01 3\xb9\x02!#i\x01\x93\x02\xb0\xbd\xe8\x10@\x18GXC\x01;\xf4\xe7\x00\xbfj\x00\x00\x00\x00\x00\x00\x00\xf8\xb5\tN\tK~D\xf4X@hgi\xb8G\x05F\x07K\x07I\xf2XyD\x10\x88ck\x98G(F\xb8G h\xf8\xbd6\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x1c\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x80\x00\x00\x00\x00\x00\x00\x00\x01\x84\x00\x12factorial\x10\x00\x00\r<\x01>\x9f8\x01:\xff", + 0x1605: b"M\x05\x16\x1f \x84\x12\x1a\xe0\x00\x00\x13\xb5\nK\nJ{D\x9cX\x02!\xe3h\x98G\x03F\x01 3\xb9\x02!#i\x01\x93\x02\xb0\xbd\xe8\x10@\x18GXC\x01;\xf4\xe7\x00\xbfj\x00\x00\x00\x00\x00\x00\x00\xf8\xb5\tN\tK~D\xf4X@hgi\xb8G\x05F\x07K\x07I\xf2XyD\x10\x88ck\x98G(F\xb8G h\xf8\xbd6\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x1c\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x80\x00\x00\x00\x00\x00\x00\x00\x01\x84\x00\x12factorial \x00\x00\r<\x01>\x9f8\x01:\xff", } # Populate other armv7m-derived archs based on armv7m. diff --git a/tests/micropython/import_mpy_native_x64.py b/tests/micropython/import_mpy_native_x64.py index d0de507d44..cec06a8f05 100644 --- a/tests/micropython/import_mpy_native_x64.py +++ b/tests/micropython/import_mpy_native_x64.py @@ -90,7 +90,7 @@ user_files = { b'\x12' # n bytes(=4), viper code b'\x00\x00\x00\x00' # dummy machine code b'\x00' # n_qstr - b'\x70' # scope_flags: VIPERBSS | VIPERRODATA | VIPERRELOC + b'\x81\x60' # scope_flags: VIPERBSS | VIPERRODATA | VIPERRELOC b'\x00\x00' # n_obj, n_raw_code b'\x06rodata' # rodata, 6 bytes b'\x04' # bss, 4 bytes diff --git a/tests/run-tests b/tests/run-tests index c1a2780dad..22d854c72f 100755 --- a/tests/run-tests +++ b/tests/run-tests @@ -448,7 +448,8 @@ def run_tests(pyb, tests, args, result_dir, num_threads=1): # Remove them from the below when they work if args.emit == 'native': skip_tests.update({'basics/%s.py' % t for t in 'gen_yield_from_close generator_name'.split()}) # require raise_varargs, generator name - skip_tests.update({'basics/async_%s.py' % t for t in 'with with2 with_break with_return'.split()}) # require yield + skip_tests.update({'basics/async_%s.py' % t for t in 'with with2 with_break with_return'.split()}) # require async_with + skip_tests.update({'basics/%s.py' % t for t in 'try_reraise try_reraise2'.split()}) # require raise_varargs skip_tests.add('basics/annotate_var.py') # requires checking for unbound local skip_tests.add('basics/del_deref.py') # requires checking for unbound local skip_tests.add('basics/del_local.py') # requires checking for unbound local @@ -464,7 +465,6 @@ def run_tests(pyb, tests, args, result_dir, num_threads=1): skip_tests.add('micropython/heapalloc_traceback.py') # because native doesn't have proper traceback info skip_tests.add('micropython/opt_level_lineno.py') # native doesn't have proper traceback info skip_tests.add('micropython/schedule.py') # native code doesn't check pending events - # skip_tests.add('../extmod/ulab/tests/argminmax.py') # requires yield def run_one_test(test_file): test_file = test_file.replace('\\', '/')