Skip to content

Commit 7d0959a

Browse files
gwostipablogsal
authored andcommitted
gh-155525: Cover quadratic f-string tokenization regression (GH-156756)
* gh-155525: Avoid quadratic f-string tokenization * fixup! gh-155525: Avoid quadratic f-string tokenization --------- (cherry picked from commit c1df684) Co-authored-by: gwosti <322952417+gwosti@users.noreply.github.com> Co-authored-by: Pablo Galindo Salgado <Pablogsal@gmail.com>
1 parent 0c66222 commit 7d0959a

3 files changed

Lines changed: 66 additions & 0 deletions

File tree

‎Lib/test/test_fstring.py‎

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -822,6 +822,18 @@ def build_fstr(n, extra=''):
822822
s = "f'{1}' 'x' 'y'" * 1024
823823
self.assertEqual(eval(s), '1xy' * 1024)
824824

825+
@support.requires_resource('cpu')
826+
def test_many_fstrings_in_module(self):
827+
fields = ''.join(f'{{x{i}}}' for i in range(100))
828+
source = ''.join(
829+
f"value_{i} = f'{fields}'\n" for i in range(1_000)
830+
)
831+
namespace = {f'x{i}': str(i) for i in range(100)}
832+
expected = ''.join(str(i) for i in range(100))
833+
exec(source, namespace)
834+
self.assertEqual(namespace['value_0'], expected)
835+
self.assertEqual(namespace['value_999'], expected)
836+
825837
def test_format_specifier_expressions(self):
826838
width = 10
827839
precision = 4
@@ -1338,6 +1350,9 @@ def test_not_equal(self):
13381350
self.assertEqual(f'{3!=4:}', 'True')
13391351
self.assertEqual(f'{3!=4!s}', 'True')
13401352
self.assertEqual(f'{3!=4!s:.3}', 'Tru')
1353+
a = 3
1354+
b = 4
1355+
self.assertEqual(f'{a!=b=:>10}', 'a!=b= 1')
13411356

13421357
def test_equal_equal(self):
13431358
# Because an expression ending in = has special meaning,
@@ -1792,6 +1807,19 @@ def test_debug_in_file(self):
17921807
self.assertEqual(stdout.decode('utf-8').strip().replace('\r\n', '\n').replace('\r', '\n'),
17931808
"3\n=3")
17941809

1810+
def test_debug_in_file_after_buffer_resize(self):
1811+
expression = "(\n" + (" " * 64 + "\n") * 256 + "1\n)"
1812+
expected = expression + "=1"
1813+
with temp_cwd():
1814+
script = 'script.py'
1815+
source = (
1816+
f"result = f'''{{{expression}=}}'''\n"
1817+
f"assert result == {expected!r}\n"
1818+
)
1819+
with open(script, 'w') as f:
1820+
f.write(source)
1821+
assert_python_ok(script)
1822+
17951823
def test_syntax_warning_infinite_recursion_in_file(self):
17961824
with temp_cwd():
17971825
script = 'script.py'

‎Lib/test/test_tstring.py‎

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
import unittest
22

3+
from test import support
4+
from test.support.os_helper import temp_cwd
5+
from test.support.script_helper import assert_python_ok
36
from test.test_string._support import TStringBaseCase, fstring
47

58

@@ -79,6 +82,31 @@ def upper(self):
7982
)
8083
self.assertEqual(fstring(t), "Name: Bob, Age: 30")
8184

85+
def test_interpolation_expression_in_file_after_buffer_resize(self):
86+
expression = "(\n" + (" " * 64 + "\n") * 256 + "1\n)"
87+
with temp_cwd():
88+
script = 'script.py'
89+
source = (
90+
f"template = t'''{{{expression}}}'''\n"
91+
"interpolation = template.interpolations[0]\n"
92+
f"assert interpolation.expression == {expression!r}\n"
93+
)
94+
with open(script, 'w') as f:
95+
f.write(source)
96+
assert_python_ok(script)
97+
98+
@support.requires_resource('cpu')
99+
def test_many_tstrings_in_module(self):
100+
fields = ''.join(f'{{x{i}}}' for i in range(100))
101+
source = ''.join(
102+
f"value_{i} = t'{fields}'\n" for i in range(1_000)
103+
)
104+
namespace = {f'x{i}': str(i) for i in range(100)}
105+
expected = ''.join(str(i) for i in range(100))
106+
exec(source, namespace)
107+
self.assertEqual(fstring(namespace['value_0']), expected)
108+
self.assertEqual(fstring(namespace['value_999']), expected)
109+
82110
def test_format_specifiers(self):
83111
# Test basic format specifiers
84112
value = 3.14159
@@ -88,6 +116,14 @@ def test_format_specifiers(self):
88116
)
89117
self.assertEqual(fstring(t), "Pi: 3.14")
90118

119+
a = 3
120+
b = 4
121+
t = t"{a!=b:>10}"
122+
self.assertTStringEqual(
123+
t, ("", ""), [(a != b, "a!=b", None, ">10")]
124+
)
125+
self.assertEqual(fstring(t), " 1")
126+
91127
def test_conversions(self):
92128
# Test !s conversion (str)
93129
obj = object()
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix quadratic-time tokenization of modules containing many f-strings or
2+
t-strings.

0 commit comments

Comments
 (0)