From d872e6eb90fee3762591e169f5a1f5d7f8777f57 Mon Sep 17 00:00:00 2001 From: "David (Pololu)" Date: Tue, 11 Apr 2023 16:40:05 -0700 Subject: [PATCH] py/modsys.c: Add sys._exc_traceback. This makes it easier to provide custom formatting of exception stack traces, e.g. to make them fit on a tiny display. This is an experimental function that exposes internal details of MicroPython and it might change in the future. Signed-off-by: David (Pololu) --- docs/library/sys.rst | 22 ++++++++++++++++++++++ py/modsys.c | 23 +++++++++++++++++++++++ py/mpconfig.h | 5 +++++ 3 files changed, 50 insertions(+) diff --git a/docs/library/sys.rst b/docs/library/sys.rst index 3efdce964c..35dfc996a6 100644 --- a/docs/library/sys.rst +++ b/docs/library/sys.rst @@ -52,6 +52,28 @@ Functions present in pre-built firmware (due to it affecting performance). The relevant configuration option is *MICROPY_PY_SYS_SETTRACE*. +.. function:: _exc_traceback(exc) + + Retrieves traceback information from an exception object, including + the filename, line number, and code block name for every code location + on the call stack when the exception was thrown. + + .. admonition:: Difference to CPython + :class: attention + + This function is a MicroPython extension intended to provide similar + functionality to the ``__traceback__`` attribute of exception + objects in CPython. + + .. admonition:: Unstable + :class: attention + + This function directly exposes the internal traceback data used by + MicroPython. Future versions might introduce incompatible changes to + the format. + + + Constants --------- diff --git a/py/modsys.c b/py/modsys.c index 35af761a08..5bdabdaf78 100644 --- a/py/modsys.c +++ b/py/modsys.c @@ -150,6 +150,26 @@ STATIC mp_obj_t mp_sys_print_exception(size_t n_args, const mp_obj_t *args) { } MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_sys_print_exception_obj, 1, 2, mp_sys_print_exception); +#if MICROPY_PY_SYS_EXC_TRACEBACK +STATIC mp_obj_t mp_sys_exc_traceback(mp_obj_t exc) { + if (!mp_obj_is_exception_instance(exc)) { + mp_raise_TypeError(MP_ERROR_TEXT("not an exception")); + } + size_t n, *values; + mp_obj_exception_get_traceback(exc, &n, &values); + // Assumption: n is a multiple of 3. + mp_obj_t obj = mp_obj_new_list(n, NULL); + mp_obj_list_t *list = MP_OBJ_TO_PTR(obj); + for (size_t i = 0; i < list->len; i += 3) { + list->items[i + 0] = MP_OBJ_NEW_QSTR(values[i + 0]); // filename + list->items[i + 1] = MP_OBJ_NEW_SMALL_INT(values[i + 1]); // line + list->items[i + 2] = MP_OBJ_NEW_QSTR(values[i + 2]); // block + } + return obj; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_sys_exc_traceback_obj, mp_sys_exc_traceback); +#endif + #if MICROPY_PY_SYS_EXC_INFO STATIC mp_obj_t mp_sys_exc_info(void) { mp_obj_t cur_exc = MP_OBJ_FROM_PTR(MP_STATE_VM(cur_exception)); @@ -277,6 +297,9 @@ STATIC const mp_rom_map_elem_t mp_module_sys_globals_table[] = { */ { MP_ROM_QSTR(MP_QSTR_print_exception), MP_ROM_PTR(&mp_sys_print_exception_obj) }, + #if MICROPY_PY_SYS_EXC_TRACEBACK + { MP_ROM_QSTR(MP_QSTR__exc_traceback), MP_ROM_PTR(&mp_sys_exc_traceback_obj) }, + #endif #if MICROPY_PY_SYS_ATEXIT { MP_ROM_QSTR(MP_QSTR_atexit), MP_ROM_PTR(&mp_sys_atexit_obj) }, #endif diff --git a/py/mpconfig.h b/py/mpconfig.h index 80e58d1cc4..d0aededcbb 100644 --- a/py/mpconfig.h +++ b/py/mpconfig.h @@ -1386,6 +1386,11 @@ typedef double mp_float_t; #define MICROPY_PY_SYS_MODULES (1) #endif +// Whether to provide "sys._exc_traceback" function. +#ifndef MICROPY_PY_SYS_EXC_TRACEBACK +#define MICROPY_PY_SYS_EXC_TRACEBACK (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) +#endif + // Whether to provide "sys.exc_info" function // Avoid enabling this, this function is Python2 heritage #ifndef MICROPY_PY_SYS_EXC_INFO