module

Krikri::StrictTemplating

Opt-in strict-undefined mode for Crinja renders.

Real Ansible's template: module runs Jinja2 with StrictUndefined: {{ some_undefined_var }} in a .j2 file raises 'some_undefined_var' is undefined and fails the task. Crinja's default is lenient - a miss resolves to the shared, NAMELESS Value::UNDEFINED singleton, which stringifies to "" - so a role whose required credential was never provided rendered a config with an empty value in place of it and reported changed, deploying a broken/incomplete file while reporting success (found via alannix_lw.lacework_agent_ansible_role's config.json.j2: "AccessToken" : "{{ lacework_accessToken }}").

Crinja already ships Crinja::StrictUndefined (raises on to_s/ comparison) but nothing lets a caller opt an Environment into it - Crinja::Config has no such setting, and the three separate sites that build an undefined (Context#undefined, Environment#undefined, Resolver.resolve_with_hash_accessor) each construct a plain one. Rather than fork the vendored shard (whose lib/ isn't even carried in this repo), this reopens the ONE site that matters for the documented failure - Resolver#resolve, the bare-name lookup - and returns a named StrictUndefined from it while the flag is on. Everything downstream then behaves as real Jinja2 does: is defined and default(...) still work (both only ever test undefined?, never stringify), while any attempt to actually PRINT the value raises Crinja::UndefinedError, which TemplateActionPlugin surfaces as a failed task.

Deliberately scoped to bare-name misses only. A DOTTED attribute miss on a defined object ({{ foo.bar }}) is strict in real Jinja2 too, but goes through Resolver.resolve_with_hash_accessor, which is also the fallback path for method-call dispatch and for this engine's own fact-coverage gaps - making it strict risks false positives across the whole existing template corpus, which is worse than the status quo it would be fixing. Same reasoning for {% if undefined %}: Crinja's Value#truthy? short-circuits on undefined? before any comparison can raise, and leaving it lenient keeps this change to exactly the shape that was found to bite.

Class methods

enabled?
Source
strict

Runs block with strict-undefined resolution enabled for the current fiber. Re-entrant: a nested call (a {% include %}'d template rendering through the same environment) leaves the flag set for the outer one.

Source