mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 13:41:24 +00:00 
			
		
		
		
	bpo-46542: test_lib2to3 uses support.infinite_recursion() (GH-31035)
* bpo-46542: test_lib2to3 uses support.infinite_recursion() Fix a Python crash in test_lib2to3 when using Python built in debug mode: limit the recursion limit. The test_all_project_files() test of test_lib2to3 now uses the test.support.infinite_recursion() context manager when processing the infinite_recursion.py file to prevent a crash when Python is built in debug mode. The two test_all_project_files() tests now use subTest() and log the refactored/parsed filename (if test_lib2to3 is run in verbose mode). * Update Lib/lib2to3/tests/data/infinite_recursion.py Co-authored-by: Jelle Zijlstra <jelle.zijlstra@gmail.com> Co-authored-by: Łukasz Langa <lukasz@langa.pl> Co-authored-by: Jelle Zijlstra <jelle.zijlstra@gmail.com>
This commit is contained in:
		
							parent
							
								
									768569325a
								
							
						
					
					
						commit
						ee0ac328d3
					
				
					 5 changed files with 47 additions and 23 deletions
				
			
		|  | @ -720,8 +720,8 @@ def generate_matches(self, nodes): | ||||||
|                         r[self.name] = nodes[:count] |                         r[self.name] = nodes[:count] | ||||||
|                     yield count, r |                     yield count, r | ||||||
|             except RuntimeError: |             except RuntimeError: | ||||||
|                 # We fall back to the iterative pattern matching scheme if the recursive |                 # Fall back to the iterative pattern matching scheme if the | ||||||
|                 # scheme hits the recursion limit. |                 # recursive scheme hits the recursion limit (RecursionError). | ||||||
|                 for count, r in self._iterative_matches(nodes): |                 for count, r in self._iterative_matches(nodes): | ||||||
|                     if self.name: |                     if self.name: | ||||||
|                         r[self.name] = nodes[:count] |                         r[self.name] = nodes[:count] | ||||||
|  |  | ||||||
|  | @ -1,5 +1,5 @@ | ||||||
| # This file is used to verify that 2to3 falls back to a slower, iterative pattern matching | # Verify that 2to3 falls back from the recursive pattern matching scheme to a | ||||||
| # scheme in the event that the faster recursive system fails due to infinite recursion. | # slower, iterative scheme in the event of a RecursionError. | ||||||
| from ctypes import * | from ctypes import * | ||||||
| STRING = c_char_p | STRING = c_char_p | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -6,8 +6,10 @@ | ||||||
| # Author: Collin Winter | # Author: Collin Winter | ||||||
| 
 | 
 | ||||||
| # Python imports | # Python imports | ||||||
| import unittest | import os.path | ||||||
|  | import sys | ||||||
| import test.support | import test.support | ||||||
|  | import unittest | ||||||
| 
 | 
 | ||||||
| # Local imports | # Local imports | ||||||
| from . import support | from . import support | ||||||
|  | @ -19,9 +21,22 @@ class Test_all(support.TestCase): | ||||||
|     def setUp(self): |     def setUp(self): | ||||||
|         self.refactor = support.get_refactorer() |         self.refactor = support.get_refactorer() | ||||||
| 
 | 
 | ||||||
|  |     def refactor_file(self, filepath): | ||||||
|  |         if test.support.verbose: | ||||||
|  |             print(f"Refactor file: {filepath}") | ||||||
|  |         if os.path.basename(filepath) == 'infinite_recursion.py': | ||||||
|  |             # bpo-46542: Processing infinite_recursion.py can crash Python | ||||||
|  |             # if Python is built in debug mode: lower the recursion limit | ||||||
|  |             # to prevent a crash. | ||||||
|  |             with test.support.infinite_recursion(150): | ||||||
|  |                 self.refactor.refactor_file(filepath) | ||||||
|  |         else: | ||||||
|  |             self.refactor.refactor_file(filepath) | ||||||
|  | 
 | ||||||
