Krikri::HostVarsVarsDict
The per-host dicts inside hostvars in a Crinja render. Real Ansible
wraps each host's vars in its own HostVarsVars object, whose attribute
lookup RAISES on a miss (object of type 'HostVarsVars' has no attribute 'ansible_enp0s8' - a typo'd/computed interface fact name
fails the task rather than rendering an empty value in its place;
found via mrlesmithjr.ansible_consul_client's
hostvars[inventory_hostname]['ansible_' + consul_client_bind_interface]
with a bind interface that doesn't exist on the real host). Crinja's
plain dict resolution (Resolver#resolve_with_hash_accessor) falls back
to a lenient Undefined on the same miss, and that path can't be made
blanket-strict (it is also the fallback for method-call dispatch and
for this engine's own fact-coverage gaps - see
Krikri::StrictTemplating's own comment). So the strictness rides on
the hostvars VALUE ITSELF: the dict only reaches a render through the
hostvars conversion (CrinjaRenderer.convert_hostvars), and its []?
raises on a miss exactly when strict templating is enabled for the
rendering fiber - every non-hostvars dict stays lenient, and outside
strict mode (where the lenient {{ ... }} hand-rolled evaluator and
when: conditions read these values) the behavior is unchanged.
A Crinja::Object wrapper rather than a Hash subclass: Crinja::Value.new
NORMALIZES any Hash into a plain Crinja::Dictionary (Crinja.value's
Hash case), which would silently strip a subclass - only Crinja::Object
instances survive as their own raw object.
Constructors
Instance methods
Both the attribute form (hostvars[h].ansible_host) and the
subscript form (hostvars[h]['ansible_host']) funnel through
resolve_getattr -> crinja_attribute for a Crinja::Object.
A strict miss does NOT raise at lookup time: it returns an
Undefined that only raises when actually forced (printed or
compared). Raising at lookup time made is defined itself hard-
fail the render (found via mullholland.motd round72000:
{% if hostvars[inventory_hostname]['ansible_' ~ int] is defined %}
with a genuinely-absent computed key - real Ansible takes the
false branch, ok=3 changed=2), defeating the one construct that
is supposed to never raise for a missing attribute. Deferring to
force time keeps a plain {{ hostvars[h].typo }} failing the task
with the exact message real Ansible's own HostVarsVars wrapper
raises, while is defined, | default(...), and {% if %} all
see a genuine Undefined.
The deferred raise is a plain RuntimeError, NOT an UndefinedError: Crinja's own evaluator rescues UndefinedError around attribute resolution and re-raises a generic "hostvars[node1][x] is undefined" that DISCARDS the cause's message - real Ansible's failure text for the plain-print case is the wrapper's own "object of type 'HostVarsVars' has no attribute ..." (an AttributeError surfacing verbatim), so the detail has to survive. A RuntimeError is not swallowed anywhere in the render path and surfaces the message as the task failure.
dict-protocol compatibility for the operations templates actually perform on a host's vars (size/iteration/key listing); the plain Hash(String, Crinja::Value) these delegate to is also what every other code path sees, since @entries stays directly readable.
dict-protocol compatibility for the operations templates actually perform on a host's vars (size/iteration/key listing); the plain Hash(String, Crinja::Value) these delegate to is also what every other code path sees, since @entries stays directly readable.
dict-protocol compatibility for the operations templates actually perform on a host's vars (size/iteration/key listing); the plain Hash(String, Crinja::Value) these delegate to is also what every other code path sees, since @entries stays directly readable.
dict-protocol compatibility for the operations templates actually perform on a host's vars (size/iteration/key listing); the plain Hash(String, Crinja::Value) these delegate to is also what every other code path sees, since @entries stays directly readable.
dict-protocol compatibility for the operations templates actually perform on a host's vars (size/iteration/key listing); the plain Hash(String, Crinja::Value) these delegate to is also what every other code path sees, since @entries stays directly readable.
dict-protocol compatibility for the operations templates actually perform on a host's vars (size/iteration/key listing); the plain Hash(String, Crinja::Value) these delegate to is also what every other code path sees, since @entries stays directly readable.