How to escape $ on Python string Template class?
The docs say that you can replace the pattern as long as it contains all necessary named groups:
import re
from string import Template
class TemplateIgnoreInvalid(Template):
# override pattern to make sure `invalid` never matches
pattern = r"""
%(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<invalid>^$) # never matches (the regex is not multilined)
)
""" % dict(delim=re.escape(Template.delimiter), id=Template.idpattern)
def check_substitution(template, **mapping):
try:
TemplateIgnoreInvalid(template).substitute(mapping)
except KeyError:
return False
else:
return True
Tests
f = check_substitution
assert f('var is $var', var=1)
assert f('$ var is $var', var=1)
assert f('var is $var and foo is $foo', var=1, foo=2)
assert not f('var is $var and foo is $foo', var=1)
assert f('$ var is $var and foo is $foo', var=1, foo=2)
assert not f('$ var is $var and foo is $foo', var=1)
# support all invalid patterns
assert f('var is $var and foo is ${foo', var=1)
assert f('var is $var and foo is ${foo', var=1, foo=2) #NOTE: problematic API
assert f('var is $var and foo is ${foo and ${baz}', var=1, baz=3)
assert not f('var is $var and foo is ${foo and ${baz}', var=1)
It works for all invalid occurences of the delimiter ($
).
The examples show that ignoring invalid patterns conceals simple typos in the template so it is not a good API.
This is a Quick Fix (Using recursion):
def check_substitution(tem, m):
try:
string.Template(tem).substitute(m)
except KeyError:
return False
except ValueError:
return check_substitution(tem.replace('$ ', '$'), m) #strip spaces after $
return True
I Know its take a longer time if there is more than One Space between $
and var
, so you may improve it by using Regular Expression.
EDIT
escaping $
into $$
makes more sense [ Thanks @Pedro ] so you can catch ValueError
by this statement:
return check_substitution(tem.replace('$ ', '$$ '), m) #escaping $ by $$