Skip to content

Commit edad8ee

Browse files
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.
1 parent 6545256 commit edad8ee

File tree

3 files changed

+93
-51
lines changed

3 files changed

+93
-51
lines changed

Lib/test/test_sys.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -808,6 +808,39 @@ def test_getandroidapilevel(self):
808808
self.assertIsInstance(level, int)
809809
self.assertGreater(level, 0)
810810

811+
def test_sys_tracebacklimit(self):
812+
code = """if 1:
813+
import sys
814+
def f1():
815+
1 / 0
816+
def f2():
817+
f1()
818+
sys.tracebacklimit = %r
819+
f2()
820+
"""
821+
def check(tracebacklimit, expected):
822+
p = subprocess.Popen([sys.executable, '-c', code % tracebacklimit],
823+
stderr=subprocess.PIPE)
824+
out = p.communicate()[1]
825+
self.assertEqual(out.splitlines(), expected)
826+
827+
traceback = [
828+
b'Traceback (most recent call last):',
829+
b' File "<string>", line 8, in <module>',
830+
b' File "<string>", line 6, in f2',
831+
b' File "<string>", line 4, in f1',
832+
b'ZeroDivisionError: division by zero'
833+
]
834+
check(10, traceback)
835+
check(3, traceback)
836+
check(2, traceback[:1] + traceback[2:])
837+
check(1, traceback[:1] + traceback[3:])
838+
check(0, [traceback[-1]])
839+
check(-1, [traceback[-1]])
840+
check(1<<1000, traceback)
841+
check(-1<<1000, [traceback[-1]])
842+
check(None, traceback)
843+
811844

812845
@test.support.cpython_only
813846
class SizeofTest(unittest.TestCase):
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
Fixed several issues in printing tracebacks (PyTraceBack_Print()).
2+
3+
* Setting sys.tracebacklimit to 0 or less now suppresses printing tracebacks.
4+
* Setting sys.tracebacklimit to None now causes using the default limit.
5+
* Setting sys.tracebacklimit to an integer larger than LONG_MAX now means using
6+
the limit LONG_MAX rather than the default limit.
7+
* Fixed integer overflows in the case of more than 2**31 traceback items on
8+
Windows.
9+
* Fixed output errors handling.

Python/traceback.c

Lines changed: 51 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -414,57 +414,68 @@ tb_displayline(PyObject *f, PyObject *filename, int lineno, PyObject *name)
414414
return err;
415415
}
416416

417+
static int
418+
tb_print_line_repeated(PyObject *f, long cnt)
419+
{
420+
int err;
421+
PyObject *line = PyUnicode_FromFormat(
422+
" [Previous line repeated %ld more times]\n", cnt-3);
423+
if (line == NULL) {
424+
return -1;
425+
}
426+
err = PyFile_WriteObject(line, f, Py_PRINT_RAW);
427+
Py_DECREF(line);
428+
return err;
429+
}
430+
417431
static int
418432
tb_printinternal(PyTracebackObject *tb, PyObject *f, long limit)
419433
{
420434
int err = 0;
421-
long depth = 0;
435+
Py_ssize_t depth = 0;
422436
PyObject *last_file = NULL;
423437
int last_line = -1;
424438
PyObject *last_name = NULL;
425439
long cnt = 0;
426-
PyObject *line;
427440
PyTracebackObject *tb1 = tb;
428441
while (tb1 != NULL) {
429442
depth++;
430443
tb1 = tb1->tb_next;
431444
}
445+
while (tb != NULL && depth > limit) {
446+
depth--;
447+
tb = tb->tb_next;
448+
}
432449
while (tb != NULL && err == 0) {
433-
if (depth <= limit) {
434-
if (last_file != NULL &&
435-
tb->tb_frame->f_code->co_filename == last_file &&
436-
last_line != -1 && tb->tb_lineno == last_line &&
437-
last_name != NULL &&
438-
tb->tb_frame->f_code->co_name == last_name) {
439-
cnt++;
440-
} else {
441-
if (cnt > 3) {
442-
line = PyUnicode_FromFormat(
443-
" [Previous line repeated %d more times]\n", cnt-3);
444-
err = PyFile_WriteObject(line, f, Py_PRINT_RAW);
445-
Py_DECREF(line);
446-
}
447-
last_file = tb->tb_frame->f_code->co_filename;
448-
last_line = tb->tb_lineno;
449-
last_name = tb->tb_frame->f_code->co_name;
450-
cnt = 0;
451-
}
452-
if (cnt < 3)
453-
err = tb_displayline(f,
454-
tb->tb_frame->f_code->co_filename,
455-
tb->tb_lineno,
456-
tb->tb_frame->f_code->co_name);
450+
if (last_file != NULL &&
451+
tb->tb_frame->f_code->co_filename == last_file &&
452+
last_line != -1 && tb->tb_lineno == last_line &&
453+
last_name != NULL && tb->tb_frame->f_code->co_name == last_name)
454+
{
455+
cnt++;
456+
}
457+
else {
458+
if (cnt > 3) {
459+
err = tb_print_line_repeated(f, cnt);
460+
}
461+
last_file = tb->tb_frame->f_code->co_filename;
462+
last_line = tb->tb_lineno;
463+
last_name = tb->tb_frame->f_code->co_name;
464+
cnt = 0;
465+
}
466+
if (err == 0 && cnt < 3) {
467+
err = tb_displayline(f,
468+
tb->tb_frame->f_code->co_filename,
469+
tb->tb_lineno,
470+
tb->tb_frame->f_code->co_name);
471+
if (err == 0) {
472+
err = PyErr_CheckSignals();
473+
}
457474
}
458-
depth--;
459475
tb = tb->tb_next;
460-
if (err == 0)
461-
err = PyErr_CheckSignals();
462476
}
463-
if (cnt > 3) {
464-
line = PyUnicode_FromFormat(
465-
" [Previous line repeated %d more times]\n", cnt-3);
466-
err = PyFile_WriteObject(line, f, Py_PRINT_RAW);
467-
Py_DECREF(line);
477+
if (err == 0 && cnt > 3) {
478+
err = tb_print_line_repeated(f, cnt);
468479
}
469480
return err;
470481
}
@@ -485,26 +496,15 @@ PyTraceBack_Print(PyObject *v, PyObject *f)
485496
return -1;
486497
}
487498
limitv = PySys_GetObject("tracebacklimit");
488-
if (limitv) {
489-
PyObject *exc_type, *exc_value, *exc_tb;
490-
491-
PyErr_Fetch(&exc_type, &exc_value, &exc_tb);
492-
limit = PyLong_AsLong(limitv);
493-
if (limit == -1 && PyErr_Occurred()) {
494-
if (PyErr_ExceptionMatches(PyExc_OverflowError)) {
495-
limit = PyTraceBack_LIMIT;
496-
}
497-
else {
498-
Py_XDECREF(exc_type);
499-
Py_XDECREF(exc_value);
500-
Py_XDECREF(exc_tb);
501-
return 0;
502-
}
499+
if (limitv && PyLong_Check(limitv)) {
500+
int overflow;
501+
limit = PyLong_AsLongAndOverflow(limitv, &overflow);
502+
if (overflow > 0) {
503+
limit = LONG_MAX;
503504
}
504505
else if (limit <= 0) {
505-
limit = PyTraceBack_LIMIT;
506+
return 0;
506507
}
507-
PyErr_Restore(exc_type, exc_value, exc_tb);
508508
}
509509
err = PyFile_WriteString("Traceback (most recent call last):\n", f);
510510
if (!err)

0 commit comments

Comments
 (0)