mirror of
https://github.com/python/cpython.git
synced 2025-09-02 23:18:25 +00:00
bpo-31949: Fixed several issues in printing tracebacks (PyTraceBack_Print()). (#4289)
* Setting sys.tracebacklimit to 0 or less now suppresses printing tracebacks. * Setting sys.tracebacklimit to None now causes using the default limit. * Setting sys.tracebacklimit to an integer larger than LONG_MAX now means using the limit LONG_MAX rather than the default limit. * Fixed integer overflows in the case of more than 2**31 traceback items on Windows. * Fixed output errors handling.
This commit is contained in:
parent
6545256df9
commit
edad8eebee
3 changed files with 94 additions and 52 deletions
|
@ -808,6 +808,39 @@ class SysModuleTest(unittest.TestCase):
|
||||||
self.assertIsInstance(level, int)
|
self.assertIsInstance(level, int)
|
||||||
self.assertGreater(level, 0)
|
self.assertGreater(level, 0)
|
||||||
|
|
||||||
|
def test_sys_tracebacklimit(self):
|
||||||
|
code = """if 1:
|
||||||
|
import sys
|
||||||
|
def f1():
|
||||||
|
1 / 0
|
||||||
|
def f2():
|
||||||
|
f1()
|
||||||
|
sys.tracebacklimit = %r
|
||||||
|
f2()
|
||||||
|
"""
|
||||||
|
def check(tracebacklimit, expected):
|
||||||
|
p = subprocess.Popen([sys.executable, '-c', code % tracebacklimit],
|
||||||
|
stderr=subprocess.PIPE)
|
||||||
|
out = p.communicate()[1]
|
||||||
|
self.assertEqual(out.splitlines(), expected)
|
||||||
|
|
||||||
|
traceback = [
|
||||||
|
b'Traceback (most recent call last):',
|
||||||
|
b' File "<string>", line 8, in <module>',
|
||||||
|
b' File "<string>", line 6, in f2',
|
||||||
|
b' File "<string>", line 4, in f1',
|
||||||
|
b'ZeroDivisionError: division by zero'
|
||||||
|
]
|
||||||
|
check(10, traceback)
|
||||||
|
check(3, traceback)
|
||||||
|
check(2, traceback[:1] + traceback[2:])
|
||||||
|
check(1, traceback[:1] + traceback[3:])
|
||||||
|
check(0, [traceback[-1]])
|
||||||
|
check(-1, [traceback[-1]])
|
||||||
|
check(1<<1000, traceback)
|
||||||
|
check(-1<<1000, [traceback[-1]])
|
||||||
|
check(None, traceback)
|
||||||
|
|
||||||
|
|
||||||
@test.support.cpython_only
|
@test.support.cpython_only
|
||||||
class SizeofTest(unittest.TestCase):
|
class SizeofTest(unittest.TestCase):
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
Fixed several issues in printing tracebacks (PyTraceBack_Print()).
|
||||||
|
|
||||||
|
* Setting sys.tracebacklimit to 0 or less now suppresses printing tracebacks.
|
||||||
|
* Setting sys.tracebacklimit to None now causes using the default limit.
|
||||||
|
* Setting sys.tracebacklimit to an integer larger than LONG_MAX now means using
|
||||||
|
the limit LONG_MAX rather than the default limit.
|
||||||
|
* Fixed integer overflows in the case of more than 2**31 traceback items on
|
||||||
|
Windows.
|
||||||
|
* Fixed output errors handling.
|
|
@ -414,57 +414,68 @@ tb_displayline(PyObject *f, PyObject *filename, int lineno, PyObject *name)
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
tb_print_line_repeated(PyObject *f, long cnt)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
PyObject *line = PyUnicode_FromFormat(
|
||||||
|
" [Previous line repeated %ld more times]\n", cnt-3);
|
||||||
|
if (line == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
err = PyFile_WriteObject(line, f, Py_PRINT_RAW);
|
||||||
|
Py_DECREF(line);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
tb_printinternal(PyTracebackObject *tb, PyObject *f, long limit)
|
tb_printinternal(PyTracebackObject *tb, PyObject *f, long limit)
|
||||||
{
|
{
|
||||||
int err = 0;
|
int err = 0;
|
||||||
long depth = 0;
|
Py_ssize_t depth = 0;
|
||||||
PyObject *last_file = NULL;
|
PyObject *last_file = NULL;
|
||||||
int last_line = -1;
|
int last_line = -1;
|
||||||
PyObject *last_name = NULL;
|
PyObject *last_name = NULL;
|
||||||
long cnt = 0;
|
long cnt = 0;
|
||||||
PyObject *line;
|
|
||||||
PyTracebackObject *tb1 = tb;
|
PyTracebackObject *tb1 = tb;
|
||||||
while (tb1 != NULL) {
|
while (tb1 != NULL) {
|
||||||
depth++;
|
depth++;
|
||||||
tb1 = tb1->tb_next;
|
tb1 = tb1->tb_next;
|
||||||
}
|
}
|
||||||
|
while (tb != NULL && depth > limit) {
|
||||||
|
depth--;
|
||||||
|
tb = tb->tb_next;
|
||||||
|
}
|
||||||
while (tb != NULL && err == 0) {
|
while (tb != NULL && err == 0) {
|
||||||
if (depth <= limit) {
|
|
||||||
if (last_file != NULL &&
|
if (last_file != NULL &&
|
||||||
tb->tb_frame->f_code->co_filename == last_file &&
|
tb->tb_frame->f_code->co_filename == last_file &&
|
||||||
last_line != -1 && tb->tb_lineno == last_line &&
|
last_line != -1 && tb->tb_lineno == last_line &&
|
||||||
last_name != NULL &&
|
last_name != NULL && tb->tb_frame->f_code->co_name == last_name)
|
||||||
tb->tb_frame->f_code->co_name == last_name) {
|
{
|
||||||
cnt++;
|
cnt++;
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
if (cnt > 3) {
|
if (cnt > 3) {
|
||||||
line = PyUnicode_FromFormat(
|
err = tb_print_line_repeated(f, cnt);
|
||||||
" [Previous line repeated %d more times]\n", cnt-3);
|
|
||||||
err = PyFile_WriteObject(line, f, Py_PRINT_RAW);
|
|
||||||
Py_DECREF(line);
|
|
||||||
}
|
}
|
||||||
last_file = tb->tb_frame->f_code->co_filename;
|
last_file = tb->tb_frame->f_code->co_filename;
|
||||||
last_line = tb->tb_lineno;
|
last_line = tb->tb_lineno;
|
||||||
last_name = tb->tb_frame->f_code->co_name;
|
last_name = tb->tb_frame->f_code->co_name;
|
||||||
cnt = 0;
|
cnt = 0;
|
||||||
}
|
}
|
||||||
if (cnt < 3)
|
if (err == 0 && cnt < 3) {
|
||||||
err = tb_displayline(f,
|
err = tb_displayline(f,
|
||||||
tb->tb_frame->f_code->co_filename,
|
tb->tb_frame->f_code->co_filename,
|
||||||
tb->tb_lineno,
|
tb->tb_lineno,
|
||||||
tb->tb_frame->f_code->co_name);
|
tb->tb_frame->f_code->co_name);
|
||||||
}
|
if (err == 0) {
|
||||||
depth--;
|
|
||||||
tb = tb->tb_next;
|
|
||||||
if (err == 0)
|
|
||||||
err = PyErr_CheckSignals();
|
err = PyErr_CheckSignals();
|
||||||
}
|
}
|
||||||
if (cnt > 3) {
|
}
|
||||||
line = PyUnicode_FromFormat(
|
tb = tb->tb_next;
|
||||||
" [Previous line repeated %d more times]\n", cnt-3);
|
}
|
||||||
err = PyFile_WriteObject(line, f, Py_PRINT_RAW);
|
if (err == 0 && cnt > 3) {
|
||||||
Py_DECREF(line);
|
err = tb_print_line_repeated(f, cnt);
|
||||||
}
|
}
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
@ -485,26 +496,15 @@ PyTraceBack_Print(PyObject *v, PyObject *f)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
limitv = PySys_GetObject("tracebacklimit");
|
limitv = PySys_GetObject("tracebacklimit");
|
||||||
if (limitv) {
|
if (limitv && PyLong_Check(limitv)) {
|
||||||
PyObject *exc_type, *exc_value, *exc_tb;
|
int overflow;
|
||||||
|
limit = PyLong_AsLongAndOverflow(limitv, &overflow);
|
||||||
PyErr_Fetch(&exc_type, &exc_value, &exc_tb);
|
if (overflow > 0) {
|
||||||
limit = PyLong_AsLong(limitv);
|
limit = LONG_MAX;
|
||||||
if (limit == -1 && PyErr_Occurred()) {
|
|
||||||
if (PyErr_ExceptionMatches(PyExc_OverflowError)) {
|
|
||||||
limit = PyTraceBack_LIMIT;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
Py_XDECREF(exc_type);
|
|
||||||
Py_XDECREF(exc_value);
|
|
||||||
Py_XDECREF(exc_tb);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (limit <= 0) {
|
else if (limit <= 0) {
|
||||||
limit = PyTraceBack_LIMIT;
|
return 0;
|
||||||
}
|
}
|
||||||
PyErr_Restore(exc_type, exc_value, exc_tb);
|
|
||||||
}
|
}
|
||||||
err = PyFile_WriteString("Traceback (most recent call last):\n", f);
|
err = PyFile_WriteString("Traceback (most recent call last):\n", f);
|
||||||
if (!err)
|
if (!err)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue