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

Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
This commit is contained in:
Adam Turner 2025-04-08 11:05:48 +01:00 committed by GitHub
parent 53908bd790
commit ee3657209b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 32 additions and 15 deletions

View file

@ -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 @@ class Template:
(?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 @@ class Template:
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 @@ class Template:
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 @@ class Template:
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