[Python-checkins] bpo-31949: Fixed several issues in printing tracebacks (PyTraceBack_Print()). (#4289)

Serhiy Storchaka webhook-mailer at python.org
Wed Nov 15 10:38:55 EST 2017


https://github.com/python/cpython/commit/edad8eebeee3c99e324a7f1ac5073167c2b0b54d
commit: edad8eebeee3c99e324a7f1ac5073167c2b0b54d
branch: master
author: Serhiy Storchaka <storchaka at gmail.com>
committer: GitHub <noreply at github.com>
date: 2017-11-15T17:38:52+02:00
summary:

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.

files:
A Misc/NEWS.d/next/Core and Builtins/2017-11-05-16-11-07.bpo-31949.2yNC_z.rst
M Lib/test/test_sys.py
M Python/traceback.c

diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py
index 50eb1b7c578..20965b9fd6f 100644
--- a/Lib/test/test_sys.py
+++ b/Lib/test/test_sys.py
@@ -808,6 +808,39 @@ def test_getandroidapilevel(self):
         self.assertIsInstance(level, int)
         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
 class SizeofTest(unittest.TestCase):
diff --git a/Misc/NEWS.d/next/Core and Builtins/2017-11-05-16-11-07.bpo-31949.2yNC_z.rst b/Misc/NEWS.d/next/Core and Builtins/2017-11-05-16-11-07.bpo-31949.2yNC_z.rst
new file mode 100644
index 00000000000..029cb57939f
--- /dev/null
+++ b/Misc/NEWS.d/next/Core and Builtins/2017-11-05-16-11-07.bpo-31949.2yNC_z.rst	
@@ -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.
diff --git a/Python/traceback.c b/Python/traceback.c
index 21b36b14719..831b4f26249 100644
--- a/Python/traceback.c
+++ b/Python/traceback.c
@@ -415,56 +415,67 @@ tb_displayline(PyObject *f, PyObject *filename, int lineno, PyObject *name)
 }
 
 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
 tb_printinternal(PyTracebackObject *tb, PyObject *f, long limit)
 {
     int err = 0;
-    long depth = 0;
+    Py_ssize_t depth = 0;
     PyObject *last_file = NULL;
     int last_line = -1;
     PyObject *last_name = NULL;
     long cnt = 0;
-    PyObject *line;
     PyTracebackObject *tb1 = tb;
     while (tb1 != NULL) {
         depth++;
         tb1 = tb1->tb_next;
     }
+    while (tb != NULL && depth > limit) {
+        depth--;
+        tb = tb->tb_next;
+    }
     while (tb != NULL && err == 0) {
-        if (depth <= limit) {
-            if (last_file != NULL &&
-                tb->tb_frame->f_code->co_filename == last_file &&
-                last_line != -1 && tb->tb_lineno == last_line &&
-                last_name != NULL &&
-                tb->tb_frame->f_code->co_name == last_name) {
-                    cnt++;
-                } else {
-                    if (cnt > 3) {
-                        line = PyUnicode_FromFormat(
-                        "  [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_line = tb->tb_lineno;
-                    last_name = tb->tb_frame->f_code->co_name;
-                    cnt = 0;
-                }
-            if (cnt < 3)
-                err = tb_displayline(f,
-                                     tb->tb_frame->f_code->co_filename,
-                                     tb->tb_lineno,
-                                     tb->tb_frame->f_code->co_name);
+        if (last_file != NULL &&
+            tb->tb_frame->f_code->co_filename == last_file &&
+            last_line != -1 && tb->tb_lineno == last_line &&
+            last_name != NULL && tb->tb_frame->f_code->co_name == last_name)
+        {
+            cnt++;
+        }
+        else {
+            if (cnt > 3) {
+                err = tb_print_line_repeated(f, cnt);
+            }
+            last_file = tb->tb_frame->f_code->co_filename;
+            last_line = tb->tb_lineno;
+            last_name = tb->tb_frame->f_code->co_name;
+            cnt = 0;
+        }
+        if (err == 0 && cnt < 3) {
+            err = tb_displayline(f,
+                                 tb->tb_frame->f_code->co_filename,
+                                 tb->tb_lineno,
+                                 tb->tb_frame->f_code->co_name);
+            if (err == 0) {
+                err = PyErr_CheckSignals();
+            }
         }
-        depth--;
         tb = tb->tb_next;
-        if (err == 0)
-            err = PyErr_CheckSignals();
     }
-    if (cnt > 3) {
-        line = PyUnicode_FromFormat(
-        "  [Previous line repeated %d more times]\n", cnt-3);
-        err = PyFile_WriteObject(line, f, Py_PRINT_RAW);
-        Py_DECREF(line);
+    if (err == 0 && cnt > 3) {
+        err = tb_print_line_repeated(f, cnt);
     }
     return err;
 }
@@ -485,26 +496,15 @@ PyTraceBack_Print(PyObject *v, PyObject *f)
         return -1;
     }
     limitv = PySys_GetObject("tracebacklimit");
-    if (limitv) {
-        PyObject *exc_type, *exc_value, *exc_tb;
-
-        PyErr_Fetch(&exc_type, &exc_value, &exc_tb);
-        limit = PyLong_AsLong(limitv);
-        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;
-            }
+    if (limitv && PyLong_Check(limitv)) {
+        int overflow;
+        limit = PyLong_AsLongAndOverflow(limitv, &overflow);
+        if (overflow > 0) {
+            limit = LONG_MAX;
         }
         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);
     if (!err)



More information about the Python-checkins mailing list