Introduction
The string module has a Template class, that lets you make substitutions in a string using a mapping object, for instance:
>>> string.Template('var is $var').substitute({'var': 1})
'var is 1'
The substitute method may raise a KeyError exception, if an attempt is made to substitute an element that is missing from the mapping, for instance
>>> string.Template('var is $var and foo is $foo').substitute({'var': 1})
KeyError: 'foo'
or may raise a ValueError, if the template string is invalid, e.g. it contains a $ character followed by a space:
>>> string.Template('$ var is $var').substitute({'var': 1})
ValueError: Invalid placeholder in string: line 1, col 1
The Problem
Given a template string and a mapping, I want to determine whether all place-holders in the template would be substituted. For this, I would try to make the substitution and catch any KeyError exception:
def check_substitution(template, mapping):
try:
string.Template(template).substitute(mapping)
except KeyError:
return False
except ValueError:
pass
return True
But this doesn’t work, because if the template is invalid and a ValueError is raised, subsequent KeyErrors aren’t caught:
>>> check_substitution('var is $var and foo is $foo', {'var': 1})
False
>>> check_substitution('$ var is $var and foo is $foo', {'var': 1})
True
but I don’t care about ValueErrors. So, what would be the right approach to this problem?
The docs say that you can replace the pattern as long as it contains all necessary named groups:
Tests
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.