https://github.com/python/cpython/commit/ee3657209b1464d66c89640e4db0dccdeec5b45c
commit: ee3657209b1464d66c89640e4db0dccdeec5b45c
branch: main
author: Adam Turner <9087854+aa-tur...@users.noreply.github.com>
committer: AA-Turner <9087854+aa-tur...@users.noreply.github.com>
date: 2025-04-08T10:05:48Z
summary:

gh-118761: Optimise import time for ``string`` (#132037)

Co-authored-by: Serhiy Storchaka <storch...@gmail.com>

files:
A Misc/NEWS.d/next/Library/2025-04-03-01-35-02.gh-issue-118761.VQcj70.rst
M Lib/string.py

diff --git a/Lib/string.py b/Lib/string.py
index c4f05c7223ce8a..eab5067c9b133e 100644
--- a/Lib/string.py
+++ b/Lib/string.py
@@ -49,11 +49,18 @@ def capwords(s, sep=None):
 
 
 ####################################################################
-import re as _re
-from collections import ChainMap as _ChainMap
-
 _sentinel_dict = {}
 
+
+class _TemplatePattern:
+    # This descriptor is overwritten in ``Template._compile_pattern()``.
+    def __get__(self, instance, cls=None):
+        if cls is None:
+            return self
+        return cls._compile_pattern()
+_TemplatePattern = _TemplatePattern()
+
+
 class Template:
     """A string class for supporting $-substitutions."""
 
@@ -64,14 +71,21 @@ class Template:
     # See https://bugs.python.org/issue31672
     idpattern = r'(?a:[_a-z][_a-z0-9]*)'
     braceidpattern = None
-    flags = _re.IGNORECASE
+    flags = None  # default: re.IGNORECASE
+
+    pattern = _TemplatePattern  # use a descriptor to compile the pattern
 
     def __init_subclass__(cls):
         super().__init_subclass__()
-        if 'pattern' in cls.__dict__:
-            pattern = cls.pattern
-        else:
-            delim = _re.escape(cls.delimiter)
+        cls._compile_pattern()
+
+    @classmethod
+    def _compile_pattern(cls):
+        import re  # deferred import, for performance
+
+        pattern = cls.__dict__.get('pattern', _TemplatePattern)
+        if pattern is _TemplatePattern:
+            delim = re.escape(cls.delimiter)
             id = cls.idpattern
             bid = cls.braceidpattern or cls.idpattern
             pattern = fr"""
@@ -82,7 +96,10 @@ def __init_subclass__(cls):
               (?P<invalid>)             # Other ill-formed delimiter exprs
             )
             """
-        cls.pattern = _re.compile(pattern, cls.flags | _re.VERBOSE)
+        if cls.flags is None:
+            cls.flags = re.IGNORECASE
+        pat = cls.pattern = re.compile(pattern, cls.flags | re.VERBOSE)
+        return pat
 
     def __init__(self, template):
         self.template = template
@@ -105,7 +122,8 @@ def substitute(self, mapping=_sentinel_dict, /, **kws):
         if mapping is _sentinel_dict:
             mapping = kws
         elif kws:
-            mapping = _ChainMap(kws, mapping)
+            from collections import ChainMap
+            mapping = ChainMap(kws, mapping)
         # Helper function for .sub()
         def convert(mo):
             # Check the most common path first.
@@ -124,7 +142,8 @@ def safe_substitute(self, mapping=_sentinel_dict, /, **kws):
         if mapping is _sentinel_dict:
             mapping = kws
         elif kws:
-            mapping = _ChainMap(kws, mapping)
+            from collections import ChainMap
+            mapping = ChainMap(kws, mapping)
         # Helper function for .sub()
         def convert(mo):
             named = mo.group('named') or mo.group('braced')
@@ -170,10 +189,6 @@ def get_identifiers(self):
                     self.pattern)
         return ids
 
-# Initialize Template.pattern.  __init_subclass__() is automatically called
-# only for subclasses, not for the Template class itself.
-Template.__init_subclass__()
-
 
 ########################################################################
 # the Formatter class
diff --git 
a/Misc/NEWS.d/next/Library/2025-04-03-01-35-02.gh-issue-118761.VQcj70.rst 
b/Misc/NEWS.d/next/Library/2025-04-03-01-35-02.gh-issue-118761.VQcj70.rst
new file mode 100644
index 00000000000000..257ad7ece7d18a
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2025-04-03-01-35-02.gh-issue-118761.VQcj70.rst
@@ -0,0 +1,2 @@
+Improve import times by up to 27x for the :mod:`string` module.
+Patch by Adam Turner.

_______________________________________________
Python-checkins mailing list -- python-checkins@python.org
To unsubscribe send an email to python-checkins-le...@python.org
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: arch...@mail-archive.com

Reply via email to