https://github.com/python/cpython/commit/c1df6843d36233ec1da71a1d5f9b74dd6ebc9b97 commit: c1df6843d36233ec1da71a1d5f9b74dd6ebc9b97 branch: main author: gwosti <[email protected]> committer: pablogsal <[email protected]> date: 2026-09-19T22:04:42Z summary:
gh-155525: Cover quadratic f-string tokenization regression (#156756) * gh-155525: Avoid quadratic f-string tokenization * fixup! gh-155525: Avoid quadratic f-string tokenization --------- Co-authored-by: Pablo Galindo Salgado <[email protected]> files: A Misc/NEWS.d/next/Core_and_Builtins/2026-09-01-07-15-20.gh-issue-155525.A7kP2m.rst M Lib/test/test_fstring.py M Lib/test/test_tstring.py diff --git a/Lib/test/test_fstring.py b/Lib/test/test_fstring.py index 2d6320549b03f6..2fe959f6c14f77 100644 --- a/Lib/test/test_fstring.py +++ b/Lib/test/test_fstring.py @@ -832,6 +832,18 @@ def build_fstr(n, extra=''): s = "f'{1}' 'x' 'y'" * 1024 self.assertEqual(eval(s), '1xy' * 1024) + @support.requires_resource('cpu') + def test_many_fstrings_in_module(self): + fields = ''.join(f'{{x{i}}}' for i in range(100)) + source = ''.join( + f"value_{i} = f'{fields}'\n" for i in range(1_000) + ) + namespace = {f'x{i}': str(i) for i in range(100)} + expected = ''.join(str(i) for i in range(100)) + exec(source, namespace) + self.assertEqual(namespace['value_0'], expected) + self.assertEqual(namespace['value_999'], expected) + def test_format_specifier_expressions(self): width = 10 precision = 4 @@ -1348,6 +1360,9 @@ def test_not_equal(self): self.assertEqual(f'{3!=4:}', 'True') self.assertEqual(f'{3!=4!s}', 'True') self.assertEqual(f'{3!=4!s:.3}', 'Tru') + a = 3 + b = 4 + self.assertEqual(f'{a!=b=:>10}', 'a!=b= 1') def test_equal_equal(self): # Because an expression ending in = has special meaning, @@ -1819,6 +1834,19 @@ def test_debug_in_file(self): self.assertEqual(stdout.decode('utf-8').strip().replace('\r\n', '\n').replace('\r', '\n'), "3\n=3") + def test_debug_in_file_after_buffer_resize(self): + expression = "(\n" + (" " * 64 + "\n") * 256 + "1\n)" + expected = expression + "=1" + with temp_cwd(): + script = 'script.py' + source = ( + f"result = f'''{{{expression}=}}'''\n" + f"assert result == {expected!r}\n" + ) + with open(script, 'w') as f: + f.write(source) + assert_python_ok(script) + def test_syntax_warning_infinite_recursion_in_file(self): with temp_cwd(): script = 'script.py' diff --git a/Lib/test/test_tstring.py b/Lib/test/test_tstring.py index 67a8e0fc6bcffb..20a5083f60d11a 100644 --- a/Lib/test/test_tstring.py +++ b/Lib/test/test_tstring.py @@ -1,5 +1,8 @@ import unittest +from test import support +from test.support.os_helper import temp_cwd +from test.support.script_helper import assert_python_ok from test.test_string._support import TStringBaseCase, fstring @@ -79,6 +82,31 @@ def upper(self): ) self.assertEqual(fstring(t), "Name: Bob, Age: 30") + def test_interpolation_expression_in_file_after_buffer_resize(self): + expression = "(\n" + (" " * 64 + "\n") * 256 + "1\n)" + with temp_cwd(): + script = 'script.py' + source = ( + f"template = t'''{{{expression}}}'''\n" + "interpolation = template.interpolations[0]\n" + f"assert interpolation.expression == {expression!r}\n" + ) + with open(script, 'w') as f: + f.write(source) + assert_python_ok(script) + + @support.requires_resource('cpu') + def test_many_tstrings_in_module(self): + fields = ''.join(f'{{x{i}}}' for i in range(100)) + source = ''.join( + f"value_{i} = t'{fields}'\n" for i in range(1_000) + ) + namespace = {f'x{i}': str(i) for i in range(100)} + expected = ''.join(str(i) for i in range(100)) + exec(source, namespace) + self.assertEqual(fstring(namespace['value_0']), expected) + self.assertEqual(fstring(namespace['value_999']), expected) + def test_format_specifiers(self): # Test basic format specifiers value = 3.14159 @@ -88,6 +116,14 @@ def test_format_specifiers(self): ) self.assertEqual(fstring(t), "Pi: 3.14") + a = 3 + b = 4 + t = t"{a!=b:>10}" + self.assertTStringEqual( + t, ("", ""), [(a != b, "a!=b", None, ">10")] + ) + self.assertEqual(fstring(t), " 1") + def test_conversions(self): # Test !s conversion (str) obj = object() diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-09-01-07-15-20.gh-issue-155525.A7kP2m.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-09-01-07-15-20.gh-issue-155525.A7kP2m.rst new file mode 100644 index 00000000000000..9da3c38ce4691b --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-09-01-07-15-20.gh-issue-155525.A7kP2m.rst @@ -0,0 +1,2 @@ +Fix quadratic-time tokenization of modules containing many f-strings or +t-strings. _______________________________________________ Python-checkins mailing list -- [email protected] To unsubscribe send an email to [email protected] https://mail.python.org/mailman3//lists/python-checkins.python.org Member address: [email protected]
