Skip to content

Commit bdf8bc5

Browse files
committed
allow exception raise inside atexit callback
1 parent 1c4a6c3 commit bdf8bc5

File tree

5 files changed

+84
-44
lines changed

5 files changed

+84
-44
lines changed

lib/utils/pyexec.c

Lines changed: 58 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,10 @@
4444
#include "lib/utils/pyexec.h"
4545
#include "genhdr/mpversion.h"
4646

47+
#if CIRCUITPY_ATEXIT
48+
#include "shared-module/atexit/__init__.h"
49+
#endif
50+
4751
pyexec_mode_kind_t pyexec_mode_kind = PYEXEC_MODE_FRIENDLY_REPL;
4852
int pyexec_system_exit = 0;
4953

@@ -58,6 +62,7 @@ STATIC bool repl_display_debugging_info = 0;
5862
#define EXEC_FLAG_SOURCE_IS_VSTR (16)
5963
#define EXEC_FLAG_SOURCE_IS_FILENAME (32)
6064
#define EXEC_FLAG_SOURCE_IS_READER (64)
65+
#define EXEC_FLAG_SOURCE_IS_ATEXIT (128)
6166

6267
// parses, compiles and executes the code in the lexer
6368
// frees the lexer before returning
@@ -81,52 +86,65 @@ STATIC int parse_compile_execute(const void *source, mp_parse_input_kind_t input
8186
nlr.ret_val = NULL;
8287
if (nlr_push(&nlr) == 0) {
8388
mp_obj_t module_fun;
84-
#if MICROPY_MODULE_FROZEN_MPY
85-
if (exec_flags & EXEC_FLAG_SOURCE_IS_RAW_CODE) {
86-
// source is a raw_code object, create the function
87-
module_fun = mp_make_function_from_raw_code(source, MP_OBJ_NULL, MP_OBJ_NULL);
88-
} else
89+
#if CIRCUITPY_ATEXIT
90+
if (!(exec_flags & EXEC_FLAG_SOURCE_IS_ATEXIT))
8991
#endif
9092
{
91-
#if MICROPY_ENABLE_COMPILER
92-
mp_lexer_t *lex;
93-
if (exec_flags & EXEC_FLAG_SOURCE_IS_VSTR) {
94-
const vstr_t *vstr = source;
95-
lex = mp_lexer_new_from_str_len(MP_QSTR__lt_stdin_gt_, vstr->buf, vstr->len, 0);
96-
} else if (exec_flags & EXEC_FLAG_SOURCE_IS_READER) {
97-
lex = mp_lexer_new(MP_QSTR__lt_stdin_gt_, *(mp_reader_t *)source);
98-
} else if (exec_flags & EXEC_FLAG_SOURCE_IS_FILENAME) {
99-
lex = mp_lexer_new_from_file(source);
100-
} else {
101-
lex = (mp_lexer_t *)source;
93+
#if MICROPY_MODULE_FROZEN_MPY
94+
if (exec_flags & EXEC_FLAG_SOURCE_IS_RAW_CODE) {
95+
// source is a raw_code object, create the function
96+
module_fun = mp_make_function_from_raw_code(source, MP_OBJ_NULL, MP_OBJ_NULL);
97+
} else
98+
#endif
99+
{
100+
#if MICROPY_ENABLE_COMPILER
101+
mp_lexer_t *lex;
102+
if (exec_flags & EXEC_FLAG_SOURCE_IS_VSTR) {
103+
const vstr_t *vstr = source;
104+
lex = mp_lexer_new_from_str_len(MP_QSTR__lt_stdin_gt_, vstr->buf, vstr->len, 0);
105+
} else if (exec_flags & EXEC_FLAG_SOURCE_IS_READER) {
106+
lex = mp_lexer_new(MP_QSTR__lt_stdin_gt_, *(mp_reader_t *)source);
107+
} else if (exec_flags & EXEC_FLAG_SOURCE_IS_FILENAME) {
108+
lex = mp_lexer_new_from_file(source);
109+
} else {
110+
lex = (mp_lexer_t *)source;
111+
}
112+
// source is a lexer, parse and compile the script
113+
qstr source_name = lex->source_name;
114+
if (input_kind == MP_PARSE_FILE_INPUT) {
115+
mp_store_global(MP_QSTR___file__, MP_OBJ_NEW_QSTR(source_name));
116+
}
117+
mp_parse_tree_t parse_tree = mp_parse(lex, input_kind);
118+
module_fun = mp_compile(&parse_tree, source_name, exec_flags & EXEC_FLAG_IS_REPL);
119+
// Clear the parse tree because it has a heap pointer we don't need anymore.
120+
*((uint32_t volatile *)&parse_tree.chunk) = 0;
121+
#else
122+
mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("script compilation not supported"));
123+
#endif
102124
}
103-
// source is a lexer, parse and compile the script
104-
qstr source_name = lex->source_name;
125+
126+
// If the code was loaded from a file it's likely to be running for a while so we'll long
127+
// live it and collect any garbage before running.
105128
if (input_kind == MP_PARSE_FILE_INPUT) {
106-
mp_store_global(MP_QSTR___file__, MP_OBJ_NEW_QSTR(source_name));
129+
module_fun = make_obj_long_lived(module_fun, 6);
130+
gc_collect();
107131
}
108-
mp_parse_tree_t parse_tree = mp_parse(lex, input_kind);
109-
module_fun = mp_compile(&parse_tree, source_name, exec_flags & EXEC_FLAG_IS_REPL);
110-
// Clear the parse tree because it has a heap pointer we don't need anymore.
111-
*((uint32_t volatile *)&parse_tree.chunk) = 0;
112-
#else
113-
mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("script compilation not supported"));
114-
#endif
115-
}
116-
117-
// If the code was loaded from a file it's likely to be running for a while so we'll long
118-
// live it and collect any garbage before running.
119-
if (input_kind == MP_PARSE_FILE_INPUT) {
120-
module_fun = make_obj_long_lived(module_fun, 6);
121-
gc_collect();
122132
}
123133

124134
// execute code
125135
mp_hal_set_interrupt_char(CHAR_CTRL_C); // allow ctrl-C to interrupt us
126136
#if MICROPY_REPL_INFO
127137
start = mp_hal_ticks_ms();
128138
#endif
129-
mp_call_function_0(module_fun);
139+
#if CIRCUITPY_ATEXIT
140+
if (exec_flags & EXEC_FLAG_SOURCE_IS_ATEXIT) {
141+
atexit_callback_t *callback = (atexit_callback_t *)source;
142+
mp_call_function_n_kw(callback->func, callback->n_pos, callback->n_kw, callback->args);
143+
} else
144+
#endif
145+
{
146+
mp_call_function_0(module_fun);
147+
}
130148
mp_hal_set_interrupt_char(-1); // disable interrupt
131149
mp_handle_pending(true); // handle any pending exceptions (and any callbacks)
132150
nlr_pop();
@@ -741,6 +759,12 @@ int pyexec_frozen_module(const char *name, pyexec_result_t *result) {
741759
}
742760
#endif
743761

762+
#if CIRCUITPY_ATEXIT
763+
int pyexec_exit_handler(const void *source, pyexec_result_t *result) {
764+
return parse_compile_execute(source, MP_PARSE_FILE_INPUT, EXEC_FLAG_SOURCE_IS_ATEXIT, result);
765+
}
766+
#endif
767+
744768
#if MICROPY_REPL_INFO
745769
mp_obj_t pyb_set_repl_info(mp_obj_t o_value) {
746770
repl_display_debugging_info = mp_obj_get_int(o_value);

lib/utils/pyexec.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,10 @@ void pyexec_event_repl_init(void);
5959
int pyexec_event_repl_process_char(int c);
6060
extern uint8_t pyexec_repl_active;
6161

62+
#if CIRCUITPY_ATEXIT
63+
int pyexec_exit_handler(const void *source, pyexec_result_t *result);
64+
#endif
65+
6266
#if MICROPY_REPL_INFO
6367
mp_obj_t pyb_set_repl_info(mp_obj_t o_value);
6468
MP_DECLARE_CONST_FUN_OBJ_1(pyb_set_repl_info_obj);

main.c

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,7 @@ STATIC bool maybe_run_list(const char * const * filenames, pyexec_result_t* exec
218218
mp_hal_stdout_tx_str(decompressed);
219219
pyexec_file(filename, exec_result);
220220
#if CIRCUITPY_ATEXIT
221-
shared_module_atexit_execute();
221+
shared_module_atexit_execute(exec_result);
222222
#endif
223223
return true;
224224
}
@@ -770,7 +770,11 @@ STATIC int run_repl(void) {
770770
exit_code = pyexec_friendly_repl();
771771
}
772772
#if CIRCUITPY_ATEXIT
773-
shared_module_atexit_execute();
773+
pyexec_result_t result;
774+
shared_module_atexit_execute(&result);
775+
if (result.return_code == PYEXEC_DEEP_SLEEP) {
776+
exit_code = PYEXEC_DEEP_SLEEP;
777+
}
774778
#endif
775779
cleanup_after_vm(heap, MP_OBJ_SENTINEL);
776780
#if CIRCUITPY_STATUS_LED

shared-module/atexit/__init__.c

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,6 @@
2828
#include "py/runtime.h"
2929
#include "shared-module/atexit/__init__.h"
3030

31-
typedef struct _atexit_callback_t {
32-
size_t n_pos, n_kw;
33-
mp_obj_t func, *args;
34-
} atexit_callback_t;
35-
3631
static size_t callback_len = 0;
3732
static atexit_callback_t *callback = NULL;
3833

@@ -79,11 +74,18 @@ void shared_module_atexit_unregister(const mp_obj_t *func) {
7974
}
8075
}
8176

82-
void shared_module_atexit_execute(void) {
77+
void shared_module_atexit_execute(pyexec_result_t *result) {
8378
if (callback) {
8479
for (size_t i = callback_len; i-- > 0;) {
8580
if (callback[i].func != mp_const_none) {
86-
mp_call_function_n_kw(callback[i].func, callback[i].n_pos, callback[i].n_kw, callback[i].args);
81+
if (result != NULL) {
82+
pyexec_result_t res;
83+
if (pyexec_exit_handler(&callback[i], &res) == PYEXEC_DEEP_SLEEP) {
84+
*result = res;
85+
}
86+
} else {
87+
pyexec_exit_handler(&callback[i], NULL);
88+
}
8789
}
8890
}
8991
}

shared-module/atexit/__init__.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,19 @@
2828
#define MICROPY_INCLUDED_SHARED_MODULE_ATEXIT___INIT___H
2929

3030
#include "py/obj.h"
31+
#include "lib/utils/pyexec.h"
32+
33+
typedef struct _atexit_callback_t {
34+
size_t n_pos, n_kw;
35+
mp_obj_t func, *args;
36+
} atexit_callback_t;
3137

3238
extern void atexit_reset(void);
3339
extern void atexit_gc_collect(void);
3440

3541
extern void shared_module_atexit_register(mp_obj_t *func,
3642
size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args);
3743
extern void shared_module_atexit_unregister(const mp_obj_t *func);
38-
extern void shared_module_atexit_execute(void);
44+
extern void shared_module_atexit_execute(pyexec_result_t *result);
3945

4046
#endif // MICROPY_INCLUDED_SHARED_MODULE_ATEXIT___INIT___H

0 commit comments

Comments
 (0)