PyYaml 结合两个 yaml 文件
Posted
技术标签:
【中文标题】PyYaml 结合两个 yaml 文件【英文标题】:PyYaml combining two yaml files 【发布时间】:2016-06-01 06:24:11 【问题描述】:我正在尝试建立一个系统,其中有几个(可能更多)用于配置的 yaml 文件。我希望能够在另一个文件中引用一个文件中的内容。
我知道 YAML 不允许这样做。
我认为我的计划是将两个 YAML 文件合并,然后将其视为一个文件。我很确定我可以将这两个文件放在一起,创建一个临时文件,然后将其读取为 YAML,或者将文件读取为文本,将它们连接起来,然后解析字符串。
但是,我觉得应该有更好的方法来做到这一点。有没有?
【问题讨论】:
我认为这已经得到了回答:***.com/questions/47424865/… @Tom,我的问题来自 5.5 年前。您指向的是 4 年前...只是让您知道... 【参考方案1】:在 YAML 中引用的唯一方法是使用 &
(锚点)和 *
(别名)。为了使它们起作用,它们必须在同一个 YAML 文档中。以下将不起作用(这是基于merge key 功能,但普通对象引用具有相同的限制):
import ruamel.yaml
yaml_str = """\
a: &BASE x: 1, y: 2
---
b:
<< : *BASE
z: 3
"""
for data in ruamel.yaml.load_all(yaml_str):
print(data)
抛出未找到“BASE”的作曲家错误。删除---
文档分隔符,一切都很好。
所以原则上连接两个文档是可行的。如果不将其与包含它的文档连接起来,就无法单独加载具有别名的文档。
另外需要注意的是,所有文档都必须在顶层具有映射或序列。如果会组合一个序列:
- &BASE a
- b
带有映射:
c: 1
d: *BASE
结果将无法加载。
如前所述,如果所有文件的***类型相同,则无法加载 YAML 文件并将它们组合到内存中。 IE。鉴于合并密钥文档中的示例拆分为1.yaml
:
- &CENTER x: 1, y: 2
- &LEFT x: 0, y: 2
- &BIG r: 10
- &SMALL r: 1
2.yaml
:
# Explicit keys
-
x: 1
y: 2
r: 10
label: center/big
3.yaml
:
# Merge one map
-
<< : *CENTER
r: 10
label: center/big
4.yaml
:
# Merge multiple maps
-
<< : [ *CENTER, *BIG ]
label: center/big
5.yaml
:
# Override
-
<< : [ *BIG, *LEFT, *SMALL ]
x: 1
label: center/big
您不能在单个 YAML 文件上使用 load()
并将它们组合起来:
import ruamel.yaml
import glob
data = []
for file_name in sorted(glob.glob('*.yaml')):
data.append(ruamel.yaml.load(open(file_name)))
print(ruamel.yaml.dump(data, allow_unicode=True))
(如果2.yaml
等没有别名,上述方法将起作用)
如果您不想在程序之外连接文件,您可以 使用这个类:
class CombinedOpenForReading(object):
def __init__(self, file_names):
self._to_do = file_names[:]
self._fp = None
def __enter__(self):
return self
def __exit__(self, exception_type, exception_value, exception_traceback):
if self._fp:
self._fp.close()
def read(self, size=None):
res = ''
while True:
if self._fp is None:
if not self._to_do:
return res
else:
self._fp = open(self._to_do.pop(0))
if size is None:
data = self._fp.read()
else:
data = self._fp.read(size)
if size is None or not data:
self._fp.close()
self._fp = None
res += data
if size is None:
continue
size -= len(data)
if size == 0:
break
return res
要做的事情:
import ruamel.yaml
import glob
with CombinedOpenForReading(sorted(glob.glob('*.yaml'))) as fp:
data = ruamel.yaml.round_trip_load(fp)
assert data[6]['r'] == 10
print(ruamel.yaml.dump(data, Dumper=ruamel.yaml.RoundTripDumper))
得到:
- &CENTER x: 1, y: 2
- &LEFT x: 0, y: 2
- &BIG r: 10
- &SMALL r: 1
# Explicit keys
- x: 1
y: 2
r: 10
label: center/big
# Merge one map
- <<: *CENTER
r: 10
label: center/big
# Merge multiple maps
- <<: [*CENTER, *BIG]
label: center/big
# Override
- <<: [*BIG, *LEFT, *SMALL]
x: 1
label: center/big
(您必须按正确的顺序提交文件,从而进行排序。并确保文件末尾有换行符,否则可能会出现意外错误。)
【讨论】:
您的警告是不是意味着 &BASE 和 *BASE 必须属于同一类事物(序列与映射)?我没有意识到这是一个问题,但它不应该是一个问题。我不打算在一个文件中使用多个文档。我打算在一个文档中使用多个文件。 啊,你的意思是为了使文件可以合并,它们必须具有相同的最顶层类型。是的,这应该不是问题。 @BrianPostow 是的,最上面的类型必须相同【参考方案2】:我认为这比@Anthon 的简单。它可能并不完整,但我认为这就是我所需要的......
def merge(fList):
'''
Takes a list of yaml files and loads them as a single yaml document.
Restrictions:
1) None of the files may have a yaml document marker (---)
2) All of the files must have the same top-level type (dictionary or list)
3) If any pointers cross between files, then the file in which they are defined (&) must be
earlier in the list than any uses (*).
'''
if not fList:
#if flist is the empty list, return an empty list. This is arbitrary, if it turns out that
#an empty dictionary is better, we can do something about that.
return []
sList = []
for f in fList:
with open(f, 'r') as stream:
sList.append(stream.read())
fString = ''
for s in sList:
fString = fString + '\n'+ s
y = yaml.load(fString)
return y
欢迎参加。
【讨论】:
合并三个文件,完全替换某些部分而不是合并它们 怎么样?如果它们是字典,并且键不相交,那当然可以,但这可能是 YAML 错误... 我真的不知道。最终使用了 merge-yaml npm cli 工具。而且那里也有问题,所以我不得不将一个文件移动到第二个位置:D 我想知道这怎么可能。甚至还有可用的 yamlreader 来检查 yaml 文件语法错误并对其进行格式化,但仍然没有帮助以上是关于PyYaml 结合两个 yaml 文件的主要内容,如果未能解决你的问题,请参考以下文章
为啥致命错误:安装 PyYAML 时找不到“yaml.h”文件?