gpt4 book ai didi

yaml - PyYaml "include file"和 yaml 别名( anchor /引用)

转载 作者:行者123 更新时间:2023-12-04 23:14:55 25 4
gpt4 key购买 nike

我有一个很大的 YAML 文件,其中大量使用了 YAML anchor 和引用,例如:

warehouse:
obj1: &obj1
key1: 1
key2: 2
specific:
spec1:
<<: *obj1
spec2:
<<: *obj1
key1: 10

文件太大了,所以我寻找了一个可以让我拆分为 2 个文件的解决方案: warehouse.yamlspecific.yaml ,并包含 warehouse.yamlspecific.yaml .我读了 this simple article ,它描述了我如何使用 PyYAML 来实现这一点,但它也说不支持合并键(<<)。

我真的有一个错误:

yaml.composer.ComposerError: found undefined alias 'obj1



当我试着那样去的时候。

所以,我开始寻找替代方法,但我很困惑,因为我对 PyYAML 不太了解。

我可以获得所需的合并 key 支持吗?我的问题还有其他解决方案吗?

最佳答案

在 PyYAML 中处理 anchor 和别名的关键是字典 anchors那是 Composer 的一部分.它将 anchor 映射到节点,以便可以查找别名。它的存在受限于 Composer 的存在,它是 Loader 的复合元素你使用的。

那个Loader类仅在调用 yaml.load() 期间存在所以之后没有简单的方法来提取它:首先你必须制作 Loader() 的实例。坚持然后确保正常compose_document()方法没有被调用(其中包括 self.anchors = {} ,以便为下一个文档(在单个流中)保持干净)。

如果你有 warehouse.yaml,事情会更复杂:

warehouse:
obj1: &obj1
key1: 1
key2: 2

specific.yaml :
warehouse: !include warehouse.yaml
specific:
spec1:
<<: *obj1
spec2:
<<: *obj1
key1: 10

即使您可以保留、提取和传递 anchor 信息,因为 Composer 正在处理 specific.yaml,您也永远不会让它与您的代码片段一起使用。会比标签 !include 更早地遇到未定义的别名用于构建(和填充 anchors )。

你可以做的来规避这个问题是包含 specific.yaml
specific:
spec1:
<<: *obj1
spec2:
<<: *obj1
key1: 10

来自 warehouse.yaml :
warehouse:
obj1: &obj1
key1: 1
key2: 2
specific: !include specific.yaml

,或将两者都包含在第三个文件中。 请注意 key specific在两个文件中 .

使用这两个文件运行:
import sys
from ruamel import yaml

def my_compose_document(self):
self.get_event()
node = self.compose_node(None, None)
self.get_event()
# self.anchors = {} # <<<< commented out
return node

yaml.SafeLoader.compose_document = my_compose_document

# adapted from http://code.activestate.com/recipes/577613-yaml-include-support/
def yaml_include(loader, node):
with open(node.value) as inputfile:
return list(my_safe_load(inputfile, master=loader).values())[0]
# leave out the [0] if your include file drops the key ^^^

yaml.add_constructor("!include", yaml_include, Loader=yaml.SafeLoader)


def my_safe_load(stream, Loader=yaml.SafeLoader, master=None):
loader = Loader(stream)
if master is not None:
loader.anchors = master.anchors
try:
return loader.get_single_data()
finally:
loader.dispose()

with open('warehouse.yaml') as fp:
data = my_safe_load(fp)
yaml.safe_dump(data, sys.stdout, default_flow_style=False)

这使:
specific:
spec1:
key1: 1
key2: 2
spec2:
key1: 10
key2: 2
warehouse:
obj1:
key1: 1
key2: 2

如果您的 specific.yaml不会有顶级 key specific :
spec1:
<<: *obj1
spec2:
<<: *obj1
key1: 10

然后替换 yaml_include()的最后一行和:
return my_safe_load(inputfile, master=loader)

以上是通过 ruamel.yaml 完成的(免责声明:我是该包的作者)并在 Python 2.7 和 3.6 上进行了测试。通过更改导入,它也可以与 PyYAML 一起使用。

与新 ruamel.yaml上面的API可以简化很多,因为 loader交给 yaml_include()构造函数知道 YAML例如,当然你仍然需要一个适应的 compose_document这不会破坏 anchor 。假设 specific.yaml 没有 顶级 key specific ,下面给出与之前相同的输出。
import sys
from ruamel.std.pathlib import Path
from ruamel.yaml import YAML, version_info

yaml = YAML(typ='safe', pure=True)
yaml.default_flow_style = False


def my_compose_document(self):
self.parser.get_event()
node = self.compose_node(None, None)
self.parser.get_event()
# self.anchors = {} # <<<< commented out
return node

yaml.Composer.compose_document = my_compose_document

# adapted from http://code.activestate.com/recipes/577613-yaml-include-support/
def yaml_include(loader, node):
y = loader.loader
yaml = YAML(typ=y.typ, pure=y.pure) # same values as including YAML
yaml.composer.anchors = loader.composer.anchors
return yaml.load(Path(node.value))

yaml.Constructor.add_constructor("!include", yaml_include)

data = yaml.load(Path('warehouse.yaml'))
yaml.dump(data, sys.stdout)

关于yaml - PyYaml "include file"和 yaml 别名( anchor /引用),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44910886/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com