An application-specific tag for Jinja2 templates within PyYAML.
This can be useful if you want to render only specially tagged nodes in the document, rather than treating the entire YAML string as a template.
-
Add
Jinja2
template constructor for tag"!j2"
import yaml import jinjyaml as jy ctor = jy.Constructor() yaml.add_constructor("!j2", ctor, yaml.SafeLoader)
-
create
YAML
file1.yml
, with such contents:array: !j2 | {% for i in range(n) %} - sub{{i}}: {{loop.index}} {% endfor %}
-
load and render the
YAML
filewith open("1.yml") as fp: data = yaml.load(fp, Loader=yaml.SafeLoader) # or for the short: # data = yaml.safe_load(fp) jy.extract(data, context={"n": 3}, inplace=True) print(data)
We'll get:
{"array": [{"sub0": 1}, {"sub1": 2}, {"sub2": 3}]}
We have such YAML files:
-
sub-1.yml
:"1.1": one "1.2": two
-
sub-2.yml
:"2.1": "2.1.1": three "2.1.2": four
-
main.yml
:foo: !j2 | {% filter indent %} {% include "sub-1.yml" %} {% endfilter %} {% filter indent %} {% include "sub-2.yml" %} {% endfilter %}
execute python code:
from pprint import pprint
import jinja2
import jinjyaml as jy
import yaml
env = jinja2.Environment(loader=jinja2.FileSystemLoader("."))
ctor = jy.Constructor()
yaml.add_constructor("!j2", ctor, yaml.SafeLoader)
with open("main.yml") as fp:
doc = yaml.safe_load(fp)
obj = jy.extract(doc, env)
pprint(obj)
We'll get:
{"foo": {"1.1": "one",
"1.2": "two",
"2.1": {"2.1.1": "three", "2.1.2": "four"}}}
ℹ️ Note:
Jinja2'sinclude
andindent
features do not handle indentation well in languages sensitive to it, such as Python or YAML. Therefore, using these features in complex cases is not recommended.For such scenarios, consider using pyyaml-include. This package provides a PyYAML extension that allows you to include other YAML files while preserving proper indentation. Using this extension can help maintain the integrity of your YAML files more effectively.
-
install pyyaml-include:
pip install pyyaml-include
-
add both pyyaml-include and
jinjyaml
's constructor:import yaml import jinjyaml as jy import pyyaml_include yaml.add_constructor("!j2", jy.Constructor) yaml.add_constructor("!inc", pyyaml_include.Constructor(base_dir="path_to_you_dir"))
-
Assume that we have YAML files same to previous example, the
main.yml
can be modified as below:foo: !j2 | {% for i in range(n) %} - !inc sub-{{loop.index}}.yml {% endfor %}
-
include and load other YAML files:
Assume that we have YAML files same to previous example:
with open("main.yml") as fp: doc = yaml.safe_load(fp) obj = jy.extract(doc, env) pprint(obj)
Then we'll get:
{
"foo": [
{"1.1": "one", "1.2": "two" },
{"2.1": {"2.1.1": "three", "2.1.2": "four"}}
]
}
In this situation, there is no need to use jinja2.Environment
and jinja2.FileSystemLoader
to render the template, nor is it necessary to apply the indent
filter within the template. This is because pyyaml-include has already parsed the included files into objects.
❇️ Conclusions:
You can use jinja2'sinclude
andindent
to literally include other YAML files as raw text, or use pyyaml-include to include other YAML files as already-parsed objects.