####################################################################
-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."""
# 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"""
(?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
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.
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')
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