https://github.com/python/cpython/commit/7994b41154279a2c7d22456a5c200e1fa662b931
commit: 7994b41154279a2c7d22456a5c200e1fa662b931
branch: 3.13
author: Barry Warsaw <[email protected]>
committer: warsaw <[email protected]>
date: 2026-07-09T19:40:59Z
summary:
[3.13] gh-153056: Backport the relevant bits of (#153057) (#153369)
gh-153056: Backport the relevant bits of (#153057)
The data race for compiling the string.Template pattern in free-threading
builds is not relevant for
3.13, due to the older string.Template and string.py module implementation.
However, the secondary
bug identified by that isue is still relevant here, and fixed in this branch:
As a side effect, a subclass that supplies an already-compiled pattern now
works too; previously
it raised the same ValueError at class definition.
* 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
---------
(cherry picked from commit 45729033bff28f8abc36c42e802cb2853c205737)
Co-authored-by: tonghuaroot (童话) <[email protected]>
files:
A Misc/NEWS.d/next/Library/2026-07-05-12-00-00.gh-issue-153056.tMpLat.rst
M Doc/library/string.rst
M Lib/string.py
M Lib/test/test_string.py
diff --git a/Doc/library/string.rst b/Doc/library/string.rst
index b7b92ecb14f8c8..153cbce7d83226 100644
--- a/Doc/library/string.rst
+++ b/Doc/library/string.rst
@@ -943,7 +943,8 @@ attributes:
Alternatively, you can provide the entire regular expression pattern by
overriding the class attribute *pattern*. If you do this, the value must be a
-regular expression object with four named capturing groups. The capturing
+regular expression pattern string, or a compiled regular expression
+object, with four named capturing groups. The capturing
groups correspond to the rules given above, along with the invalid placeholder
rule:
diff --git a/Lib/string.py b/Lib/string.py
index 2eab6d4f595c4e..f6babf337e8e65 100644
--- a/Lib/string.py
+++ b/Lib/string.py
@@ -70,6 +70,11 @@ def __init_subclass__(cls):
super().__init_subclass__()
if 'pattern' in cls.__dict__:
pattern = cls.pattern
+ if isinstance(pattern, _re.Pattern):
+ # An already-compiled pattern (which the documentation allows)
+ # is used as-is; re.compile() rejects flags on a compiled
+ # pattern.
+ return
else:
delim = _re.escape(cls.delimiter)
id = cls.idpattern
diff --git a/Lib/test/test_string.py b/Lib/test/test_string.py
index 824b89ad517c12..537587aa9ad66d 100644
--- a/Lib/test/test_string.py
+++ b/Lib/test/test_string.py
@@ -272,6 +272,20 @@ def test_SafeTemplate(self):
eq(s.safe_substitute(dict(who='tim', what='ham', meal='dinner')),
'tim likes ham for dinner')
+ def test_precompiled_pattern(self):
+ # A subclass may supply an already-compiled pattern; it must be reused,
+ # not recompiled (re.compile() rejects flags on a compiled pattern).
+ import re
+ compiled = re.compile(
+ r'\$(?:(?P<escaped>\$)|(?P<named>[a-z]+)|'
+ r'\{(?P<braced>[a-z]+)\}|(?P<invalid>))')
+ class MyTemplate(Template):
+ pattern = compiled
+ self.assertIs(MyTemplate.pattern, compiled)
+ self.assertEqual(
+ MyTemplate('$who likes $what').substitute(who='tim', what='ham'),
+ 'tim likes ham')
+
def test_invalid_placeholders(self):
raises = self.assertRaises
s = Template('$who likes $')
diff --git
a/Misc/NEWS.d/next/Library/2026-07-05-12-00-00.gh-issue-153056.tMpLat.rst
b/Misc/NEWS.d/next/Library/2026-07-05-12-00-00.gh-issue-153056.tMpLat.rst
new file mode 100644
index 00000000000000..483340d3504215
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2026-07-05-12-00-00.gh-issue-153056.tMpLat.rst
@@ -0,0 +1,3 @@
+Fix :class:`string.Template` raising a spurious :exc:`ValueError` when the
+*pattern* attribute is a compiled regular expression object, which the
+documentation allows.
_______________________________________________
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]