Template.substitute / safe_substitute

Same substitution, two error policies. substitute is strict: every placeholder needs a value and every $ must be valid. safe_substitute never raises for those — it returns the unknown placeholders and stray $ unchanged.

Template methodPython 2.4+Live demo
Common call
Template('$a $b').safe_substitute(a=1)
Returns
'1 $b'
Replaces
try/except KeyError around formatting
Watch out
safe_substitute hides typos: $nmae simply stays in the output
Template.substitute(mappingmapping — Values by placeholder name (any object with __getitem__ that raises KeyError). Positional-only.type: mapping · default: {}={}, /, **kwds) · Template.safe_substitute(mapping={}, /, **kwds)
→ str

Demo

Live evaluation
One template, one value for name — both methods side by side (substitute errors are caught and shown).
Try:
Inputs
textstrtemplate text
namestrvalue for $name
Code
from string import Template
t = Template('Hello $name!')
values = {'name': 'Ada'}
try:
    strict = t.substitute(values)
except (KeyError, ValueError) as e:
    strict = f'{type(e).__name__}: {e}'
(t.safe_substitute(values), strict)
Result
('Hello Ada!', 'Hello Ada!')

The ValueError message gives the line and the column, both counted from 1, of the $ that does not start a placeholder — in the stray-$ case that is line 2, col 9. '${ name }' is not a placeholder (no spaces allowed inside the braces), so it is invalid too. $$ always becomes a single $ and is never read as the start of a name. Values that are not used are simply ignored by both methods.

Parameters

NameTypeRequiredDescription
mappingmappingno ({})Values by placeholder name (any object with __getitem__ that raises KeyError). Positional-only.
**kwdsanynoValues as keyword arguments. They take precedence over mapping when both have the same name.

Return value

str — The template text with every placeholder replaced by str(value) and every $$ by $.

Common patterns

Strict where you control the template
Fail loudly on a missing value instead of sending "Dear $name".
from string import Template
body = Template(EMAIL_BODY).substitute(name=user.name, link=reset_url)
Two-stage filling
safe_substitute fills what is known now and keeps the rest for later.
from string import Template
stage1 = Template(text).safe_substitute(site='example.org')
final = Template(stage1).substitute(user='ada')
Report all missing names at once
Compare get_identifiers() with your values before substituting (3.11+).
from string import Template
t = Template(text)
missing = [n for n in t.get_identifiers() if n not in values]
if missing:
    raise KeyError(f'missing values: {missing}')
t.substitute(values)

Examples

1. safe_substitute keeps unknowns
from string import Template Template('$who likes $what').safe_substitute(who='tim')
Returns
'tim likes $what'
2. substitute raises KeyError
from string import Template Template('$who likes $what').substitute(who='tim')
Returns
KeyError: 'what'
3. Stray $: ValueError with position
from string import Template Template('ab\ncd $').substitute()
Returns
ValueError: Invalid placeholder in string: line 2, col 4
4. Stray $ kept by safe_substitute
from string import Template Template('Cost: $5').safe_substitute()
Returns
'Cost: $5'
5. Keywords override the mapping
from string import Template Template('$x').substitute({'x': 1}, x=2)
Returns
'2'
6. Any mapping works
from string import Template from collections import ChainMap Template('$a $b').substitute(ChainMap({'a': 1}, {'a': 9, 'b': 2}))
Returns
'1 2'
7. Names like self and mapping are fine
from string import Template Template('$self $mapping').substitute(self=1, mapping=2)
Returns
'1 2'

Pitfalls

1. safe_substitute hiding typos
A misspelt placeholder is not an error — it ends up in the output.
safe_substitute
from string import Template
Template('Dear $nmae').safe_substitute(name='Ada')
'Dear $nmae'
substitute
from string import Template
Template('Dear $nmae').substitute(name='Ada')
KeyError: 'nmae'
2. Catching only KeyError
substitute raises ValueError, not KeyError, for a malformed $. Catch both, or check is_valid() first.
except KeyError
from string import Template
try:
    out = Template('100$').substitute()
except KeyError:
    out = 'missing value'
out
ValueError: Invalid placeholder in string: line 1, col 4
except (KeyError, ValueError)
from string import Template
try:
    out = Template('100$').substitute()
except (KeyError, ValueError) as e:
    out = f'bad template: {e}'
out
'bad template: Invalid placeholder in string: line 1, col 4'

When to use

Use it
  • substitute — templates you control, where a missing value is a bug
  • safe_substitute — user-edited templates, partial filling, previews
Reach for something else
  • Production output from user templates with safe_substitute alone → validate with is_valid()/get_identifiers() first

Notes

CPython impl
Both run self.pattern.sub(convert, self.template); convert returns str(mapping[named]) for a placeholder, the delimiter for $$, and for an invalid $ either raises (substitute) or returns the match unchanged (safe_substitute)
Mapping
With both a mapping and keywords, a ChainMap(kwds, mapping) is used — keywords first
Position
The ValueError column is the 1-based column of the offending $ on its line; lines are split with str.splitlines() rules

FAQ

substitute raises KeyError when a placeholder has no value and ValueError when a $ is not followed by a valid name; safe_substitute does neither and leaves those parts of the text unchanged. Both replace $$ with $.