mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 13:41:24 +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 @@ def test_getandroidapilevel(self): | ||||||
|         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 && err == 0) { |     while (tb != NULL && depth > limit) { | ||||||
|         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); |  | ||||||
|         } |  | ||||||
|         depth--; |         depth--; | ||||||
|         tb = tb->tb_next; |         tb = tb->tb_next; | ||||||
|         if (err == 0) |  | ||||||
|             err = PyErr_CheckSignals(); |  | ||||||
|     } |     } | ||||||
|     if (cnt > 3) { |     while (tb != NULL && err == 0) { | ||||||
|         line = PyUnicode_FromFormat( |         if (last_file != NULL && | ||||||
|         "  [Previous line repeated %d more times]\n", cnt-3); |             tb->tb_frame->f_code->co_filename == last_file && | ||||||
|         err = PyFile_WriteObject(line, f, Py_PRINT_RAW); |             last_line != -1 && tb->tb_lineno == last_line && | ||||||
|         Py_DECREF(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(); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         tb = tb->tb_next; | ||||||
|  |     } | ||||||
|  |     if (err == 0 && cnt > 3) { | ||||||
|  |         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
	
	 Serhiy Storchaka
						Serhiy Storchaka