Skip to content

Commit 1e5da26

Browse files
RealJohnSmithdpgeorge
authored andcommitted
shared/runtime: Set exit code according to the SystemExit exception.
Add abort setup code `nlr_set_abort` to the standard runtime executor. This makes the standard runtime respond to abort signal without any further modifications. - When aborted, the program exits with 137 exit code (configurable, same as posix sig abort), to differentiate from a normal shutdown. - When exited by exception/crash, the program will exit with exit code 1 (configurable). - When exited by exception KeyboardInterrupt, the program will exit with exit code 130 (configurable, same as posix sig int). - When exited with a exit code (from Python environment), this code is propagated. When a different object is passed, exit code is set to 1 and the value printed, to be consistent with Python docs: https://python.readthedocs.io/en/latest/library/exceptions.html#SystemExit Signed-off-by: John Smith <[email protected]>
1 parent 4f2f520 commit 1e5da26

File tree

3 files changed

+57
-6
lines changed

3 files changed

+57
-6
lines changed

py/mpconfig.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1180,6 +1180,16 @@ typedef time_t mp_timestamp_t;
11801180
#define MICROPY_ENABLE_VM_ABORT (0)
11811181
#endif
11821182

1183+
// Whether to handle abort behavior in pyexec code
1184+
#ifndef MICROPY_PYEXEC_ENABLE_VM_ABORT
1185+
#define MICROPY_PYEXEC_ENABLE_VM_ABORT (0)
1186+
#endif
1187+
1188+
// Whether to set exit codes according to the exit reason (keyboard interrupt, crash, normal exit, ...)
1189+
#ifndef MICROPY_PYEXEC_ENABLE_EXIT_CODE_HANDLING
1190+
#define MICROPY_PYEXEC_ENABLE_EXIT_CODE_HANDLING (0)
1191+
#endif
1192+
11831193
// Support for internal scheduler
11841194
#ifndef MICROPY_ENABLE_SCHEDULER
11851195
#define MICROPY_ENABLE_SCHEDULER (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES)

shared/runtime/pyexec.c

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,10 @@ static int parse_compile_execute(const void *source, mp_parse_input_kind_t input
7272
nlr_buf_t nlr;
7373
nlr.ret_val = NULL;
7474
if (nlr_push(&nlr) == 0) {
75+
#if MICROPY_PYEXEC_ENABLE_VM_ABORT
76+
nlr_set_abort(&nlr);
77+
#endif
78+
7579
mp_obj_t module_fun;
7680
#if MICROPY_MODULE_FROZEN_MPY
7781
if (exec_flags & EXEC_FLAG_SOURCE_IS_RAW_CODE) {
@@ -116,7 +120,7 @@ static int parse_compile_execute(const void *source, mp_parse_input_kind_t input
116120
mp_hal_set_interrupt_char(-1); // disable interrupt
117121
mp_handle_pending(true); // handle any pending exceptions (and any callbacks)
118122
nlr_pop();
119-
ret = 1;
123+
ret = PYEXEC_NORMAL_EXIT;
120124
if (exec_flags & EXEC_FLAG_PRINT_EOF) {
121125
mp_hal_stdout_tx_strn("\x04", 1);
122126
}
@@ -135,15 +139,41 @@ static int parse_compile_execute(const void *source, mp_parse_input_kind_t input
135139
mp_hal_stdout_tx_strn("\x04", 1);
136140
}
137141

138-
// check for SystemExit
139-
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))) {
140-
// at the moment, the value of SystemExit is unused
142+
#if MICROPY_PYEXEC_ENABLE_VM_ABORT
143+
if (nlr.ret_val == NULL) { // abort
144+
ret = PYEXEC_ABORT;
145+
} else
146+
#endif
147+
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))) { // system exit
148+
#if MICROPY_PYEXEC_ENABLE_EXIT_CODE_HANDLING
149+
mp_obj_t val = mp_obj_exception_get_value(MP_OBJ_FROM_PTR(nlr.ret_val));
150+
if (val != mp_const_none) {
151+
if (mp_obj_is_int(val)) {
152+
ret = (int)mp_obj_int_get_truncated(val);
153+
} else {
154+
mp_obj_print_helper(MICROPY_ERROR_PRINTER, val, PRINT_STR);
155+
mp_print_str(MICROPY_ERROR_PRINTER, "\n");
156+
ret = PYEXEC_UNHANDLED_EXCEPTION;
157+
}
158+
} else {
159+
ret = PYEXEC_NORMAL_EXIT;
160+
}
161+
#else
141162
ret = PYEXEC_FORCED_EXIT;
142-
} else {
163+
#endif
164+
} else { // other exception
143165
mp_obj_print_exception(&mp_plat_print, MP_OBJ_FROM_PTR(nlr.ret_val));
144-
ret = 0;
166+
ret = PYEXEC_UNHANDLED_EXCEPTION;
167+
#if MICROPY_PYEXEC_ENABLE_EXIT_CODE_HANDLING
168+
if (mp_obj_is_subclass_fast(MP_OBJ_FROM_PTR(((mp_obj_base_t *)nlr.ret_val)->type), MP_OBJ_FROM_PTR(&mp_type_KeyboardInterrupt))) { // keyboard interrupt
169+
ret = PYEXEC_KEYBOARD_INTERRUPT;
170+
}
171+
#endif
145172
}
146173
}
174+
#if MICROPY_PYEXEC_ENABLE_VM_ABORT
175+
nlr_set_abort(NULL);
176+
#endif
147177

148178
#if MICROPY_REPL_INFO
149179
// display debugging info if wanted

shared/runtime/pyexec.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,17 @@ extern pyexec_mode_kind_t pyexec_mode_kind;
3737

3838
#define PYEXEC_FORCED_EXIT (0x100)
3939

40+
#if MICROPY_PYEXEC_ENABLE_EXIT_CODE_HANDLING
41+
#define PYEXEC_NORMAL_EXIT (0)
42+
#define PYEXEC_UNHANDLED_EXCEPTION (1)
43+
#define PYEXEC_KEYBOARD_INTERRUPT (128 + 2) // same as SIG INT exit code
44+
#define PYEXEC_ABORT (128 + 9) // same as SIG KILL exit code
45+
#else
46+
#define PYEXEC_NORMAL_EXIT (1)
47+
#define PYEXEC_UNHANDLED_EXCEPTION (0)
48+
#define PYEXEC_ABORT PYEXEC_FORCED_EXIT
49+
#endif
50+
4051
int pyexec_raw_repl(void);
4152
int pyexec_friendly_repl(void);
4253
int pyexec_file(const char *filename);

0 commit comments

Comments
 (0)