在 Python 中加载用 Jinja2 嵌套的 YAML

Posted

技术标签:

【中文标题】在 Python 中加载用 Jinja2 嵌套的 YAML【英文标题】:Load YAML nested with Jinja2 in Python 【发布时间】:2015-11-05 04:07:14 【问题描述】:

我有一个 YAML 文件 (all.yaml),如下所示:

...
var1: val1
var2: val2
var3: var1-var2.txt
...

如果我像这样在 Python 中加载它:

import yaml

f = open('all.yaml')
dataMap = yaml.safe_load(f)
f.close()
print(dataMap["var3"])

输出是var1-var2.txt 而不是val1-val2.txt

是否可以用值替换嵌套的变量?

我尝试加载它:

import jinja2
templateLoader = jinja2.FileSystemLoader( searchpath="/path/to/dir" )
templateEnv = jinja2.Environment( loader=templateLoader )
TEMPLATE_FILE = "all.yaml"
template = templateEnv.get_template( TEMPLATE_FILE )

不再抛出异常,现在我被卡住了,不得不研究如何进行。

【问题讨论】:

您显然需要在某些时候使用 Jinja2。到目前为止,您尝试过什么? 我尝试用 import jinja2 templateLoader = jinja2.FileSystemLoader( searchpath="/" ) templateEnv = jinja2.Environment( loader=templateLoader ) TEMPLATE_FILE = "all.yaml" template = templateEnv.get_template ( TEMPLATE_FILE ) 但这会抛出 TemplateNotFound-Exception 最好更新您的答案,而不是试图将代码示例留在 cmets 中。 TemplateNotFound 异常应该比较容易解决;您的文件 all.yml 不太可能位于 /,但这是您告诉 Jinja2 查找的位置。 是的,这就是问题所在。我更新了问题。谢谢 为什么这个问题被否决了? 【参考方案1】:

我不相信你可以使用:

yaml.load 

yaml.safe_load 

在包含jinja2 变量作为值的文件上。 variable 将尝试被 yaml 解释为字典。

【讨论】:

注意: 这可以通过引用占位符来避免,以便 YAML 将其解释为纯标量值(字符串)。将variable 更改为"variable"【参考方案2】:

首先定义一个Undefined 类并加载yaml 以获取已知值。然后再次加载它并使用已知值进行渲染。

#!/usr/bin/env python

import yaml
from jinja2 import Template, Undefined

str1 = '''var1: val1
var2: val2
var3: var1-var2.txt
'''

class NullUndefined(Undefined):
  def __getattr__(self, key):
    return ''

t = Template(str1, undefined=NullUndefined)
c = yaml.safe_load(t.render())

print t.render(c)

运行它:

$ ./test.py
var1: val1
var2: val2
var3: val1-val2.txt

【讨论】:

【参考方案3】:

YAML 规范中没有标量部分的替换/替换。

您想在该级别上执行的任何操作都必须在您的应用程序中完成。对我和 YAML 来说,var1 只是一个嵌套映射。 var1var1: null: null 的缩写。之后,- 就不允许了。

但是,您的帖子存在多个问题:

    您使用的 PyYAML 仅支持旧的 (2005) YAML 1.1。因此,您不能像在 YAML 1.2 中那样使用显式文档开头 (---) 来拥有多个文档(即以 ... 结尾)

    1234563 /p>

    如果您只在文件中使用var1,PyYAML 无法加载它,因为它将 YAML 映射加载为 Python 字典,而 Python 不允许字典的可变键。就像你尝试在 Python 中得到 TypeError 一样:dict(var1=None): None

因此,您至少应该将输入文件 all.yaml 更改为:

---
var1: val1
var2: val2
var3: 'var1-var2.txt'
...

让它在 YAML 中加载。

你必须加载这个文件两次:

一次由 PyYAML 获取可用于呈现模板的值 曾作为 jinja2 的模板

在渲染模板后,您在 PyYAML 中再次加载该(字符串)并且您拥有所需的值。

鉴于上面在当前目录和这个程序中指定的更正的all.yaml

import yaml
import jinja2

YAML_FILE = 'all.yaml'
with open(YAML_FILE) as fp:
    dataMap = yaml.safe_load(fp)

env = jinja2.Environment(loader=jinja2.FileSystemLoader(searchpath='.'))
template = env.get_template(YAML_FILE)

data = yaml.safe_load(template.render(**dataMap))
print(data["var3"])

将打印您想要的内容:

val1-val2.txt

【讨论】:

您绝对可以将 YAML 模块和 Jinja2 模块结合起来做一些像 OP 要求的事情。 Ansible 是对 YAML 值执行 Jinja2 模板处理的工具的一个示例。可以肯定的是,您不能单独使用 yaml 模块执行此操作,但我认为这不是 OP 所要求的。 @larsks 你是对的。另一个示例工具是cookiecutter 当然,你总是可以用 is 有效的 YAML 替换无效的 YAML,然后加载它。然而,OP 声明他的第一个示例 (all.yaml) 是 YAML,但事实并非如此。您首先必须扩展该 jinja2 模板,然后解析扩展的结果,希望届时它被 YAML 解析器理解的内容替换。 这是一个完全准确的说法。恰好看起来与 YAML 语法相似的未处理 Jinja 模板与格式正确的 YAML 不同。经处理的 Jinja 模板决不能保证生成格式良好的 YAML。一个挑战是将这种情况传达给尚未使用 YAML(更不用说为其开发模块)的潜在 YAML 用户,同时尽量减少他们感到困惑或被吓跑的机会。【参考方案4】:

这是一种可能的解决方案:

    使用 yaml 模块解析您的 YAML 文档 遍历 YAML 文档中的键,将每个值视为 Jinja2 模板,将 YAML 文档的键作为参数传递到该模板。

例如:

import yaml
from jinja2 import Template

with open('sample.yml') as fd:
    data = yaml.load(fd)

for k, v in data.items():
    t = Template(v)
    data[k] = t.render(**data)

print yaml.safe_dump(data, default_flow_style=False)

这在您的特定示例中可以正常工作,但不会对嵌套数据结构等有用的东西(事实上,它可能会爆炸)。

【讨论】:

当 YAML 文件包含类似 val: 1.5 的内容时,它会抛出 AttributeError: 'float' object has no attribute 'iter_fields' 哦,当然。这是一个例子,不是一个强大的解决方案! :) 嘿,-1 人,有什么可以改进的吗?我知道这是一个较旧的,但我很乐意修复它。干杯!

以上是关于在 Python 中加载用 Jinja2 嵌套的 YAML的主要内容,如果未能解决你的问题,请参考以下文章

XListview的下拉刷新上拉加载用Pull解析XML

等待使用 Python 在 Selenium 中加载所有资源 [重复]

Ansible之template模板

使用熊猫时python中的嵌套循环问题

jinja2批量生成python脚本

GAE / Python / jinja2 / 如何在join语句中引用子目录