我目前正在构建一个使用YAML配置的python应用程序。我通过使用其他YAML文件生成YAML配置文件。我有一个“模板” YAML,它定义了我要在应用程序使用的YAML文件中使用的基本结构,然后定义了许多不同的“数据” YAML,它们填充了模板以某种方式旋转应用程序的行为。例如,说我有10个“数据” YAML。根据部署应用程序的位置,选择1个“数据” YAML,并用于填写“模板” YAML。结果填写的YAML是应用程序用来运行的内容。这节省了我很多工作。我遇到了这种方法的问题。假设我有一个YAML模板,如下所示:
{
"extends": "../../tsconfig-base.json",
"include": [
"**/*.ts"
],
"outDir": "built/shared",
"compilerOptions": {
"module": "amd",
"types": []
}
}
然后在其他地方,我喜欢10个“数据” YAML,每个YAML具有不同的{{id}}值。我似乎无法找出一种有效的方法来替换模板中所有这些{{id}}的出现。我有一个问题,因为有时要替换的值是我想主要保留的值的子字符串,或者出现的位置在层次结构中彼此相距很远,从而导致循环解决方案效率低下。我当前使用template + data生成配置文件的方法在python中看起来像这样:
id: {{id}}
endpoints:
url1: https://website.com/{{id}}/search
url2: https://website.com/foo/{{id}}/get_thing
url3: https://website.com/hello/world/{{id}}/trigger_stuff
foo:
bar:
deeply:
nested: {{id}}
关于如何更快地解决所有{{id}}事件的想法吗?
答案 0 :(得分:1)
如果单个id
文件的每个位置yaml
都相同,那么您可以将模板作为纯文本读取并逐行使用字符串替换。
new_file = []
# New id for replacement (from loaded file)
id_ = '123'
# Open template file
with open('template.yaml', 'r') as f:
# Iterate through each line
for l in f:
# Replace every {{id}} occurrence
new_file.append(l.replace('{{id}}', id_))
# Save the new file
with open('new_file.yaml', 'w') as f:
for l in new_file:
f.write(l)
这会将{{id}}
替换为文件中各处的相同id_
,并且不会更改任何格式。
答案 1 :(得分:0)
YAML内置了“锚”,您可以制作它并引用类似的变量。在我看来,这些实际上不是在替换它们的值,因为在解析YAML之后您只会看到结果。代码是从Reddit post偷偷窃取的,其中涉及类似的主题:
# example.yaml
params: ¶ms
PARAM1: &P1 5
PARAM2: &P2 "five"
PARAM3: &P3 [*P1, *P2]
data:
<<: *params
more:
- *P3
- *P2
ff
# yaml.load(example) =>
{
'params': {
'PARAM1': 5,
'PARAM2': 'five',
'PARAM3': [5, 'five']
},
'data': {
'PARAM1': 5,
'PARAM2': 'five',
'PARAM3': [5, 'five'],
'more': [[5, 'five'], 'five']
}
}
this上的帖子是我认为您可以将锚用作子字符串的方式(假设您使用的是python)
答案 2 :(得分:0)
您正在向我们建议PyYAML,但它并不非常适合 YAML文件上的更新。在此过程中,是否可以将文件加载到 首先,您松开映射键顺序,保留所有注释 在文件中包含,合并会扩展,以及任何特殊的锚点名称 在翻译中迷路。除此之外,PyYAML无法处理 最新的YAML规范(9年前发布),并且只能处理简单的映射键。
主要有两种解决方案:
如果使用替换,则可以比使用替换更有效的方式
@caseWestern建议的逐行替换。但是大多数
总而言之,您应该强化这些替代所采用的标量
地点。当前您有普通标量(即流式标量)
如果您插入诸如
#
,:
和其他语法上重要的元素。
为了防止这种情况发生,请重写输入文件以供使用 块样式文字标量:
id: {{id}}
endpoints:
url1: |-
https://website.com/{{id}}/search
url2: |-
https://website.com/foo/{{id}}/get_thing
url3: |-
https://website.com/hello/world/{{id}}/trigger_stuff
foo:
bar:
deeply:
nested: |-
{{id}}
如果以上内容位于alt.yaml
中,您可以执行以下操作:
val = 'xyz'
with open('alt.yaml') as ifp:
with open('new.yaml', 'w') as ofp:
ofp.write(ifp.read().replace('{{id}}', val))
获得:
id: xyz
endpoints:
url1: |-
https://website.com/xyz/search
url2: |-
https://website.com/foo/xyz/get_thing
url3: |-
https://website.com/hello/world/xyz/trigger_stuff
foo:
bar:
deeply:
nested: |-
xyz
使用ruamel.yaml(免责声明:我是该软件包的作者),您不必
担心通过语法上重要的替换文本来破坏输入。如果
这样做,输出将自动正确引用。你一定要
请确保您输入的内容是有效的YAML,并使用{{
之类的
节点的开头表示两个嵌套的流样式映射,您会遇到麻烦。
这里的最大优点是您的输入文件已加载,并且被检查为 正确的YAML。但这比文件级替换慢得多。
因此,如果您输入的是in.yaml
:
id: <<id>> # has to be unique
endpoints: &EP
url1: https://website.com/<<id>>/search
url2: https://website.com/foo/<<id>>/get_thing
url3: https://website.com/hello/world/<<id>>/trigger_stuff
foo:
bar:
deeply:
nested: <<id>>
endpoints: *EP
[octal, hex]: 0o123, 0x1F
您可以这样做:
import sys
import ruamel.yaml
def recurse(d, pat, rep):
if isinstance(d, dict):
for k in d:
if isinstance(d[k], str):
d[k] = d[k].replace(pat, rep)
else:
recurse(d[k], pat, rep)
if isinstance(d, list):
for idx, elem in enumerate(d):
if isinstance(elem, str):
d[idx] = elem.replace(pat, rep)
else:
recurse(d[idx], pat, rep)
yaml = ruamel.yaml.YAML()
yaml.preserve_quotes = True
with open('in.yaml') as fp:
data = yaml.load(fp)
recurse(data, '<<id>>', 'xy: z') # not that this makes much sense, but it proves a point
yaml.dump(data, sys.stdout)
给出:
id: 'xy: z' # has to be unique
endpoints: &EP
url1: 'https://website.com/xy: z/search'
url2: 'https://website.com/foo/xy: z/get_thing'
url3: 'https://website.com/hello/world/xy: z/trigger_stuff'
foo:
bar:
deeply:
nested: 'xy: z'
endpoints: *EP
[octal, hex]: 0o123, 0x1F
请注意:
具有替换模式的值在转储时自动引用到
处理:
+空格,否则将表示映射并破坏YAML
与PyYAML的YAML.load()
函数相反的load
方法是
安全(即无法通过操纵输入来执行任意Python
文件。
注释,八进制和十六进制整数以及别名被保留。
PyYAML尽管有效,但根本无法加载文件in.yaml
上面的recurse
仅更改输入映射值,
如果您还想做按键,则必须弹出
重新插入所有密钥(即使没有更改),以保持原始状态
订单,或者您需要使用enumerate
和d.insert(position, key,
value)
。如果有合并,您也不能只遍历键,
您将不得不遍历“ dict”的非合并键。