Skip to content

Commit f371c0a

Browse files
committed
add traceback module
1 parent fc59a7a commit f371c0a

File tree

8 files changed

+214
-33
lines changed

8 files changed

+214
-33
lines changed

locale/circuitpython.pot

Lines changed: 26 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,11 @@ msgid ""
3535
"https://github.com/adafruit/circuitpython/issues\n"
3636
msgstr ""
3737

38-
#: py/obj.c
38+
#: py/obj.c shared-bindings/traceback/__init__.c
3939
msgid " File \"%q\""
4040
msgstr ""
4141

42-
#: py/obj.c
42+
#: py/obj.c shared-bindings/traceback/__init__.c
4343
msgid " File \"%q\", line %d"
4444
msgstr ""
4545

@@ -322,7 +322,7 @@ msgstr ""
322322
msgid "*x must be assignment target"
323323
msgstr ""
324324

325-
#: py/obj.c
325+
#: py/obj.c shared-bindings/traceback/__init__.c
326326
msgid ", in %q\n"
327327
msgstr ""
328328

@@ -1185,11 +1185,6 @@ msgstr ""
11851185
msgid "Input/output error"
11861186
msgstr ""
11871187

1188-
#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c
1189-
#, c-format
1190-
msgid "Missing jmp_pin. Instruction %d jumps on pin"
1191-
msgstr ""
1192-
11931188
#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c
11941189
#, c-format
11951190
msgid "Instruction %d shifts in more bits than pin count"
@@ -1506,6 +1501,11 @@ msgstr ""
15061501
msgid "Missing first_set_pin. Instruction %d sets pin(s)"
15071502
msgstr ""
15081503

1504+
#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c
1505+
#, c-format
1506+
msgid "Missing jmp_pin. Instruction %d jumps on pin"
1507+
msgstr ""
1508+
15091509
#: shared-bindings/busio/UART.c shared-bindings/displayio/Group.c
15101510
msgid "Must be a %q subclass."
15111511
msgstr ""
@@ -2227,7 +2227,7 @@ msgstr ""
22272227
msgid "Touch alarms not available"
22282228
msgstr ""
22292229

2230-
#: py/obj.c
2230+
#: py/obj.c shared-bindings/traceback/__init__.c
22312231
msgid "Traceback (most recent call last):\n"
22322232
msgstr ""
22332233

@@ -2525,7 +2525,7 @@ msgid "argument name reused"
25252525
msgstr ""
25262526

25272527
#: py/argcheck.c shared-bindings/_stage/__init__.c
2528-
#: shared-bindings/digitalio/DigitalInOut.c shared-bindings/gamepad/GamePad.c
2528+
#: shared-bindings/digitalio/DigitalInOut.c
25292529
msgid "argument num/types mismatch"
25302530
msgstr ""
25312531

@@ -3089,6 +3089,10 @@ msgstr ""
30893089
msgid "file must be a file opened in byte mode"
30903090
msgstr ""
30913091

3092+
#: shared-bindings/traceback/__init__.c
3093+
msgid "file write is not available"
3094+
msgstr ""
3095+
30923096
#: shared-bindings/storage/__init__.c
30933097
msgid "filesystem must provide mount method"
30943098
msgstr ""
@@ -3376,6 +3380,10 @@ msgstr ""
33763380
msgid "invalid element_size %d, must be, 1, 2, or 4"
33773381
msgstr ""
33783382

3383+
#: shared-bindings/traceback/__init__.c
3384+
msgid "invalid exception"
3385+
msgstr ""
3386+
33793387
#: extmod/modframebuf.c
33803388
msgid "invalid format"
33813389
msgstr ""
@@ -3457,6 +3465,10 @@ msgstr ""
34573465
msgid "lhs and rhs should be compatible"
34583466
msgstr ""
34593467

3468+
#: shared-bindings/traceback/__init__.c
3469+
msgid "limit should be an int"
3470+
msgstr ""
3471+
34603472
#: py/emitnative.c
34613473
msgid "local '%q' has type '%q' but source is '%q'"
34623474
msgstr ""
@@ -3609,10 +3621,6 @@ msgstr ""
36093621
msgid "no active exception to reraise"
36103622
msgstr ""
36113623

3612-
#: shared-bindings/socket/__init__.c shared-module/network/__init__.c
3613-
msgid "no available NIC"
3614-
msgstr ""
3615-
36163624
#: py/compile.c
36173625
msgid "no binding for nonlocal found"
36183626
msgstr ""
@@ -4085,6 +4093,10 @@ msgstr ""
40854093
msgid "source palette too large"
40864094
msgstr ""
40874095

4096+
#: shared-bindings/traceback/__init__.c
4097+
msgid "stack is not ok"
4098+
msgstr ""
4099+
40884100
#: py/objstr.c
40894101
msgid "start/end indices"
40904102
msgstr ""

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));

shared-bindings/traceback/__init__.c

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
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
// empty file

0 commit comments

Comments
 (0)