py/modsys: Add optional sys.tracebacklimit attribute.
With behaviour as per CPython. Signed-off-by: Damien George <damien@micropython.org>
This commit is contained in:
parent
bc181550a4
commit
cac939ddc3
|
@ -144,6 +144,14 @@ Constants
|
|||
|
||||
Standard output `stream`.
|
||||
|
||||
.. data:: tracebacklimit
|
||||
|
||||
A mutable attribute holding an integer value which is the maximum number of traceback
|
||||
entries to store in an exception. Set to 0 to disable adding tracebacks. Defaults
|
||||
to 1000.
|
||||
|
||||
Note: this is not available on all ports.
|
||||
|
||||
.. data:: version
|
||||
|
||||
Python language version that this implementation conforms to, as a string.
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
#define MICROPY_REPL_EMACS_WORDS_MOVE (1)
|
||||
#define MICROPY_REPL_EMACS_EXTRA_WORDS_MOVE (1)
|
||||
#define MICROPY_WARNINGS_CATEGORY (1)
|
||||
#define MICROPY_MODULE_ATTR_DELEGATION (1)
|
||||
#define MICROPY_MODULE_GETATTR (1)
|
||||
#define MICROPY_PY_DELATTR_SETATTR (1)
|
||||
#define MICROPY_PY_ALL_INPLACE_SPECIAL_METHODS (1)
|
||||
|
@ -44,6 +45,7 @@
|
|||
#define MICROPY_PY_BUILTINS_HELP (1)
|
||||
#define MICROPY_PY_BUILTINS_HELP_MODULES (1)
|
||||
#define MICROPY_PY_SYS_GETSIZEOF (1)
|
||||
#define MICROPY_PY_SYS_TRACEBACKLIMIT (1)
|
||||
#define MICROPY_PY_MATH_CONSTANTS (1)
|
||||
#define MICROPY_PY_MATH_FACTORIAL (1)
|
||||
#define MICROPY_PY_URANDOM_EXTRA_FUNCS (1)
|
||||
|
|
|
@ -185,6 +185,9 @@ MP_DEFINE_CONST_FUN_OBJ_1(mp_sys_settrace_obj, mp_sys_settrace);
|
|||
|
||||
#if MICROPY_PY_SYS_ATTR_DELEGATION
|
||||
STATIC const uint16_t sys_mutable_keys[] = {
|
||||
#if MICROPY_PY_SYS_TRACEBACKLIMIT
|
||||
MP_QSTR_tracebacklimit,
|
||||
#endif
|
||||
MP_QSTRnull,
|
||||
};
|
||||
|
||||
|
|
|
@ -1377,10 +1377,15 @@ typedef double mp_float_t;
|
|||
#define MICROPY_PY_SYS_STDIO_BUFFER (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES)
|
||||
#endif
|
||||
|
||||
// Whether to provide sys.tracebacklimit mutable attribute
|
||||
#ifndef MICROPY_PY_SYS_TRACEBACKLIMIT
|
||||
#define MICROPY_PY_SYS_TRACEBACKLIMIT (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EVERYTHING)
|
||||
#endif
|
||||
|
||||
// Whether the sys module supports attribute delegation
|
||||
// This is enabled automatically when needed by other features
|
||||
#ifndef MICROPY_PY_SYS_ATTR_DELEGATION
|
||||
#define MICROPY_PY_SYS_ATTR_DELEGATION (0)
|
||||
#define MICROPY_PY_SYS_ATTR_DELEGATION (MICROPY_PY_SYS_TRACEBACKLIMIT)
|
||||
#endif
|
||||
|
||||
// Whether to provide "uerrno" module
|
||||
|
|
|
@ -41,6 +41,9 @@
|
|||
// variable, but in the future it is hoped that the state can become local.
|
||||
|
||||
enum {
|
||||
#if MICROPY_PY_SYS_TRACEBACKLIMIT
|
||||
MP_SYS_MUTABLE_TRACEBACKLIMIT,
|
||||
#endif
|
||||
MP_SYS_MUTABLE_NUM,
|
||||
};
|
||||
|
||||
|
|
|
@ -575,6 +575,16 @@ void mp_obj_exception_add_traceback(mp_obj_t self_in, qstr file, size_t line, qs
|
|||
// append this traceback info to traceback data
|
||||
// if memory allocation fails (eg because gc is locked), just return
|
||||
|
||||
#if MICROPY_PY_SYS_TRACEBACKLIMIT
|
||||
mp_int_t max_traceback = MP_OBJ_SMALL_INT_VALUE(MP_STATE_VM(sys_mutable[MP_SYS_MUTABLE_TRACEBACKLIMIT]));
|
||||
if (max_traceback <= 0) {
|
||||
return;
|
||||
} else if (self->traceback_data != NULL && self->traceback_len >= max_traceback * TRACEBACK_ENTRY_LEN) {
|
||||
self->traceback_len -= TRACEBACK_ENTRY_LEN;
|
||||
memmove(self->traceback_data, self->traceback_data + TRACEBACK_ENTRY_LEN, self->traceback_len * sizeof(self->traceback_data[0]));
|
||||
}
|
||||
#endif
|
||||
|
||||
if (self->traceback_data == NULL) {
|
||||
self->traceback_data = m_new_maybe(size_t, TRACEBACK_ENTRY_LEN);
|
||||
if (self->traceback_data == NULL) {
|
||||
|
|
|
@ -141,6 +141,10 @@ void mp_init(void) {
|
|||
MP_STATE_THREAD(current_code_state) = NULL;
|
||||
#endif
|
||||
|
||||
#if MICROPY_PY_SYS_TRACEBACKLIMIT
|
||||
MP_STATE_VM(sys_mutable[MP_SYS_MUTABLE_TRACEBACKLIMIT]) = MP_OBJ_NEW_SMALL_INT(1000);
|
||||
#endif
|
||||
|
||||
#if MICROPY_PY_BLUETOOTH
|
||||
MP_STATE_VM(bluetooth) = MP_OBJ_NULL;
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,78 @@
|
|||
# test sys.tracebacklimit
|
||||
|
||||
try:
|
||||
try:
|
||||
import usys as sys
|
||||
import uio as io
|
||||
except ImportError:
|
||||
import sys
|
||||
import io
|
||||
except ImportError:
|
||||
print("SKIP")
|
||||
raise SystemExit
|
||||
|
||||
try:
|
||||
sys.tracebacklimit = 1000
|
||||
except AttributeError:
|
||||
print("SKIP")
|
||||
raise SystemExit
|
||||
|
||||
if hasattr(sys, "print_exception"):
|
||||
print_exception = sys.print_exception
|
||||
else:
|
||||
import traceback
|
||||
|
||||
print_exception = lambda e, f: traceback.print_exception(None, e, sys.exc_info()[2], file=f)
|
||||
|
||||
|
||||
def print_exc(e):
|
||||
buf = io.StringIO()
|
||||
print_exception(e, buf)
|
||||
s = buf.getvalue()
|
||||
for l in s.split("\n"):
|
||||
# Remove filename.
|
||||
if l.startswith(" File "):
|
||||
l = l.split('"')
|
||||
print(l[0], l[2])
|
||||
# uPy and CPy tracebacks differ in that CPy prints a source line for
|
||||
# each traceback entry. In this case, we know that offending line
|
||||
# has 4-space indent, so filter it out.
|
||||
elif not l.startswith(" "):
|
||||
print(l)
|
||||
|
||||
|
||||
def f0():
|
||||
raise ValueError("value")
|
||||
|
||||
|
||||
def f1():
|
||||
f0()
|
||||
|
||||
|
||||
def f2():
|
||||
f1()
|
||||
|
||||
|
||||
def f3():
|
||||
f2()
|
||||
|
||||
|
||||
def ftop():
|
||||
try:
|
||||
f3()
|
||||
except ValueError as er:
|
||||
print_exc(er)
|
||||
|
||||
|
||||
ftop()
|
||||
|
||||
for limit in range(4, -2, -1):
|
||||
print("limit", limit)
|
||||
sys.tracebacklimit = limit
|
||||
ftop()
|
||||
|
||||
|
||||
# test deleting the attribute
|
||||
print(hasattr(sys, "tracebacklimit"))
|
||||
del sys.tracebacklimit
|
||||
print(hasattr(sys, "tracebacklimit"))
|
|
@ -504,6 +504,7 @@ def run_tests(pyb, tests, args, result_dir, num_threads=1):
|
|||
skip_tests.add("basics/del_local.py") # requires checking for unbound local
|
||||
skip_tests.add("basics/exception_chain.py") # raise from is not supported
|
||||
skip_tests.add("basics/scope_implicit.py") # requires checking for unbound local
|
||||
skip_tests.add("basics/sys_tracebacklimit.py") # requires traceback info
|
||||
skip_tests.add("basics/try_finally_return2.py") # requires raise_varargs
|
||||
skip_tests.add("basics/unboundlocal.py") # requires checking for unbound local
|
||||
skip_tests.add("extmod/uasyncio_event.py") # unknown issue
|
||||
|
|
|
@ -45,8 +45,8 @@ utime utimeq
|
|||
argv atexit byteorder exc_info
|
||||
exit getsizeof implementation maxsize
|
||||
modules path platform print_exception
|
||||
stderr stdin stdout version
|
||||
version_info
|
||||
stderr stdin stdout tracebacklimit
|
||||
version version_info
|
||||
ementation
|
||||
# attrtuple
|
||||
(start=1, stop=2, step=3)
|
||||
|
|
Loading…
Reference in New Issue