8000 Add supervisor.set_next_code() function (prototype). · adafruit/circuitpython@9bd1ad6 · GitHub
[go: up one dir, main page]

Skip to content

Commit 9bd1ad6

Browse files
committed
Add supervisor.set_next_code() function (prototype).
Part of #1084.
1 parent b238824 commit 9bd1ad6

File tree

6 files changed

+181
-12
lines changed

6 files changed

+181
-12
lines changed

locale/circuitpython.pot

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@ msgstr ""
3838
msgid " File \"%q\", line %d"
3939
msgstr ""
4040

41+
#: main.c
42+
msgid " not found.\n"
43+
msgstr ""
44+
4145
#: main.c
4246
msgid " output:\n"
4347
msgstr ""
@@ -2037,7 +2041,7 @@ msgstr ""
20372041
msgid "argsort argument must be an ndarray"
20382042
msgstr ""
20392043

2040-
#: py/runtime.c
2044+
#: py/runtime.c shared-bindings/supervisor/__init__.c
20412045
msgid "argument has wrong type"
20422046
msgstr ""
20432047

main.c

Lines changed: 69 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -271,7 +271,11 @@ bool run_code_py(safe_mode_t safe_mode) {
271271
result.exception_type = NULL;
272272
result.exception_line = 0;
273273

274+
bool skip_repl;
274275
bool found_main = false;
276+
uint8_t next_code_options = 0;
277+
// Collects stickiness bits that apply in the current situation.
278+
uint8_t next_code_stickiness_situation = SUPERVISOR_NEXT_CODE_OPT_NEWLY_SET;
275279

276280
if (safe_mode == NO_SAFE_MODE) {
277281
new_status_color(MAIN_RUNNING);
@@ -286,19 +290,59 @@ bool run_code_py(safe_mode_t safe_mode) {
286290
filesystem_flush();
287291
supervisor_allocation* heap = allocate_remaining_memory();
288292
start_mp(heap);
289-
found_main = maybe_run_list(supported_filenames, &result);
290-
#if CIRCUITPY_FULL_BUILD
291-
if (!found_main){
292-
found_main = maybe_run_list(double_extension_filenames, &result);
293-
if (found_main) {
294-
serial_write_compressed(translate("WARNING: Your code filename has two extensions\n"));
293+
if (next_code_allocation) {
294+
((next_code_info_t*)next_code_allocation->ptr)->options &= ~SUPERVISOR_NEXT_CODE_OPT_NEWLY_SET;
295+
next_code_options = ((next_code_info_t*)next_code_allocation->ptr)->options;
296+
if (((next_code_info_t*)next_code_allocation->ptr)->filename[0] != '\0') {
297+
const char* next_list[] = {((next_code_info_t*)next_code_allocation->ptr)->filename, ""};
298+
found_main = maybe_run_list(next_list, &result);
299+
if (!found_main) {
300+
serial_write(((next_code_info_t*)next_code_allocation->ptr)->filename);
301+
serial_write_compressed(translate(" not found.\n"));
302+
}
295303
}
296304
}
297-
#endif
305+
if (!found_main) {
306+
found_main = maybe_run_list(supported_filenames, &result);
307+
#if CIRCUITPY_FULL_BUILD
308+
if (!found_main){
309+
found_main = maybe_run_list(double_extension_filenames, &result);
310+
if (found_main) {
311+
serial_write_compressed(translate("WARNING: Your code filename has two extensions\n"));
312+
}
313+
}
314+
#endif
315+
}
298316
cleanup_after_vm(heap);
299317

318+
// If a new next code file was set, that is a reason to keep it (obviously). Stuff this into
319+
// the options because it can be treated like any other reason-for-stickiness bit. The
320+
// source is different though: it comes from the options that will apply to the next run,
321+
// while the rest of next_code_options is what applied to this run.
322+
if (next_code_allocation != NULL && (((next_code_info_t*)next_code_allocation->ptr)->options & SUPERVISOR_NEXT_CODE_OPT_NEWLY_SET)) {
323+
next_code_options |= SUPERVISOR_NEXT_CODE_OPT_NEWLY_SET;
324+
}
325+
326+
if (reload_requested) {
327+
next_code_stickiness_situation |= SUPERVISOR_NEXT_CODE_OPT_STICKY_ON_RELOAD;
328+
}
329+
else if (result.return_code == 0) {
330+
next_code_stickiness_situation |= SUPERVISOR_NEXT_CODE_OPT_STICKY_ON_SUCCESS;
331+
if (next_code_options & SUPERVISOR_NEXT_CODE_OPT_RELOAD_ON_SUCCESS) {
332+
skip_repl = true;
333+
goto done;
334+
}
335+
}
336+
else {
337+
next_code_stickiness_situation |= SUPERVISOR_NEXT_CODE_OPT_STICKY_ON_ERROR;
338+
if (next_code_options & SUPERVISOR_NEXT_CODE_OPT_RELOAD_ON_ERROR) {
339+
skip_repl = true;
340+
goto done;
341+
}
342+
}
300343
if (result.return_code & PYEXEC_FORCED_EXIT) {
301-
return reload_requested;
344+
skip_repl = reload_requested;
345+
goto done;
302346
}
303347
}
304348

@@ -316,13 +360,21 @@ bool run_code_py(safe_mode_t safe_mode) {
316360
while (true) {
317361
RUN_BACKGROUND_TASKS;
318362
if (reload_requested) {
363+
next_code_stickiness_situation |= SUPERVISOR_NEXT_CODE_OPT_STICKY_ON_RELOAD;
364+
// Should the STICKY_ON_SUCCESS and STICKY_ON_ERROR bits be cleared in
365+
// next_code_stickiness_situation? I can see arguments either way, but I'm deciding
366+
// "no" for now, mainly because it's a bit less code. At this point, we have both a
367+
// success or error and a reload, so let's have both of the respective options take
368+
// effect (in OR combination).
319369
reload_requested = false;
320-
return true;
370+
skip_repl = true;
371+
goto done;
321372
}
322373

323374
if (serial_connected() && serial_bytes_available()) {
324375
// Skip REPL if reload was requested.
325-
return (serial_read() == CHAR_CTRL_D);
376+
skip_repl = (serial_read() == CHAR_CTRL_D);
377+
goto done;
326378
}
327379

328380
if (!serial_connected_before_animation && serial_connected()) {
@@ -347,6 +399,13 @@ bool run_code_py(safe_mode_t safe_mode) {
347399

348400
tick_rgb_status_animation(&animation);
349401
}
402+
403+
done:
404+
if ((next_code_options & next_code_stickiness_situation) == 0) {
405+
free_memory(next_code_allocation);
406+
next_code_allocation = NULL;
407+
}
408+
return skip_repl;
350409
}
351410

352411
FIL* boot_output_file;

shared-bindings/supervisor/__init__.c

Lines changed: 85 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,12 @@
2323
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
2424
* THE SOFTWARE.
2525
*/
26+
#include <string.h>
27+
2628
#include "py/obj.h"
2729
#include "py/runtime.h"
2830
#include "py/reload.h"
31+
#include "py/objstr.h"
2932

3033
#include "lib/utils/interrupt_char.h"
3134
#include "supervisor/shared/autoreload.h"
@@ -109,6 +112,87 @@ STATIC mp_obj_t supervisor_set_next_stack_limit(mp_obj_t size_obj) {
109112
}
110113
MP_DEFINE_CONST_FUN_OBJ_1(supervisor_set_next_stack_limit_obj, supervisor_set_next_stack_limit);
111114

115+
//| def set_next_code_file(filename: Optional[str], *, reload_on_success : bool = False, reload_on_error: bool = False, sticky_on_success: bool = False, sticky_on_error: bool = False, sticky_on_reload: bool = False) -> None:
116+
//| """Set what file to run on the next vm run.
117+
//|
118+
//| When not ``None``, the given ``filename`` is inserted at the front of the usual ['code.py',
119+
//| 'main.py'] search sequence.
120+
//|
121+
//| The optional keyword arguments specify what happens after the specified file has run:
122+
//|
123+
//| ``sticky_on_…`` determine whether the newly set filename and options stay in effect: If
124+
//| True, further runs will continue to run that file (unless it says otherwise by calling
125+
//| ``set_next_code_filename()`` itself). If False, the settings will only affect one run and
126+
//| revert to the standard code.py/main.py afterwards.
127+
//|
128+
//| ``reload_on_…`` determine how to continue: If False, wait in the usual "Code done running.
129+
//| Waiting for reload. / Press any key to enter the REPL. Use CTRL-D to reload." state. If
130+
//| True, reload immediately as if CTRL-D was pressed.
131+
//|
132+
//| ``…_on_success`` take effect when the program runs to completion or calls ``sys.exit()``.
133+
//|
134+
//| ``…_on_error`` take effect when the program exits with an exception, including the
135+
//| KeyboardInterrupt caused by CTRL-C.
136+
//|
137+
//| ``…_on_reload`` take effect when the program is interrupted by files being written to the USB
138+
//| drive (auto-reload) or when it calls ``supervisor.reload()``.
139+
//|
140+
//| These settings are stored in RAM, not in persistent memory, and will therefore only affect
141+
//| soft reloads. Powering off or resetting the device will always revert to standard settings.
142+
//|
143+
//| When called multiple times in the same run, only the last call takes effect, replacing any
144+
//| settings made by previous ones. This is the main use of passing ``None`` as a filename: to
145+
//| reset to the standard search sequence."""
146+
//| ...
147+
//|
148+
STATIC mp_obj_t supervisor_set_next_code_file(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
149+
static const mp_arg_t allowed_args[] = {
150+
{ MP_QSTR_filename, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = mp_const_none} },
151+
{ MP_QSTR_reload_on_success, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} },
152+
{ MP_QSTR_reload_on_error, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} },
153+
{ MP_QSTR_sticky_on_success, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} },
154+
{ MP_QSTR_sticky_on_error, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} },
155+
{ MP_QSTR_sticky_on_reload, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} },
156+
};
157+
struct {
158+
mp_arg_val_t filename;
159+
mp_arg_val_t reload_on_success;
160+
mp_arg_val_t reload_on_error;
161+
mp_arg_val_t sticky_on_success;
162+
mp_arg_val_t sticky_on_error;
163+
mp_arg_val_t sticky_on_reload;
164+
} args;
165+
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, (mp_arg_val_t*)&args);
166+
if (!MP_OBJ_IS_STR_OR_BYTES(args.filename.u_obj) && args.filename.u_obj != mp_const_none) {
167+
mp_raise_TypeError(translate("argument has wrong type"));
168+
}
169+
if (args.filename.u_obj == mp_const_none) args.filename.u_obj = mp_const_empty_bytes;
170+
uint8_t options = 0;
171+
if (args.reload_on_success.u_bool) options |= SUPERVISOR_NEXT_CODE_OPT_RELOAD_ON_SUCCESS;
172+
if (args.reload_on_error.u_bool) options |= SUPERVISOR_NEXT_CODE_OPT_RELOAD_ON_ERROR;
173+
if (args.sticky_on_success.u_bool) options |= SUPERVISOR_NEXT_CODE_OPT_STICKY_ON_SUCCESS;
174+
if (args.sticky_on_error.u_bool) options |= SUPERVISOR_NEXT_CODE_OPT_STICKY_ON_ERROR;
175+
if (args.sticky_on_reload.u_bool) options |= SUPERVISOR_NEXT_CODE_OPT_STICKY_ON_RELOAD;
176+
size_t len;
177+
const char* filename = mp_obj_str_get_data(args.filename.u_obj, &len);
178+
free_memory(next_code_allocation);
179+
if (options != 0 || len != 0) {
180+
next_code_allocation = allocate_memory(align32_size(sizeof(next_code_info_t) + len + 1), false, true);
181+
if (next_code_allocation == NULL) {
182+
m_malloc_fail(sizeof(next_code_info_t) + len + 1);
183+
}
184+
next_code_info_t* next_code = (next_code_info_t*)next_code_allocation->ptr;
185+
next_code->options = options | SUPERVISOR_NEXT_CODE_OPT_NEWLY_SET;
186+
memcpy(&next_code->filename, filename, len);
187+
next_code->filename[len] = '\0';
188+
}
189+
else {
190+
next_code_allocation = NULL;
191+
}
192+
return mp_const_none;
193+
}
194+
MP_DEFINE_CONST_FUN_OBJ_KW(supervisor_set_next_code_file_obj, 0, supervisor_set_next_code_file);
195+
112196
STATIC const mp_rom_map_elem_t supervisor_module_globals_table[] = {
113197
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_supervisor) },
114198
{ MP_OBJ_NEW_QSTR(MP_QSTR_enable_autoreload), MP_ROM_PTR(&supervisor_enable_autoreload_obj) },
@@ -117,7 +201,7 @@ STATIC const mp_rom_map_elem_t supervisor_module_globals_table[] = {
117201
{ MP_ROM_QSTR(MP_QSTR_runtime), MP_ROM_PTR(&common_hal_supervisor_runtime_obj) },
118202
{ MP_ROM_QSTR(MP_QSTR_reload), MP_ROM_PTR(&supervisor_reload_obj) },
119203
{ MP_ROM_QSTR(MP_QSTR_set_next_stack_limit), MP_ROM_PTR(&supervisor_set_next_stack_limit_obj) },
120-
204+
{ MP_ROM_QSTR(MP_QSTR_set_next_code_file), MP_ROM_PTR(&supervisor_set_next_code_file_obj) },
121205
};
122206

