Upgrade string from CPython 3.14.2

This commit is contained in:
CPython Devleopers
2026-01-16 00:12:45 +09:00
committed by Jeong, YunWon
parent 346481d95e
commit 076d692b42
2 changed files with 71 additions and 22 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
@@ -212,19 +227,20 @@ class Formatter:
# this is some markup, find the object and do
# the formatting
# handle arg indexing when empty field_names are given.
if field_name == '':
# handle arg indexing when empty field first parts are given.
field_first, _ = _string.formatter_field_name_split(field_name)
if field_first == '':
if auto_arg_index is False:
raise ValueError('cannot switch from manual field '
'specification to automatic field '
'numbering')
field_name = str(auto_arg_index)
field_name = str(auto_arg_index) + field_name
auto_arg_index += 1
elif field_name.isdigit():
elif isinstance(field_first, int):
if auto_arg_index:
raise ValueError('cannot switch from manual field '
'specification to automatic field '
'numbering')
raise ValueError('cannot switch from automatic field '
'numbering to manual field '
'specification')
# disable auto arg incrementing, if it gets
# used later on, then an exception will be raised
auto_arg_index = False

33
Lib/string/templatelib.py vendored Normal file
View File

@@ -0,0 +1,33 @@
"""Support for template string literals (t-strings)."""
t = t"{0}"
Template = type(t)
Interpolation = type(t.interpolations[0])
del t
def convert(obj, /, conversion):
"""Convert *obj* using formatted string literal semantics."""
if conversion is None:
return obj
if conversion == 'r':
return repr(obj)
if conversion == 's':
return str(obj)
if conversion == 'a':
return ascii(obj)
raise ValueError(f'invalid conversion specifier: {conversion}')
def _template_unpickle(*args):
import itertools
if len(args) != 2:
raise ValueError('Template expects tuple of length 2 to unpickle')
strings, interpolations = args
parts = []
for string, interpolation in itertools.zip_longest(strings, interpolations):
if string is not None:
parts.append(string)
if interpolation is not None:
parts.append(interpolation)
return Template(*parts)