Silently empty output from a typo is a classic template bug, especially in config generation.
When a template references something that doesn't exist, Jinja creates an undefined object. The class used is set on the Environment with undefined=:
Undefined(default): prints as empty, is falsy and iterates as empty, but raisesUndefinedErroron attribute access ({{ db.host }}with nodb) or arithmeticChainableUndefined: also allowsa.b.cchains on undefined values, returning undefined instead of raising, handy with deeply optional dataDebugUndefined: prints the expression back, such as{{ user.name }}, so missing values stand out in outputStrictUndefined: raisesUndefinedErroron almost any use, including printing, iterating and truth tests
StrictUndefined is the safest choice for generated configuration, emails and anything where a blank is a bug. Within it, you can still handle optional values explicitly with {% if x is defined %} or x|default("..."), which work because tests and default don't trigger the error.
To find out which variables a template expects, parse it and ask:
from jinja2 import meta
meta.find_undeclared_variables(env.parse(source))That is useful for validating that a caller provides everything, or for documenting template inputs.
Error messages from Jinja include the template name and line number in the traceback, because compiled templates keep a mapping back to template lines.
Example
from jinja2 import ChainableUndefined, DebugUndefined, Environment, StrictUndefined, meta
from jinja2.exceptions import UndefinedError
src = "host={{ db_host }} port={{ port|default(5432) }} {% if debug is defined %}debug{% endif %}"
print(Environment().from_string(src).render()) # host= port=5432
print(Environment(undefined=DebugUndefined).from_string(src).render())
# host={{ db_host }} port=5432
try:
Environment(undefined=StrictUndefined).from_string(src).render()
except UndefinedError as e:
print("error:", e) # error: 'db_host' is undefined
print(Environment(undefined=StrictUndefined).from_string(src).render(db_host="h"))
# host=h port=5432
print(Environment(undefined=ChainableUndefined).from_string("[{{ a.b.c }}]").render()) # []
env = Environment()
print(sorted(meta.find_undeclared_variables(env.parse(src)))) # ['db_host', 'debug', 'port']Remember
- The undefined= option decides what missing values do
- StrictUndefined turns typos into errors; default and is defined still work
- meta.find_undeclared_variables lists a template's inputs
Watch out for
- Generating config files with the default Undefined, so a typo produces an empty value.