導入
文字列モジュールには Template クラスがあり、マッピング オブジェクトを使用して文字列内の置換を行うことができます。次に例を示します。
>>> string.Template('var is $var').substitute({'var': 1})
'var is 1'
マッピングに存在しない要素を置換しようとした場合、substituteメソッドはKeyError例外を発生させる場合があります。たとえば、
>>> string.Template('var is $var and foo is $foo').substitute({'var': 1})
KeyError: 'foo'
または、テンプレート文字列が無効な場合、たとえば$
文字の後にスペースが含まれている場合、ValueError が発生する可能性があります。
>>> string.Template('$ var is $var').substitute({'var': 1})
ValueError: Invalid placeholder in string: line 1, col 1
問題
テンプレート文字列とマッピングが与えられた場合、テンプレート内のすべてのプレースホルダーが置換されるかどうかを判断します。このために、置換を実行して KeyError 例外をキャッチします。
def check_substitution(template, mapping):
try:
string.Template(template).substitute(mapping)
except KeyError:
return False
except ValueError:
pass
return True
しかし、これは機能しません。テンプレートが無効で ValueError が発生した場合、後続の KeyError はキャッチされないためです。
>>> check_substitution('var is $var and foo is $foo', {'var': 1})
False
>>> check_substitution('$ var is $var and foo is $foo', {'var': 1})
True
しかし、私気にしないValueErrors について。では、この問題に対する正しいアプローチは何でしょうか?
ベストアンサー1
ドキュメントによると必要な名前付きグループがすべて含まれている限り、パターンを置き換えることができます。
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
テスト
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)
これは、区切り文字 ( $
) のすべての無効な出現に対して機能します。
これらの例は、無効なパターンを無視するとテンプレート内の単純なタイプミスが隠されるため、適切な API ではないことを示しています。