add traceback module · MicroPythonNexus/circuitpython@f371c0a · GitHub
Skip to content

Commit f371c0a

Browse files
committed
add traceback module
1 parent fc59a7a commit f371c0a

8 files changed

Lines changed: 214 additions & 33 deletions

File tree

locale/circuitpython.pot

Lines changed: 26 additions & 14 deletions

ports/atmel-samd/boards/adafruit_proxlight_trinkey_m0/mpconfigboard.mk

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ CIRCUITPY_PWMIO = 0
1919
CIRCUITPY_ROTARYIO = 0
2020
CIRCUITPY_RTC = 0
2121
CIRCUITPY_USB_MIDI = 0
22+
CIRCUITPY_TRACEBACK = 0
2223

2324
CIRCUITPY_PIXELBUF = 1
2425
CIRCUITPY_BUSDEVICE = 1

py/circuitpy_defns.mk

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -293,6 +293,9 @@ endif
293293
ifeq ($(CIRCUITPY_TOUCHIO),1)
294294
SRC_PATTERNS += touchio/%
295295
endif
296+
ifeq ($(CIRCUITPY_TRACEBACK),1)
297+
SRC_PATTERNS += traceback/%
298+
endif
296299
ifeq ($(CIRCUITPY_UHEAP),1)
297300
SRC_PATTERNS += uheap/%
298301
endif
@@ -544,6 +547,7 @@ SRC_SHARED_MODULE_ALL = \
544547
terminalio/Terminal.c \
545548
terminalio/__init__.c \
546549
time/__init__.c \
550+
traceback/__init__.c \
547551
uheap/__init__.c \
548552
ustack/__init__.c \
549553
vectorio/Circle.c \

py/circuitpy_mpconfig.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -763,6 +763,13 @@ extern const struct _mp_obj_module_t touchio_module;
763763
#define TOUCHIO_MODULE
764764
#endif
765765

766+
#if CIRCUITPY_TRACEBACK
767+
extern const struct _mp_obj_module_t traceback_module;
768+
#define TRACEBACK_MODULE { MP_OBJ_NEW_QSTR(MP_QSTR_traceback), (mp_obj_t)&traceback_module },
769+
#else
770+
#define TRACEBACK_MODULE
771+
#endif
772+
766773
#if CIRCUITPY_UHEAP
767774
extern const struct _mp_obj_module_t uheap_module;
768775
#define UHEAP_MODULE { MP_OBJ_NEW_QSTR(MP_QSTR_uheap),(mp_obj_t)&uheap_module },
@@ -917,6 +924,7 @@ extern const struct _mp_obj_module_t msgpack_module;
917924
SUPERVISOR_MODULE \
918925
SYNTHIO_MODULE \
919926
TOUCHIO_MODULE \
927+
TRACEBACK_MODULE \
920928
UHEAP_MODULE \
921929
USB_CDC_MODULE \
922930
USB_HID_MODULE \

py/circuitpy_mpconfig.mk

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,9 @@ CFLAGS += -DCIRCUITPY_TOUCHIO_USE_NATIVE=$(CIRCUITPY_TOUCHIO_USE_NATIVE)
324324
CIRCUITPY_TOUCHIO ?= 1
325325
CFLAGS += -DCIRCUITPY_TOUCHIO=$(CIRCUITPY_TOUCHIO)
326326

327+
CIRCUITPY_TRACEBACK ?= 1
328+
CFLAGS += -DCIRCUITPY_TRACEBACK=$(CIRCUITPY_TRACEBACK)
329+
327330
# For debugging.
328331
CIRCUITPY_UHEAP ?= 0
329332
CFLAGS += -DCIRCUITPY_UHEAP=$(CIRCUITPY_UHEAP)

py/modsys.c

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -120,25 +120,6 @@ STATIC mp_obj_t mp_sys_exit(size_t n_args, const mp_obj_t *args) {
120120
}
121121
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_sys_exit_obj, 0, 1, mp_sys_exit);
122122