|     def test_all_project_files(self): |     def test_all_project_files(self): | ||||||
|         for filepath in support.all_project_files(): |         for filepath in support.all_project_files(): | ||||||
|             self.refactor.refactor_file(filepath) |             with self.subTest(filepath=filepath): | ||||||
|  |                 self.refactor_file(filepath) | ||||||
| 
 | 
 | ||||||
| if __name__ == '__main__': | if __name__ == '__main__': | ||||||
|     unittest.main() |     unittest.main() | ||||||
|  |  | ||||||
|  | @ -20,6 +20,7 @@ | ||||||
| import subprocess | import subprocess | ||||||
| import sys | import sys | ||||||
| import tempfile | import tempfile | ||||||
|  | import test.support | ||||||
| import unittest | import unittest | ||||||
| 
 | 
 | ||||||
| # Local imports | # Local imports | ||||||
|  | @ -589,25 +590,31 @@ class TestParserIdempotency(support.TestCase): | ||||||
| 
 | 
 | ||||||
|     """A cut-down version of pytree_idempotency.py.""" |     """A cut-down version of pytree_idempotency.py.""" | ||||||
| 
 | 
 | ||||||
|  |     def parse_file(self, filepath): | ||||||
|  |         if test.support.verbose: | ||||||
|  |             print(f"Parse file: {filepath}") | ||||||
|  |         with open(filepath, "rb") as fp: | ||||||
|  |             encoding = tokenize.detect_encoding(fp.readline)[0] | ||||||
|  |         self.assertIsNotNone(encoding, | ||||||
|  |                              "can't detect encoding for %s" % filepath) | ||||||
|  |         with open(filepath, "r", encoding=encoding) as fp: | ||||||
|  |             source = fp.read() | ||||||
|  |         try: | ||||||
|  |             tree = driver.parse_string(source) | ||||||
|  |         except ParseError: | ||||||
|  |             try: | ||||||
|  |                 tree = driver_no_print_statement.parse_string(source) | ||||||
|  |             except ParseError as err: | ||||||
|  |                 self.fail('ParseError on file %s (%s)' % (filepath, err)) | ||||||
|  |         new = str(tree) | ||||||
|  |         if new != source: | ||||||
|  |             print(diff_texts(source, new, filepath)) | ||||||
|  |             self.fail("Idempotency failed: %s" % filepath) | ||||||
|  | 
 | ||||||
|     def test_all_project_files(self): |     def test_all_project_files(self): | ||||||
|         for filepath in support.all_project_files(): |         for filepath in support.all_project_files(): | ||||||
|             with open(filepath, "rb") as fp: |             with self.subTest(filepath=filepath): | ||||||
|                 encoding = tokenize.detect_encoding(fp.readline)[0] |                 self.parse_file(filepath) | ||||||
|             self.assertIsNotNone(encoding, |  | ||||||
|                                  "can't detect encoding for %s" % filepath) |  | ||||||
|             with open(filepath, "r", encoding=encoding) as fp: |  | ||||||
|                 source = fp.read() |  | ||||||
|             try: |  | ||||||
|                 tree = driver.parse_string(source) |  | ||||||
|             except ParseError: |  | ||||||
|                 try: |  | ||||||
|                     tree = driver_no_print_statement.parse_string(source) |  | ||||||
|                 except ParseError as err: |  | ||||||
|                     self.fail('ParseError on file %s (%s)' % (filepath, err)) |  | ||||||
|             new = str(tree) |  | ||||||
|             if new != source: |  | ||||||
|                 print(diff_texts(source, new, filepath)) |  | ||||||
|                 self.fail("Idempotency failed: %s" % filepath) |  | ||||||
| 
 | 
 | ||||||
|     def test_extended_unpacking(self): |     def test_extended_unpacking(self): | ||||||
|         driver.parse_string("a, *b, c = x\n") |         driver.parse_string("a, *b, c = x\n") | ||||||
|  |  | ||||||
|  | @ -0,0 +1,2 @@ | ||||||
|  | Fix a Python crash in test_lib2to3 when using Python built in debug mode: | ||||||
|  | limit the recursion limit. Patch by Victor Stinner. | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Victor Stinner
						Victor Stinner