我正在尝试建立一个系统,其中有一些(可能更多)yaml文件将用于配置。我希望能够在另一个文件中引用内容。
我知道YAML不允许这样做。
我认为,我的计划是将两个YAML文件合并,然后将其视为单个文件。我很确定我可以将两个文件放在一起,创建一个临时文件,然后将其作为YAML读取,或者将文件作为文本读取,连接它们然后解析字符串。
然而,我觉得应该有更好的方法来做到这一点。在那儿?
答案 0 :(得分:0)
在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文件并将它们合并到内存中。即鉴于合并密钥文档中的示例分为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
(你必须以正确的顺序提交文件,因此排序。并确保文件末尾有换行符,否则可能会出现意外错误。)
答案 1 :(得分:0)
我认为这比@ 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
欢迎评论。