Skip to content

Commit 3046386

Browse files
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 <[email protected]> Co-authored-by: Łukasz Langa <[email protected]> Co-authored-by: Jelle Zijlstra <[email protected]> (cherry picked from commit ee0ac32) Co-authored-by: Victor Stinner <[email protected]>
1 parent ad9cf2f commit 3046386

File tree

5 files changed

+47
-23
lines changed

5 files changed

+47
-23
lines changed

Lib/lib2to3/pytree.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -720,8 +720,8 @@ def generate_matches(self, nodes):
720720
r[self.name] = nodes[:count]
721721
yield count, r
722722
except RuntimeError:
723-
# We fall back to the iterative pattern matching scheme if the recursive
724-
# scheme hits the recursion limit.
723+
# Fall back to the iterative pattern matching scheme if the
724+
# recursive scheme hits the recursion limit (RecursionError).
725725
for count, r in self._iterative_matches(nodes):
726726
if self.name:
727727
r[self.name] = nodes[:count]

Lib/lib2to3/tests/data/infinite_recursion.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
# This file is used to verify that 2to3 falls back to a slower, iterative pattern matching
2-
# scheme in the event that the faster recursive system fails due to infinite recursion.
1+
# Verify that 2to3 falls back from the recursive pattern matching scheme to a
2+
# slower, iterative scheme in the event of a RecursionError.
33
from ctypes import *
44
STRING = c_char_p
55

Lib/lib2to3/tests/test_all_fixers.py

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,10 @@
66
# Author: Collin Winter
77

88
# Python imports
9-
import unittest
9+
import os.path
10+
import sys
1011
import test.support
12+
import unittest
1113

1214
# Local imports
1315
from . import support
@@ -19,9 +21,22 @@ class Test_all(support.TestCase):
1921
def setUp(self):
2022
self.refactor = support.get_refactorer()
2123

24+
def refactor_file(self, filepath):
25+
if test.support.verbose:
26+
print(f"Refactor file: {filepath}")
27+
if os.path.basename(filepath) == 'infinite_recursion.py':
28+
# bpo-46542: Processing infinite_recursion.py can crash Python
29+
# if Python is built in debug mode: lower the recursion limit
30+
# to prevent a crash.
31+
with test.support.infinite_recursion(150):
32+
self.refactor.refactor_file(filepath)
33+
else:
34+
self.refactor.refactor_file(filepath)
35+
2236
def test_all_project_files(self):
2337
for filepath in support.all_project_files():
24-
self.refactor.refactor_file(filepath)
38+
with self.subTest(filepath=filepath):
39+
self.refactor_file(filepath)
2540

2641
if __name__ == '__main__':
2742
unittest.main()

Lib/lib2to3/tests/test_parser.py

Lines changed: 24 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import subprocess
2121
import sys
2222
import tempfile
23+
import test.support
2324
import unittest
2425

2526
# Local imports
@@ -582,25 +583,31 @@ class TestParserIdempotency(support.TestCase):
582583

583584
"""A cut-down version of pytree_idempotency.py."""
584585

586+
def parse_file(self, filepath):
587+
if test.support.verbose:
588+
print(f"Parse file: {filepath}")
589+
with open(filepath, "rb") as fp:
590+
encoding = tokenize.detect_encoding(fp.readline)[0]
591+
self.assertIsNotNone(encoding,
592+
"can't detect encoding for %s" % filepath)
593+
with open(filepath, "r", encoding=encoding) as fp:
594+
source = fp.read()
595+
try:
596+
tree = driver.parse_string(source)
597+
except ParseError:
598+
try:
599+
tree = driver_no_print_statement.parse_string(source)
600+
except ParseError as err:
601+
self.fail('ParseError on file %s (%s)' % (filepath, err))
602+
new = str(tree)
603+
if new != source:
604+
print(diff_texts(source, new, filepath))
605+
self.fail("Idempotency failed: %s" % filepath)
606+
585607
def test_all_project_files(self):
586608
for filepath in support.all_project_files():
587-
with open(filepath, "rb") as fp:
588-
encoding = tokenize.detect_encoding(fp.readline)[0]
589-
self.assertIsNotNone(encoding,
590-
"can't detect encoding for %s" % filepath)
591-
with open(filepath, "r", encoding=encoding) as fp:
592-
source = fp.read()
593-
try:
594-
tree = driver.parse_string(source)
595-
except ParseError:
596-
try:
597-
tree = driver_no_print_statement.parse_string(source)
598-
except ParseError as err:
599-
self.fail('ParseError on file %s (%s)' % (filepath, err))
600-
new = str(tree)
601-
if new != source:
602-
print(diff_texts(source, new, filepath))
603-
self.fail("Idempotency failed: %s" % filepath)
609+
with self.subTest(filepath=filepath):
610+
self.parse_file(filepath)
604611

605612
def test_extended_unpacking(self):
606613
driver.parse_string("a, *b, c = x\n")
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix a Python crash in test_lib2to3 when using Python built in debug mode:
2+
limit the recursion limit. Patch by Victor Stinner.

0 commit comments

Comments
 (0)