From 71364bb355ef6f739923e52404f8d203b4cc4cc6 Mon Sep 17 00:00:00 2001 From: Andrew Leech Date: Sat, 8 Mar 2025 19:10:57 +1100 Subject: [PATCH 1/8] py/modatexit: Add atexit module. Signed-off-by: Andrew Leech --- docs/library/atexit.rst | 19 +++++ docs/library/index.rst | 1 + py/modatexit.c | 101 +++++++++++++++++++++++++ py/modatexit.h | 33 ++++++++ py/mpconfig.h | 8 ++ py/py.cmake | 1 + py/py.mk | 1 + py/runtime.c | 4 + tests/misc/atexit.py | 28 +++++++ tests/misc/atexit.py.exp | 3 + tests/misc/atexit_unregister.py | 28 +++++++ tests/misc/atexit_unregister.py.exp | 2 + tests/ports/unix/extra_coverage.py.exp | 6 +- 13 files changed, 232 insertions(+), 3 deletions(-) create mode 100644 docs/library/atexit.rst create mode 100644 py/modatexit.c create mode 100644 py/modatexit.h create mode 100644 tests/misc/atexit.py create mode 100644 tests/misc/atexit.py.exp create mode 100644 tests/misc/atexit_unregister.py create mode 100644 tests/misc/atexit_unregister.py.exp diff --git a/docs/library/atexit.rst b/docs/library/atexit.rst new file mode 100644 index 0000000000000..1d85dc88ac756 --- /dev/null +++ b/docs/library/atexit.rst @@ -0,0 +1,19 @@ +:mod:`atexit` -- exit handlers +============================== + +.. module:: atexit + :synopsis: The atexit module defines functions to register and unregister cleanup functions. + +|see_cpython_module| :mod:`python:atexit`. + +Functions +--------- + +.. function:: register(func) + + Register func as a function to be executed on :ref:`soft_reset` or exit on unix/windows ports. + Functions are executed in "lifo" registration order, ie the last registered is the first executed. + +.. function:: unregister(func) + + Remove func from the list of functions to be run at interpreter shutdown. Not enabled on all ports. diff --git a/docs/library/index.rst b/docs/library/index.rst index 2919378ce13b2..33d910075d10a 100644 --- a/docs/library/index.rst +++ b/docs/library/index.rst @@ -57,6 +57,7 @@ library. :maxdepth: 1 array.rst + atexit.rst asyncio.rst binascii.rst builtins.rst diff --git a/py/modatexit.c b/py/modatexit.c new file mode 100644 index 0000000000000..61f2b9f97ea63 --- /dev/null +++ b/py/modatexit.c @@ -0,0 +1,101 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2025 Andrew Leech + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include +#include + +#include "py/obj.h" +#include "py/runtime.h" +#include "py/modatexit.h" +#include "shared/runtime/pyexec.h" + +#if MICROPY_PY_ATEXIT + +mp_obj_t mp_atexit_register(mp_obj_t function) { + if (!mp_obj_is_callable(function)) { + mp_raise_ValueError(MP_ERROR_TEXT("function not callable")); + } + if (MP_STATE_VM(atexit_handlers) == NULL) { + MP_STATE_VM(atexit_handlers) = MP_OBJ_TO_PTR(mp_obj_new_list(0, NULL)); + } + mp_obj_list_append(MP_OBJ_FROM_PTR(MP_STATE_VM(atexit_handlers)), function); + // return the passed in function so this can be used as a decorator + return function; +} +static MP_DEFINE_CONST_FUN_OBJ_1(mp_atexit_register_obj, mp_atexit_register); + +#if MICROPY_PY_ATEXIT_UNREGISTER +mp_obj_t mp_atexit_unregister(mp_obj_t function) { + nlr_buf_t nlr; + // ValueError is thrown when function is no longer in the list + if (nlr_push(&nlr) == 0) { + while (MP_STATE_VM(atexit_handlers) != NULL) { + mp_obj_list_remove(MP_OBJ_FROM_PTR(MP_STATE_VM(atexit_handlers)), function); + } + } + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_1(mp_atexit_unregister_obj, mp_atexit_unregister); +#endif + +// port specific shutdown procedures should cLl this +// to run any registered atexit handlers. +int mp_atexit_execute(void) { + int exit_code = 0; + if (MP_STATE_VM(atexit_handlers) != NULL) { + mp_obj_list_t *list = MP_STATE_VM(atexit_handlers); + for (int i = list->len - 1; i >= 0; i--) { + mp_obj_t function = list->items[i]; + + nlr_buf_t nlr; + if (nlr_push(&nlr) == 0) { + mp_call_function_0(function); + } else { + exit_code = pyexec_handle_uncaught_exception(nlr.ret_val); + } + } + } + return exit_code; +} + +static const mp_rom_map_elem_t mp_module_atexit_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_atexit) }, + { MP_ROM_QSTR(MP_QSTR_register), MP_ROM_PTR(&mp_atexit_register_obj) }, + #if MICROPY_PY_ATEXIT_UNREGISTER + { MP_ROM_QSTR(MP_QSTR_unregister), MP_ROM_PTR(&mp_atexit_unregister_obj) }, + #endif +}; +static MP_DEFINE_CONST_DICT(mp_module_atexit_globals, mp_module_atexit_globals_table); + +const mp_obj_module_t mp_module_atexit = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&mp_module_atexit_globals, +}; + +MP_REGISTER_ROOT_POINTER(mp_obj_list_t * atexit_handlers); +MP_REGISTER_MODULE(MP_QSTR_atexit, mp_module_atexit); + +#endif // MICROPY_PY_ATEXIT diff --git a/py/modatexit.h b/py/modatexit.h new file mode 100644 index 0000000000000..a21333781f741 --- /dev/null +++ b/py/modatexit.h @@ -0,0 +1,33 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2024 Andrew Leech + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef MICROPY_INCLUDED_PY_MODATEXIT_H +#define MICROPY_INCLUDED_PY_MODATEXIT_H + +int mp_atexit_execute(void); +mp_obj_t mp_atexit_register(mp_obj_t callback); +mp_obj_t mp_atexit_unregister(mp_obj_t callback); + +#endif // MICROPY_INCLUDED_PY_MODATEXIT_H diff --git a/py/mpconfig.h b/py/mpconfig.h index 05f39b3605bf3..50809a1b31604 100644 --- a/py/mpconfig.h +++ b/py/mpconfig.h @@ -1351,6 +1351,14 @@ typedef double mp_float_t; #define MICROPY_PY_ARRAY_SLICE_ASSIGN (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) #endif +// Whether to provide "atexit" module. +#ifndef MICROPY_PY_ATEXIT +#define MICROPY_PY_ATEXIT (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) +#endif +#ifndef MICROPY_PY_ATEXIT_UNREGISTER +#define MICROPY_PY_ATEXIT_UNREGISTER (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EVERYTHING) +#endif + // Whether to support attrtuple type (MicroPython extension) // It provides space-efficient tuples with attribute access #ifndef MICROPY_PY_ATTRTUPLE diff --git a/py/py.cmake b/py/py.cmake index 1c81ed4c58f32..914425a85645c 100644 --- a/py/py.cmake +++ b/py/py.cmake @@ -42,6 +42,7 @@ set(MICROPY_SOURCE_PY ${MICROPY_PY_DIR}/malloc.c ${MICROPY_PY_DIR}/map.c ${MICROPY_PY_DIR}/modarray.c + ${MICROPY_PY_DIR}/modatexit.c ${MICROPY_PY_DIR}/modbuiltins.c ${MICROPY_PY_DIR}/modcmath.c ${MICROPY_PY_DIR}/modcollections.c diff --git a/py/py.mk b/py/py.mk index e352d89792bfd..2624d9ae1c4c2 100644 --- a/py/py.mk +++ b/py/py.mk @@ -191,6 +191,7 @@ PY_CORE_O_BASENAME = $(addprefix py/,\ builtinevex.o \ builtinhelp.o \ modarray.o \ + modatexit.o \ modbuiltins.o \ modcollections.o \ modgc.o \ diff --git a/py/runtime.c b/py/runtime.c index 58819819ad025..2fd57deceed00 100644 --- a/py/runtime.c +++ b/py/runtime.c @@ -171,6 +171,10 @@ void mp_init(void) { MP_STATE_VM(sys_mutable[MP_SYS_MUTABLE_TRACEBACKLIMIT]) = MP_OBJ_NEW_SMALL_INT(1000); #endif + #if MICROPY_PY_ATEXIT + MP_STATE_VM(atexit_handlers) = NULL; + #endif + #if MICROPY_PY_BLUETOOTH MP_STATE_VM(bluetooth) = MP_OBJ_NULL; #endif diff --git a/tests/misc/atexit.py b/tests/misc/atexit.py new file mode 100644 index 0000000000000..f87d9b74fcd06 --- /dev/null +++ b/tests/misc/atexit.py @@ -0,0 +1,28 @@ +# test atexit module + +try: + import atexit +except ImportError: + print("SKIP") + raise SystemExit + +some_var = None + + +@atexit.register +def do_at_exit_1(): + print("atexit: registered first") + + +def do_at_exit_2(): + global some_var + print("atexit: registered last with access to", some_var) + + +atexit.register(do_at_exit_2) + +some_var = "local var" +print("printed before exit") + +# Trigger micropython shutdown/soft-reset now. +raise SystemExit diff --git a/tests/misc/atexit.py.exp b/tests/misc/atexit.py.exp new file mode 100644 index 0000000000000..9fb195a6dc9b8 --- /dev/null +++ b/tests/misc/atexit.py.exp @@ -0,0 +1,3 @@ +printed before exit +atexit: registered last with access to local var +atexit: registered first diff --git a/tests/misc/atexit_unregister.py b/tests/misc/atexit_unregister.py new file mode 100644 index 0000000000000..e9e6ec149a7b6 --- /dev/null +++ b/tests/misc/atexit_unregister.py @@ -0,0 +1,28 @@ +# test atexit.unregister is enabled. + +try: + import atexit + + atexit.unregister +except (ImportError, AttributeError): + print("SKIP") + raise SystemExit + + +def do_at_exit(): + print("should not printed at exit") + + +atexit.register(do_at_exit) + + +@atexit.register +def do_at_exit_2(): + print("should be printed") + + +atexit.register(do_at_exit) + +atexit.unregister(do_at_exit) + +print("printed before exit") diff --git a/tests/misc/atexit_unregister.py.exp b/tests/misc/atexit_unregister.py.exp new file mode 100644 index 0000000000000..597020dabcc20 --- /dev/null +++ b/tests/misc/atexit_unregister.py.exp @@ -0,0 +1,2 @@ +printed before exit +should be printed diff --git a/tests/ports/unix/extra_coverage.py.exp b/tests/ports/unix/extra_coverage.py.exp index 5ff947e883d37..3112a01efcecc 100644 --- a/tests/ports/unix/extra_coverage.py.exp +++ b/tests/ports/unix/extra_coverage.py.exp @@ -51,9 +51,9 @@ ame__ port builtins micropython _asyncio _thread -array binascii btree cexample -cmath collections cppexample cryptolib -deflate errno example_package +array atexit binascii btree +cexample cmath collections cppexample +cryptolib deflate errno example_package ffi framebuf gc hashlib heapq io json machine marshal math os platform From 37bd082bc2e9171dfc9d9519cd98f4408496d5e9 Mon Sep 17 00:00:00 2001 From: Andrew Leech Date: Tue, 29 Oct 2024 04:23:24 +1100 Subject: [PATCH 2/8] examples/usercmodule: Add atexit, __init__ and root pointer to cexample. Signed-off-by: Andrew Leech --- examples/usercmodule/cexample/examplemodule.c | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/examples/usercmodule/cexample/examplemodule.c b/examples/usercmodule/cexample/examplemodule.c index 83cc3b27c058e..6f9bd5c55f578 100644 --- a/examples/usercmodule/cexample/examplemodule.c +++ b/examples/usercmodule/cexample/examplemodule.c @@ -1,9 +1,12 @@ // Include MicroPython API. #include "py/runtime.h" +#include "py/modatexit.h" // Used to get the time in the Timer class example. #include "py/mphal.h" +static bool custom_library_initialised = false; + // This is the function which will be called from Python as cexample.add_ints(a, b). static mp_obj_t example_add_ints(mp_obj_t a_obj, mp_obj_t b_obj) { // Extract the ints from the micropython input objects. @@ -149,6 +152,49 @@ MP_DEFINE_CONST_OBJ_TYPE( locals_dict, &example_Timer_locals_dict ); +// If any data/struct needs to be allocated it should +// be stored in a root pointer to inform the gc not to +// collect it. +// Note this will be reset during a soft-reset. +MP_REGISTER_ROOT_POINTER(uint8_t *example_c_mod_buffer); + +MP_DECLARE_CONST_FUN_OBJ_0(example_deinit_obj); + +// This __init__ function will be run during module import if +// micropython is built with MICROPY_MODULE_BUILTIN_INIT enabled. +static mp_obj_t example___init__(void) { + if (!custom_library_initialised) { + // __init__ for builtins is called each time the module is imported, + // so ensure that initialisation only happens once. + custom_library_initialised = true; + // this is a good place to create any buffers needed and initialise c libraries or + // hardware peripherals that only need setup once before use by your application. + MP_STATE_VM(example_c_mod_buffer) = m_malloc(8); + + #if MICROPY_PY_ATEXIT + mp_atexit_register(MP_OBJ_FROM_PTR(&example_deinit_obj)); + #endif + } + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_0(example___init___obj, example___init__); + +// This will be run during exit / soft-reset (if MICROPY_PY_ATEXIT and +// MICROPY_MODULE_BUILTIN_INIT are enabled) and can be used to reset any +// global static verifies +static mp_obj_t example_deinit(void) { + // This is registered in __init__ be run during exit / soft-reset. + if (custom_library_initialised) { + // global / static are not automatically reset during exit so reset here. + custom_library_initialised = false; + // this can also be used to reset hardware peripherals or c library + // resources such that they're safe to re-initialise in __init__ + // again after the soft-reset is complete. + } + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_0(example_deinit_obj, example_deinit); + // Define all attributes of the module. // Table entries are key/value pairs of the attribute name (a string) // and the MicroPython object reference. @@ -156,6 +202,8 @@ MP_DEFINE_CONST_OBJ_TYPE( // optimized to word-sized integers by the build system (interned strings). static const mp_rom_map_elem_t example_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_cexample) }, + { MP_ROM_QSTR(MP_QSTR___init__), MP_ROM_PTR(&example___init___obj) }, + { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&example_deinit_obj) }, { MP_ROM_QSTR(MP_QSTR_add_ints), MP_ROM_PTR(&example_add_ints_obj) }, { MP_ROM_QSTR(MP_QSTR_Timer), MP_ROM_PTR(&example_type_Timer) }, { MP_ROM_QSTR(MP_QSTR_AdvancedTimer), MP_ROM_PTR(&example_type_AdvancedTimer) }, From 88bbdd7ea6d355a2681856a9506f23139a00a4e4 Mon Sep 17 00:00:00 2001 From: Andrew Leech Date: Thu, 7 Nov 2024 21:18:38 +1100 Subject: [PATCH 3/8] shared/runtime/pyexec: Add default exception handler function. Also improve consistency with the matching functionality in the unix port. Signed-off-by: Andrew Leech --- shared/runtime/pyexec.c | 29 +++++++++++++++++++++-------- shared/runtime/pyexec.h | 1 + 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/shared/runtime/pyexec.c b/shared/runtime/pyexec.c index c828c75817940..86111749c57bf 100644 --- a/shared/runtime/pyexec.c +++ b/shared/runtime/pyexec.c @@ -39,6 +39,7 @@ #include "irq.h" #include "usb.h" #endif +#include "extmod/modplatform.h" #include "shared/readline/readline.h" #include "shared/runtime/pyexec.h" #include "genhdr/mpversion.h" @@ -58,6 +59,25 @@ static bool repl_display_debugging_info = 0; #define EXEC_FLAG_SOURCE_IS_READER (1 << 6) #define EXEC_FLAG_NO_INTERRUPT (1 << 7) +// If exc is SystemExit, return value where FORCED_EXIT bit set, +// and lower 8 bits are SystemExit value. For all other exceptions, +// return 1. +int pyexec_handle_uncaught_exception(mp_obj_base_t *exc) { + // check for SystemExit + if (mp_obj_is_subclass_fast(MP_OBJ_FROM_PTR(exc->type), MP_OBJ_FROM_PTR(&mp_type_SystemExit))) { + + mp_obj_t exit_val = mp_obj_exception_get_value(MP_OBJ_FROM_PTR(exc)); + mp_int_t val = 0; + if (exit_val != mp_const_none && !mp_obj_get_int_maybe(exit_val, &val)) { + val = 1; + } + return PYEXEC_FORCED_EXIT | (val & 255); + } + + mp_obj_print_exception(&mp_plat_print, MP_OBJ_FROM_PTR(exc)); + return 1; +} + // parses, compiles and executes the code in the lexer // frees the lexer before returning // EXEC_FLAG_PRINT_EOF prints 2 EOF chars: 1 after normal output, 1 after exception output @@ -139,14 +159,7 @@ static int parse_compile_execute(const void *source, mp_parse_input_kind_t input mp_hal_stdout_tx_strn("\x04", 1); } - // check for SystemExit - if (mp_obj_is_subclass_fast(MP_OBJ_FROM_PTR(((mp_obj_base_t *)nlr.ret_val)->type), MP_OBJ_FROM_PTR(&mp_type_SystemExit))) { - // at the moment, the value of SystemExit is unused - ret = PYEXEC_FORCED_EXIT; - } else { - mp_obj_print_exception(&mp_plat_print, MP_OBJ_FROM_PTR(nlr.ret_val)); - ret = 0; - } + pyexec_handle_uncaught_exception(nlr.ret_val); } #if MICROPY_REPL_INFO diff --git a/shared/runtime/pyexec.h b/shared/runtime/pyexec.h index 95f4481626611..c4588328351e7 100644 --- a/shared/runtime/pyexec.h +++ b/shared/runtime/pyexec.h @@ -36,6 +36,7 @@ typedef enum { extern pyexec_mode_kind_t pyexec_mode_kind; #define PYEXEC_FORCED_EXIT (0x100) +int pyexec_handle_uncaught_exception(mp_obj_base_t *exc); int pyexec_raw_repl(void); int pyexec_friendly_repl(void); From 79ea5a78637320fe487d1eabf35c1a18a21ad623 Mon Sep 17 00:00:00 2001 From: Andrew Leech Date: Thu, 31 Oct 2024 20:55:19 +1100 Subject: [PATCH 4/8] unix/main: Update handle_uncaught_exception for consistency with pyexec. Signed-off-by: Andrew Leech --- ports/unix/main.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/ports/unix/main.c b/ports/unix/main.c index 0d137fe1b58ea..4376b1415bbfb 100644 --- a/ports/unix/main.c +++ b/ports/unix/main.c @@ -52,6 +52,7 @@ #include "extmod/modplatform.h" #include "extmod/vfs.h" #include "extmod/vfs_posix.h" +#include "shared/runtime/pyexec.h" #include "genhdr/mpversion.h" #include "input.h" @@ -87,11 +88,10 @@ static void stderr_print_strn(void *env, const char *str, size_t len) { const mp_print_t mp_stderr_print = {NULL, stderr_print_strn}; -#define FORCED_EXIT (0x100) // If exc is SystemExit, return value where FORCED_EXIT bit set, // and lower 8 bits are SystemExit value. For all other exceptions, // return 1. -static int handle_uncaught_exception(mp_obj_base_t *exc) { +int pyexec_handle_uncaught_exception(mp_obj_base_t *exc) { // check for SystemExit if (mp_obj_is_subclass_fast(MP_OBJ_FROM_PTR(exc->type), MP_OBJ_FROM_PTR(&mp_type_SystemExit))) { // None is an exit value of 0; an int is its value; anything else is 1 @@ -100,7 +100,7 @@ static int handle_uncaught_exception(mp_obj_base_t *exc) { if (exit_val != mp_const_none && !mp_obj_get_int_maybe(exit_val, &val)) { val = 1; } - return FORCED_EXIT | (val & 255); + return PYEXEC_FORCED_EXIT | (val & 255); } // Report all other exceptions @@ -171,7 +171,7 @@ static int execute_from_lexer(int source_kind, const void *source, mp_parse_inpu // uncaught exception mp_hal_set_interrupt_char(-1); mp_handle_pending(false); - return handle_uncaught_exception(nlr.ret_val); + return pyexec_handle_uncaught_exception(nlr.ret_val); } } @@ -271,7 +271,7 @@ static int do_repl(void) { mp_hal_stdio_mode_orig(); ret = execute_from_lexer(LEX_SRC_VSTR, &line, parse_input_kind, true); - if (ret & FORCED_EXIT) { + if (ret & PYEXEC_FORCED_EXIT) { return ret; } } @@ -299,7 +299,7 @@ static int do_repl(void) { int ret = execute_from_lexer(LEX_SRC_STR, line, MP_PARSE_SINGLE_INPUT, true); free(line); - if (ret & FORCED_EXIT) { + if (ret & PYEXEC_FORCED_EXIT) { return ret; } } @@ -684,7 +684,7 @@ MP_NOINLINE int main_(int argc, char **argv) { nlr_pop(); } else { // uncaught exception - return handle_uncaught_exception(nlr.ret_val) & 0xff; + return pyexec_handle_uncaught_exception(nlr.ret_val) & 0xff; } // If this module is a package, see if it has a `__main__.py`. From f431ecf3ab88e4780da0e0227708fc9a572c5b76 Mon Sep 17 00:00:00 2001 From: Andrew Leech Date: Thu, 7 Nov 2024 21:19:57 +1100 Subject: [PATCH 5/8] unix/main.c: Integrate atexit to be run while exiting. Signed-off-by: Andrew Leech --- ports/unix/main.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ports/unix/main.c b/ports/unix/main.c index 4376b1415bbfb..bf5467b4de135 100644 --- a/ports/unix/main.c +++ b/ports/unix/main.c @@ -45,6 +45,7 @@ #include "py/gc.h" #include "py/objstr.h" #include "py/cstack.h" +#include "py/modatexit.h" #include "py/mperrno.h" #include "py/mphal.h" #include "py/mpthread.h" @@ -756,6 +757,11 @@ MP_NOINLINE int main_(int argc, char **argv) { } } + #if MICROPY_PY_ATEXIT + int atexit_code = mp_atexit_execute(); + ret = (atexit_code != 0) ? atexit_code : ret; + #endif + #if MICROPY_PY_SYS_SETTRACE MP_STATE_THREAD(prof_trace_callback) = MP_OBJ_NULL; #endif From 1ce8827790fc2980a4fea54bdd953bdf90c47886 Mon Sep 17 00:00:00 2001 From: Andrew Leech Date: Fri, 22 Nov 2024 17:26:09 +1100 Subject: [PATCH 6/8] shared/runtime/pyexec: Integrate atexit to be run during exit process. Signed-off-by: Andrew Leech --- shared/runtime/pyexec.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/shared/runtime/pyexec.c b/shared/runtime/pyexec.c index 86111749c57bf..a95b9b6a15042 100644 --- a/shared/runtime/pyexec.c +++ b/shared/runtime/pyexec.c @@ -34,6 +34,7 @@ #include "py/repl.h" #include "py/gc.h" #include "py/frozenmod.h" +#include "py/modatexit.h" #include "py/mphal.h" #if MICROPY_HW_ENABLE_USB #include "irq.h" @@ -188,6 +189,11 @@ static int parse_compile_execute(const void *source, mp_parse_input_kind_t input mp_hal_stdout_tx_strn("\x04", 1); } + #if MICROPY_PY_ATEXIT + int atexit_code = mp_atexit_execute(); + ret = (atexit_code != 0) ? atexit_code : ret; + #endif + #ifdef MICROPY_BOARD_AFTER_PYTHON_EXEC MICROPY_BOARD_AFTER_PYTHON_EXEC(input_kind, exec_flags, nlr.ret_val, &ret); #endif From 763c1d7c1018b496aca46a3e7d9a8313725345bc Mon Sep 17 00:00:00 2001 From: Andrew Leech Date: Mon, 10 Mar 2025 22:22:00 +1100 Subject: [PATCH 7/8] webassembly: Disable atexit module. There is no common deinit routine to add support in. Signed-off-by: Andrew Leech --- ports/webassembly/mpconfigport.h | 1 + 1 file changed, 1 insertion(+) diff --git a/ports/webassembly/mpconfigport.h b/ports/webassembly/mpconfigport.h index ab56162ca2b07..a6ba12eb23aab 100644 --- a/ports/webassembly/mpconfigport.h +++ b/ports/webassembly/mpconfigport.h @@ -54,6 +54,7 @@ #define MICROPY_FLOAT_IMPL (MICROPY_FLOAT_IMPL_DOUBLE) #define MICROPY_USE_INTERNAL_ERRNO (1) #define MICROPY_USE_INTERNAL_PRINTF (0) +#define MICROPY_PY_ATEXIT (0) #define MICROPY_EPOCH_IS_1970 (1) #define MICROPY_PY_ASYNCIO_TASK_QUEUE_PUSH_CALLBACK (1) From 4bd2fa2dac7a30ac6a01b6e9ce11baf4a4e4728c Mon Sep 17 00:00:00 2001 From: Andrew Leech Date: Tue, 11 Mar 2025 00:46:08 +1100 Subject: [PATCH 8/8] qemu/main.c: Integrate atexit to be run while exiting. Signed-off-by: Andrew Leech --- ports/qemu/main.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ports/qemu/main.c b/ports/qemu/main.c index 75c6fe4cdc0d8..501e7703a6015 100644 --- a/ports/qemu/main.c +++ b/ports/qemu/main.c @@ -30,6 +30,7 @@ #include "py/runtime.h" #include "py/stackctrl.h" #include "py/gc.h" +#include "py/modatexit.h" #include "py/mperrno.h" #include "shared/runtime/gchelper.h" #include "shared/runtime/pyexec.h" @@ -60,6 +61,10 @@ int main(int argc, char **argv) { } } + #if MICROPY_PY_ATEXIT + mp_atexit_execute(); + #endif + mp_printf(&mp_plat_print, "MPY: soft reboot\n"); gc_sweep_all(); pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy