Skip to content

Commit

Permalink
bpo-1198569: Allow string.Template braced pattern to be different (py…
Browse files Browse the repository at this point in the history
…thon#3288)

* bpo-1198569: Allow the braced pattern to be different

``string.Template`` subclasses can optionally define ``braceidpattern`` if
they want to specify different placeholder patterns inside and outside the
braces.  If None (the default) it falls back to ``idpattern``.
  • Loading branch information
warsaw authored Sep 4, 2017
1 parent f9f1734 commit ba42796
Show file tree
Hide file tree
Showing 4 changed files with 45 additions and 4 deletions.
18 changes: 15 additions & 3 deletions Doc/library/string.rst
Original file line number Diff line number Diff line change
Expand Up @@ -754,9 +754,21 @@ attributes:
be set in the subclass's class namespace).

* *idpattern* -- This is the regular expression describing the pattern for
non-braced placeholders (the braces will be added automatically as
appropriate). The default value is the regular expression
``[_a-z][_a-z0-9]*``.
non-braced placeholders. The default value is the regular expression
``[_a-z][_a-z0-9]*``. If this is given and *braceidpattern* is ``None``
this pattern will also apply to braced placeholders.

.. versionchanged:: 3.7
*braceidpattern* can be used to define separate patterns used inside and
outside the braces.

* *braceidpattern* -- This is like *idpattern* but describes the pattern for
braced placeholders. Defaults to ``None`` which means to fall back to
*idpattern* (i.e. the same pattern is used both inside and outside braces).
If given, this allows you to define different patterns for braced and
unbraced placeholders.

.. versionadded:: 3.7

* *flags* -- The regular expression flags that will be applied when compiling
the regular expression used for recognizing substitutions. The default value
Expand Down
4 changes: 3 additions & 1 deletion Lib/string.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ class _TemplateMetaclass(type):
%(delim)s(?:
(?P<escaped>%(delim)s) | # Escape sequence of two delimiters
(?P<named>%(id)s) | # delimiter and a Python identifier
{(?P<braced>%(id)s)} | # delimiter and a braced identifier
{(?P<braced>%(bid)s)} | # delimiter and a braced identifier
(?P<invalid>) # Other ill-formed delimiter exprs
)
"""
Expand All @@ -70,6 +70,7 @@ def __init__(cls, name, bases, dct):
pattern = _TemplateMetaclass.pattern % {
'delim' : _re.escape(cls.delimiter),
'id' : cls.idpattern,
'bid' : cls.braceidpattern or cls.idpattern,
}
cls.pattern = _re.compile(pattern, cls.flags | _re.VERBOSE)

Expand All @@ -79,6 +80,7 @@ class Template(metaclass=_TemplateMetaclass):

delimiter = '$'
idpattern = r'[_a-z][_a-z0-9]*'
braceidpattern = None
flags = _re.IGNORECASE

def __init__(self, template):
Expand Down
24 changes: 24 additions & 0 deletions Lib/test/test_string.py
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,30 @@ class PathPattern(Template):
s = PathPattern('$bag.foo.who likes to eat a bag of $bag.what')
self.assertEqual(s.substitute(m), 'tim likes to eat a bag of ham')

def test_idpattern_override_inside_outside(self):
# bpo-1198569: Allow the regexp inside and outside braces to be
# different when deriving from Template.
class MyPattern(Template):
idpattern = r'[a-z]+'
braceidpattern = r'[A-Z]+'
flags = 0
m = dict(foo='foo', BAR='BAR')
s = MyPattern('$foo ${BAR}')
self.assertEqual(s.substitute(m), 'foo BAR')

def test_idpattern_override_inside_outside_invalid_unbraced(self):
# bpo-1198569: Allow the regexp inside and outside braces to be
# different when deriving from Template.
class MyPattern(Template):
idpattern = r'[a-z]+'
braceidpattern = r'[A-Z]+'
flags = 0
m = dict(foo='foo', BAR='BAR')
s = MyPattern('$FOO')
self.assertRaises(ValueError, s.substitute, m)
s = MyPattern('${bar}')
self.assertRaises(ValueError, s.substitute, m)

def test_pattern_override(self):
class MyPattern(Template):
pattern = r"""
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
``string.Template`` subclasses can optionally define ``braceidpattern`` if
they want to specify different placeholder patterns inside and outside the
braces. If None (the default) it falls back to ``idpattern``.

0 comments on commit ba42796

Please sign in to comment.