123-
STATIC mp_obj_t mp_sys_print_exception(size_t n_args, const mp_obj_t *args) {
124-
#if MICROPY_PY_IO && MICROPY_PY_SYS_STDFILES
125-
void *stream_obj = &mp_sys_stdout_obj;
126-
if (n_args > 1) {
127-
mp_get_stream_raise(args[1], MP_STREAM_OP_WRITE);
128-
stream_obj = MP_OBJ_TO_PTR(args[1]);
129-
}
130-
131-
mp_print_t print = {stream_obj, mp_stream_write_adaptor};
132-
mp_obj_print_exception(&print, args[0]);
133-
#else
134-
(void)n_args;
135-
mp_obj_print_exception(&mp_plat_print, args[0]);
136-
#endif
137-
138-
return mp_const_none;
139-
}
140-
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_sys_print_exception_obj, 1, 2, mp_sys_print_exception);
141-
142123
#if MICROPY_PY_SYS_EXC_INFO
143124
STATIC mp_obj_t mp_sys_exc_info(void) {
144125
mp_obj_t cur_exc = MP_OBJ_FROM_PTR(MP_STATE_VM(cur_exception));
Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
/*
2+
* This file is part of the Micro Python project, http://micropython.org/
3+
*
4+
* The MIT License (MIT)
5+
*
6+
* Copyright (c) 2021 microDev
7+
*
8+
* Permission is hereby granted, free of charge, to any person obtaining a copy
9+
* of this software and associated documentation files (the "Software"), to deal
10+
* in the Software without restriction, including without limitation the rights
11+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12+
* copies of the Software, and to permit persons to whom the Software is
13+
* furnished to do so, subject to the following conditions:
14+
*
15+
* The above copyright notice and this permission notice shall be included in
16+
* all copies or substantial portions of the Software.
17+
*
18+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24+
* THE SOFTWARE.
25+
*/
26+
27+
#include "py/stream.h"
28+
#include "py/runtime.h"
29+
30+
#include "supervisor/shared/stack.h"
31+
32+
//| """Traceback Module
33+
//|
34+
//| This module provides a standard interface to print stack traces of programs.
35+
//| This is useful when you want to print stack traces under program control.
36+
//|
37+
//| """
38+
//| ...
39+
//|
40+
41+
//| def print_exception(etype: Type[BaseException], value: BaseException, tb: TracebackType,
42+
//| limit: Optional[int] = None, file: Optional[io.FileIO] = None, chain: Optional[bool] = True) -> None:
43+
//|
44+
//| """Prints exception information and stack trace entries.
45+
//|
46+
//| .. note: Setting `chain` will have no effect as chained exceptions are not yet implemented.
47+
//|
48+
//| :param Type[BaseException] etype: This is ignored and inferred from the type of ``value``.
49+
//| :param BaseException value: The exception. Must be an instance of `BaseException`.
50+
//| :param TracebackType tb: The traceback object. If `None`, the traceback will not be printed.
51+
//| :param int limit: Print up to limit stack trace entries (starting from the caller’s frame) if limit is positive.
52+
//| Otherwise, print the last ``abs(limit)`` entries. If limit is omitted or None, all entries are printed.
53+
//| :param io.FileIO file: If file is omitted or `None`, the output goes to `sys.stderr`; otherwise it should be an open
54+
//| file or file-like object to receive the output.
55+
//| :param bool chain: If `True` then chained exceptions will be printed.
56+
//|
57+
//| """
58+
//| ...
59+
//|
60+
STATIC mp_obj_t traceback_print_exception(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
61+
enum { ARG_etype, ARG_value, ARG_tb, ARG_limit, ARG_file, ARG_chain };
62+
static const mp_arg_t allowed_args[] = {
63+
{ MP_QSTR_etype, MP_ARG_OBJ | MP_ARG_REQUIRED },
64+
{ MP_QSTR_value, MP_ARG_OBJ | MP_ARG_REQUIRED },
65+
{ MP_QSTR_tb, MP_ARG_OBJ | MP_ARG_REQUIRED },
66+
{ MP_QSTR_limit, MP_ARG_OBJ, {.u_obj = mp_const_none} },
67+
{ MP_QSTR_file, MP_ARG_OBJ, {.u_obj = mp_const_none} },
68+
{ MP_QSTR_chain, MP_ARG_BOOL, {.u_bool = true} },
69+
};
70+
71+
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
72+
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
73+
74+
mp_obj_t exc = args[ARG_value].u_obj;
75+
if (!mp_obj_is_exception_instance(exc)) {
76+
mp_raise_TypeError(translate("invalid exception"));
77+
}
78+
79+
mp_print_t print = mp_plat_print;
80+
if (args[ARG_file].u_obj != mp_const_none) {
81+
#if MICROPY_PY_IO && MICROPY_PY_SYS_STDFILES
82+
mp_get_stream_raise(args[ARG_file].u_obj, MP_STREAM_OP_WRITE);
83+
print.data = MP_OBJ_TO_PTR(args[ARG_file].u_obj);
84+
print.print_strn = mp_stream_write_adaptor;
85+
#else
86+
mp_raise_NotImplementedError(translate("file write is not available"));
87+
#endif
88+
}
89+
90+
mp_int_t limit = 0;
91+
bool print_tb = true;
92+
if (args[ARG_limit].u_obj != mp_const_none) {
93+
if (!mp_obj_get_int_maybe(args[ARG_limit].u_obj, &limit)) {
94+
mp_raise_TypeError(translate("limit should be an int"));
95+
}
96+
print_tb = !(limit == 0);
97+
}
98+
99+
if (args[ARG_tb].u_obj != mp_const_none && print_tb) {
100+
if (!stack_ok()) {
101+
mp_raise_RuntimeError(translate("stack is not ok"));
102+
}
103+
size_t n, *values;
104+
mp_obj_exception_get_traceback(exc, &n, &values);
105+
if (n > 0) {
106+
assert(n % 3 == 0);
107+
// Decompress the format strings
108+
const compressed_string_t *traceback = MP_ERROR_TEXT("Traceback (most recent call last):\n");
109+
char decompressed[decompress_length(traceback)];
110+
decompress(traceback, decompressed);
111+
#if MICROPY_ENABLE_SOURCE_LINE
112+
const compressed_string_t *frame = MP_ERROR_TEXT(" File \"%q\", line %d");
113+
#else
114+
const compressed_string_t *frame = MP_ERROR_TEXT(" File \"%q\"");
115+
#endif
116+
char decompressed_frame[decompress_length(frame)];
117+
decompress(frame, decompressed_frame);
118+
const compressed_string_t *block_fmt = MP_ERROR_TEXT(", in %q\n");
119+
char decompressed_block[decompress_length(block_fmt)];
120+
decompress(block_fmt, decompressed_block);
121+
122+
// Set traceback formatting
123+
// Default: Print full traceback
124+
int i = n - 3, j;
125+
if (limit > 0) {
126+
// Print upto limit traceback
127+
// from caller's frame
128+
limit = n - (limit * 3);
129+
} else if (limit < 0) {
130+
// Print upto limit traceback
131+
// from last
132+
i = 0, limit = 3 + (limit * 3);
133+
}
134+
135+
// Print the traceback
136+
mp_print_str(&print, decompressed);
137+
for (; i >= limit; i -= 3) {
138+
j = (i < 0) ? -i : i;
139+
#if MICROPY_ENABLE_SOURCE_LINE
140+
mp_printf(&print, decompressed_frame, values[j], (int)values[j + 1]);
141+
#else
142+
mp_printf(&print, decompressed_frame, values[j]);
143+
#endif
144+
// The block name can be NULL if it's unknown
145+
qstr block = values[j + 2];
146+
if (block == MP_QSTRnull) {
147+
mp_print_str(&print, "\n");
148+
} else {
149+
mp_printf(&print, decompressed_block, block);
150+
}
151+
}
152+
}
153+
}
154+
mp_obj_print_helper(&print, exc, PRINT_EXC);
155+
mp_print_str(&print, "\n");
156+
return mp_const_none;
157+
}
158+
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(traceback_print_exception_obj, 3, traceback_print_exception);
159+
160+
STATIC const mp_rom_map_elem_t traceback_module_globals_table[] = {
161+
// module name
162+
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_traceback) },
163+
// module functions
164+
{ MP_ROM_QSTR(MP_QSTR_print_exception), MP_ROM_PTR(&traceback_print_exception_obj) },
165+
};
166+
STATIC MP_DEFINE_CONST_DICT(traceback_module_globals, traceback_module_globals_table);
167+
168+
const mp_obj_module_t traceback_module = {
169+
.base = { &mp_type_module },
170+
.globals = (mp_obj_dict_t *)&traceback_module_globals,
171+
};

shared-module/traceback/__init__.c

Lines changed: 1 addition & 0 deletions

0 commit comments

Comments
 (0)