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`.
|
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
|
.. data:: version
|
||||||
|
|
||||||
Python language version that this implementation conforms to, as a string.
|
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_WORDS_MOVE (1)
|
||||||
#define MICROPY_REPL_EMACS_EXTRA_WORDS_MOVE (1)
|
#define MICROPY_REPL_EMACS_EXTRA_WORDS_MOVE (1)
|
||||||
#define MICROPY_WARNINGS_CATEGORY (1)
|
#define MICROPY_WARNINGS_CATEGORY (1)
|
||||||
|
#define MICROPY_MODULE_ATTR_DELEGATION (1)
|
||||||
#define MICROPY_MODULE_GETATTR (1)
|
#define MICROPY_MODULE_GETATTR (1)
|
||||||
#define MICROPY_PY_DELATTR_SETATTR (1)
|
#define MICROPY_PY_DELATTR_SETATTR (1)
|
||||||
#define MICROPY_PY_ALL_INPLACE_SPECIAL_METHODS (1)
|
#define MICROPY_PY_ALL_INPLACE_SPECIAL_METHODS (1)
|
||||||
|
@ -44,6 +45,7 @@
|
||||||
#define MICROPY_PY_BUILTINS_HELP (1)
|
#define MICROPY_PY_BUILTINS_HELP (1)
|
||||||
#define MICROPY_PY_BUILTINS_HELP_MODULES (1)
|
#define MICROPY_PY_BUILTINS_HELP_MODULES (1)
|
||||||
#define MICROPY_PY_SYS_GETSIZEOF (1)
|
#define MICROPY_PY_SYS_GETSIZEOF (1)
|
||||||
|
#define MICROPY_PY_SYS_TRACEBACKLIMIT (1)
|
||||||
#define MICROPY_PY_MATH_CONSTANTS (1)
|
#define MICROPY_PY_MATH_CONSTANTS (1)
|
||||||
#define MICROPY_PY_MATH_FACTORIAL (1)
|
#define MICROPY_PY_MATH_FACTORIAL (1)
|
||||||
#define MICROPY_PY_URANDOM_EXTRA_FUNCS (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
|
#if MICROPY_PY_SYS_ATTR_DELEGATION
|
||||||
STATIC const uint16_t sys_mutable_keys[] = {
|
STATIC const uint16_t sys_mutable_keys[] = {
|
||||||
|
#if MICROPY_PY_SYS_TRACEBACKLIMIT
|
||||||
|
MP_QSTR_tracebacklimit,
|
||||||
|
#endif
|
||||||
MP_QSTRnull,
|
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)
|
#define MICROPY_PY_SYS_STDIO_BUFFER (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES)
|
||||||
#endif
|
#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
|
// Whether the sys module supports attribute delegation
|
||||||
// This is enabled automatically when needed by other features
|
// This is enabled automatically when needed by other features
|
||||||
#ifndef MICROPY_PY_SYS_ATTR_DELEGATION
|
#ifndef MICROPY_PY_SYS_ATTR_DELEGATION
|
||||||
#define MICROPY_PY_SYS_ATTR_DELEGATION (0)
|
#define MICROPY_PY_SYS_ATTR_DELEGATION (MICROPY_PY_SYS_TRACEBACKLIMIT)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Whether to provide "uerrno" module
|
// Whether to provide "uerrno" module
|
||||||
|
|
|
@ -41,6 +41,9 @@
|
||||||
// variable, but in the future it is hoped that the state can become local.
|
// variable, but in the future it is hoped that the state can become local.
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
|
#if MICROPY_PY_SYS_TRACEBACKLIMIT
|
||||||
|
MP_SYS_MUTABLE_TRACEBACKLIMIT,
|
||||||
|
#endif
|
||||||
MP_SYS_MUTABLE_NUM,
|
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
|
// append this traceback info to traceback data
|
||||||
// if memory allocation fails (eg because gc is locked), just return
|
// 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) {
|
if (self->traceback_data == NULL) {
|
||||||
self->traceback_data = m_new_maybe(size_t, TRACEBACK_ENTRY_LEN);
|
self->traceback_data = m_new_maybe(size_t, TRACEBACK_ENTRY_LEN);
|
||||||
if (self->traceback_data == NULL) {
|
if (self->traceback_data == NULL) {
|
||||||
|
|
|
@ -141,6 +141,10 @@ void mp_init(void) {
|
||||||
MP_STATE_THREAD(current_code_state) = NULL;
|
MP_STATE_THREAD(current_code_state) = NULL;
|
||||||
#endif
|
#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
|
#if MICROPY_PY_BLUETOOTH
|
||||||
MP_STATE_VM(bluetooth) = MP_OBJ_NULL;
|
MP_STATE_VM(bluetooth) = MP_OBJ_NULL;
|
||||||
#endif
|
#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/del_local.py") # requires checking for unbound local
|
||||||
skip_tests.add("basics/exception_chain.py") # raise from is not supported
|
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/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/try_finally_return2.py") # requires raise_varargs
|
||||||
skip_tests.add("basics/unboundlocal.py") # requires checking for unbound local
|
skip_tests.add("basics/unboundlocal.py") # requires checking for unbound local
|
||||||
skip_tests.add("extmod/uasyncio_event.py") # unknown issue
|
skip_tests.add("extmod/uasyncio_event.py") # unknown issue
|
||||||
|
|
|
@ -45,8 +45,8 @@ utime utimeq
|
||||||
argv atexit byteorder exc_info
|
argv atexit byteorder exc_info
|
||||||
exit getsizeof implementation maxsize
|
exit getsizeof implementation maxsize
|
||||||
modules path platform print_exception
|
modules path platform print_exception
|
||||||
stderr stdin stdout version
|
stderr stdin stdout tracebacklimit
|
||||||
version_info
|
version version_info
|
||||||
ementation
|
ementation
|
||||||
# attrtuple
|
# attrtuple
|
||||||
(start=1, stop=2, step=3)
|
(start=1, stop=2, step=3)
|
||||||
|
|
Loading…
Reference in New Issue