Files
cpython/Lib/test/test_free_threading/test_string_template_race.py
T
45729033bf gh-153056: Fix a data race compiling the string.Template pattern in free-threading builds (#153057)
* gh-153056: Fix a data race compiling the string.Template pattern in free-threading builds

Template compiles its substitution pattern lazily and caches it on the class.  On the free-threaded build two concurrent first uses could race: a thread that observed the pattern another thread had just compiled would try to recompile it, and re.compile() rejects flags on an already-compiled pattern, raising a spurious ValueError.  Return the already-compiled pattern instead.

As a side effect, a subclass that supplies an already-compiled pattern now works too; previously it raised the same ValueError at class definition.

* Trim test comments and NEWS wording

* Document that the pattern attribute accepts a string or a compiled regex

* Comment the three states of pattern and note the documented-behavior fix in NEWS

* Update Doc/library/string.rst

---------

Co-authored-by: Barry Warsaw <[email protected]>
2026-07-08 00:40:54 +00:00

36 lines
1.1 KiB
Python

import string
import unittest
from string import Template
from test.support import threading_helper
@threading_helper.requires_working_threading()
class TestTemplateCompileRace(unittest.TestCase):
def test_concurrent_first_use(self):
# Racing the lazy pattern compile must not raise a spurious ValueError
# from recompiling an already-compiled pattern. A throwaway subclass,
# re-armed to the sentinel each round, keeps string.Template unmutated
# (subclasses precompile eagerly in __init_subclass__).
uncompiled = string._TemplatePattern
errors = []
def use_template(cls):
try:
cls("$x and ${y}").substitute(x=1, y=2)
except Exception as e:
errors.append(e)
for _ in range(20):
class T(Template):
pass
T.pattern = uncompiled
T.flags = None
threading_helper.run_concurrently(use_template, nthreads=10, args=(T,))
self.assertEqual(errors, [], msg=f"unexpected errors: {errors}")
if __name__ == "__main__":
unittest.main()