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]

Reply via email to