123207
STATIC MP_DEFINE_CONST_DICT(supervisor_module_globals, supervisor_module_globals_table);

supervisor/shared/autoreload.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@
3030
#include "py/reload.h"
3131
#include "supervisor/shared/tick.h"
3232

33+
supervisor_allocation* next_code_allocation;
34+
3335
static volatile uint32_t autoreload_delay_ms = 0;
3436
static bool autoreload_enabled = false;
3537
static bool autoreload_suspended = false;

supervisor/shared/autoreload.h

Lines changed: 18 additions & 0 deletions
35
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,24 @@
2929

3030
#include <stdbool.h>
3131

32+
#include "supervisor/memory.h"
33+
34+
enum {
+
SUPERVISOR_NEXT_CODE_OPT_RELOAD_ON_SUCCESS = 0x1,
36+
SUPERVISOR_NEXT_CODE_OPT_RELOAD_ON_ERROR = 0x2,
37+
SUPERVISOR_NEXT_CODE_OPT_STICKY_ON_SUCCESS = 0x4,
38+
SUPERVISOR_NEXT_CODE_OPT_STICKY_ON_ERROR = 0x8,
39+
SUPERVISOR_NEXT_CODE_OPT_STICKY_ON_RELOAD = 0x10,
40+
SUPERVISOR_NEXT_CODE_OPT_NEWLY_SET = 0x20,
41+
};
42+
43+
typedef struct {
44+
uint8_t options;
45+
char filename[];
46+
} next_code_info_t;
47+
48+
extern supervisor_allocation* next_code_allocation;
49+
3250
extern volatile bool reload_requested;
3351

3452
void autoreload_tick(void);

supervisor/shared/memory.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ enum {
3636
CIRCUITPY_SUPERVISOR_ALLOC_COUNT =
3737
// stack + heap
3838
2
39+
// next_code_allocation
40+
+ 1
3941
#ifdef EXTERNAL_FLASH_DEVICES
4042
+ 1
4143
#endif

0 commit comments

Comments
 (0)
0