Skip to content

Commit

Permalink
shared/runtime/pyexec: Make a raised SystemExit always do a forced exit.
Browse files Browse the repository at this point in the history
The current situation with SystemExit and soft reset is the following:
- `sys.exit()` follows CPython and just raises `SystemExit`.
- On the unix port, raising `SystemExit` quits the application/MicroPython,
  whether at the REPL or in code (this follows CPython behaviour).
- On bare-metal ports, raising `SystemExit` at the REPL does nothing,
  raising it in code will stop the code and drop into the REPL.
- `machine.soft_reset()` raises `SystemExit` but with a special flag set,
  and bare-metal targets check this flag when it propagates to the
  top-level and do a soft reset when they receive it.

The original idea here was that a bare-metal target can't "quit" like the
unix port can, and so dropping to the REPL was considered the same as
"quit".  But this bare-metal behaviour is arguably inconsistent with unix,
and "quit" should mean terminate everything, including REPL access.

This commit changes the behaviour to the following, which is more
consistent:
- Raising `SystemExit` on a bare-metal port will do a soft reset (unless
  the exception is caught by the application).
- `machine.soft_reset()` is now equivalent to `sys.exit()`.
- unix port behaviour remains unchanged.

Tested running the test suite on an stm32 board and everything still
passes, in particular tests that skip by raising `SystemExit` still
correctly skip.

Signed-off-by: Damien George <damien@micropython.org>
  • Loading branch information
dpgeorge committed Jul 20, 2024
1 parent a734ee9 commit 69c25ea
Show file tree
Hide file tree
Showing 5 changed files with 1 addition and 17 deletions.
1 change: 0 additions & 1 deletion extmod/modmachine.c
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,6 @@ NORETURN static void mp_machine_deepsleep(size_t n_args, const mp_obj_t *args);
#endif

static mp_obj_t machine_soft_reset(void) {
pyexec_system_exit = PYEXEC_FORCED_EXIT;
mp_raise_type(&mp_type_SystemExit);
}
static MP_DEFINE_CONST_FUN_OBJ_0(machine_soft_reset_obj, machine_soft_reset);
Expand Down
3 changes: 0 additions & 3 deletions ports/qemu-arm/modmachine.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,6 @@
// This file is never compiled standalone, it's included directly from
// extmod/modmachine.c via MICROPY_PY_MACHINE_INCLUDEFILE.

// This variable is needed for machine.soft_reset(), but the variable is otherwise unused.
int pyexec_system_exit = 0;

static void mp_machine_idle(void) {
// Do nothing.
}
3 changes: 0 additions & 3 deletions ports/unix/modmachine.c
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,6 @@
#define MICROPY_PAGE_MASK (MICROPY_PAGE_SIZE - 1)
#endif

// This variable is needed for machine.soft_reset(), but the variable is otherwise unused.
int pyexec_system_exit = 0;

uintptr_t mod_machine_mem_get_addr(mp_obj_t addr_o, uint align) {
uintptr_t addr = mp_obj_get_int_truncated(addr_o);
if ((addr & (align - 1)) != 0) {
Expand Down
6 changes: 1 addition & 5 deletions shared/runtime/pyexec.c
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@
#include "genhdr/mpversion.h"

pyexec_mode_kind_t pyexec_mode_kind = PYEXEC_MODE_FRIENDLY_REPL;
int pyexec_system_exit = 0;

#if MICROPY_REPL_INFO
static bool repl_display_debugging_info = 0;
Expand Down Expand Up @@ -74,9 +73,6 @@ static int parse_compile_execute(const void *source, mp_parse_input_kind_t input
MICROPY_BOARD_BEFORE_PYTHON_EXEC(input_kind, exec_flags);
#endif

// by default a SystemExit exception returns 0
pyexec_system_exit = 0;

nlr_buf_t nlr;
nlr.ret_val = NULL;
if (nlr_push(&nlr) == 0) {
Expand Down Expand Up @@ -146,7 +142,7 @@ static int parse_compile_execute(const void *source, mp_parse_input_kind_t input
// 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_system_exit;
ret = PYEXEC_FORCED_EXIT;
} else {
mp_obj_print_exception(&mp_plat_print, MP_OBJ_FROM_PTR(nlr.ret_val));
ret = 0;
Expand Down
5 changes: 0 additions & 5 deletions shared/runtime/pyexec.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,6 @@ typedef enum {

extern pyexec_mode_kind_t pyexec_mode_kind;

// Set this to the value (eg PYEXEC_FORCED_EXIT) that will be propagated through
// the pyexec functions if a SystemExit exception is raised by the running code.
// It will reset to 0 at the start of each execution (eg each REPL entry).
extern int pyexec_system_exit;

#define PYEXEC_FORCED_EXIT (0x100)

int pyexec_raw_repl(void);
Expand Down

0 comments on commit 69c25ea

Please sign in to comment.