Batu Lab NotesPractical developer guides

Fail on missing string.Template placeholders before shipping a report

By Batu · English technical notes

Also published in our primary archive.

To fail on missing string.Template placeholders before shipping a report, render with Template.substitute() rather than safe_substitute(). substitute() raises KeyError when note is absent, allowing the caller to stop the report path. In contrast, safe_substitute() leaves ${note} in the output, which can silently ship an unresolved marker.

The template also demonstrates two dollar-sign details. $$ produces one literal dollar, so $$${amount} renders as $12. The supplied note contains $later, but it is inserted as ordinary text; Template does not evaluate replacement text as another template pass. The assertion confirms that observed single-pass result. This is useful for describing Template behavior, but it is not HTML escaping, SQL parameterization, or general-purpose sanitization.

The example catches only the expected missing-key case and records the missing field name. In an application, decide whether the exception should reach the caller, be converted to a validation result, or be logged without exposing report data. Also validate the values’ meaning separately; strict placeholder completeness only says that a named mapping entry existed.

Python documents the different missing-placeholder behavior of substitute() and safe_substitute(), as well as $$ escaping. These APIs are longstanding in Python 3 and do not require a newer version.

AI assistance disclosure: this article was prepared with AI assistance and checked against the shown synthetic fixture.

Source: Python string.Template.

from string import Template

template = Template("Cost: $$${amount}; note: ${note}")
complete = {"amount": "12", "note": "Use $later literally"}
rendered = template.substitute(complete)

try:
    template.substitute({"amount": "12"})
except KeyError as error:
    missing_field = error.args[0]
else:
    raise AssertionError("the incomplete mapping should fail")

safe_rendered = template.safe_substitute({"amount": "12"})

assert rendered == "Cost: $12; note: Use $later literally"
assert missing_field == "note"
assert safe_rendered == "Cost: $12; note: ${note}"

print("complete:", rendered)
print("substitute missing:", missing_field)
print("safe missing:", safe_rendered)
complete: Cost: $12; note: Use $later literally
substitute missing: note
safe missing: Cost: $12; note: ${note}