@@ -49,11 +49,18 @@ def capwords(s, sep=None):
4949
5050
5151####################################################################
52- import re as _re
53- from collections import ChainMap as _ChainMap
54-
5552_sentinel_dict = {}
5653
54+
55+ class _TemplatePattern :
56+ # This descriptor is overwritten in ``Template._compile_pattern()``.
57+ def __get__ (self , instance , cls = None ):
58+ if cls is None :
59+ return self
60+ return cls ._compile_pattern ()
61+ _TemplatePattern = _TemplatePattern ()
62+
63+
5764class Template :
5865 """A string class for supporting $-substitutions."""
5966
@@ -64,14 +71,21 @@ class Template:
6471 # See https://bugs.python.org/issue31672
6572 idpattern = r'(?a:[_a-z][_a-z0-9]*)'
6673 braceidpattern = None
67- flags = _re .IGNORECASE
74+ flags = None # default: re.IGNORECASE
75+
76+ pattern = _TemplatePattern # use a descriptor to compile the pattern
6877
6978 def __init_subclass__ (cls ):
7079 super ().__init_subclass__ ()
71- if 'pattern' in cls .__dict__ :
72- pattern = cls .pattern
73- else :
74- delim = _re .escape (cls .delimiter )
80+ cls ._compile_pattern ()
81+
82+ @classmethod
83+ def _compile_pattern (cls ):
84+ import re # deferred import, for performance
85+
86+ pattern = cls .__dict__ .get ('pattern' , _TemplatePattern )
87+ if pattern is _TemplatePattern :
88+ delim = re .escape (cls .delimiter )
7589 id = cls .idpattern
7690 bid = cls .braceidpattern or cls .idpattern
7791 pattern = fr"""
@@ -82,7 +96,10 @@ def __init_subclass__(cls):
8296 (?P<invalid>) # Other ill-formed delimiter exprs
8397 )
8498 """
85- cls .pattern = _re .compile (pattern , cls .flags | _re .VERBOSE )
99+ if cls .flags is None :
100+ cls .flags = re .IGNORECASE
101+ pat = cls .pattern = re .compile (pattern , cls .flags | re .VERBOSE )
102+ return pat
86103
87104 def __init__ (self , template ):
88105 self .template = template
@@ -105,7 +122,8 @@ def substitute(self, mapping=_sentinel_dict, /, **kws):
105122 if mapping is _sentinel_dict :
106123 mapping = kws
107124 elif kws :
108- mapping = _ChainMap (kws , mapping )
125+ from collections import ChainMap
126+ mapping = ChainMap (kws , mapping )
109127 # Helper function for .sub()
110128 def convert (mo ):
111129 # Check the most common path first.
@@ -124,7 +142,8 @@ def safe_substitute(self, mapping=_sentinel_dict, /, **kws):
124142 if mapping is _sentinel_dict :
125143 mapping = kws
126144 elif kws :
127- mapping = _ChainMap (kws , mapping )
145+ from collections import ChainMap
146+ mapping = ChainMap (kws , mapping )
128147 # Helper function for .sub()
129148 def convert (mo ):
130149 named = mo .group ('named' ) or mo .group ('braced' )
@@ -170,10 +189,6 @@ def get_identifiers(self):
170189 self .pattern )
171190 return ids
172191
173- # Initialize Template.pattern. __init_subclass__() is automatically called
174- # only for subclasses, not for the Template class itself.
175- Template .__init_subclass__ ()
176-
177192
178193########################################################################
179194# the Formatter class
@@ -212,19 +227,20 @@ def _vformat(self, format_string, args, kwargs, used_args, recursion_depth,
212227 # this is some markup, find the object and do
213228 # the formatting
214229
215- # handle arg indexing when empty field_names are given.
216- if field_name == '' :
230+ # handle arg indexing when empty field first parts are given.
231+ field_first , _ = _string .formatter_field_name_split (field_name )
232+ if field_first == '' :
217233 if auto_arg_index is False :
218234 raise ValueError ('cannot switch from manual field '
219235 'specification to automatic field '
220236 'numbering' )
221- field_name = str (auto_arg_index )
237+ field_name = str (auto_arg_index ) + field_name
222238 auto_arg_index += 1
223- elif field_name . isdigit ( ):
239+ elif isinstance ( field_first , int ):
224240 if auto_arg_index :
225- raise ValueError ('cannot switch from manual field '
226- 'specification to automatic field '
227- 'numbering ' )
241+ raise ValueError ('cannot switch from automatic field '
242+ 'numbering to manual field '
243+ 'specification ' )
228244 # disable auto arg incrementing, if it gets
229245 # used later on, then an exception will be raised
230246 auto_arg_index = False
0 commit comments