自动扩展 YAML 合并的工具?
Tool to automatically expand YAML merges?
我正在寻找一种工具或流程,它可以轻松获取包含锚点、别名和合并键的 YAML 文件,并扩展别名并合并到一个平面 YAML 文件中。仍然有许多不完全支持合并的常用 YAML 解析。
我希望能够利用合并来保持干燥,但有些情况下需要将其构建到更详细的 "flat" YAML 文件中,以便可以使用通过依赖于不完整 YAML 解析器的其他工具。
示例源 YAML:
default: &DEFAULT
URL: website.com
mode: production
site_name: Website
some_setting: h2i8yiuhef
some_other_setting: 3600
development:
<<: *DEFAULT
URL: website.local
mode: dev
test:
<<: *DEFAULT
URL: test.website.qa
mode: test
期望的输出 YAML:
default:
URL: website.com
mode: production
site_name: Website
some_setting: h2i8yiuhef
some_other_setting: 3600
development:
URL: website.local
mode: dev
site_name: Website
some_setting: h2i8yiuhef
some_other_setting: 3600
test:
URL: test.website.qa
mode: test
site_name: Website
some_setting: h2i8yiuhef
some_other_setting: 3600
如果您的系统上安装了 python,您可以执行 pip install ruamel.yaml.cmd
¹ 然后:
yaml merge-expand input.yaml output.yaml
(将 output.yaml
替换为 -
以写入标准输出)。这实现了保留键顺序和注释的合并扩展。
以上其实是利用ruamel.yaml
¹的几行代码
因此,如果您有 Python(2.7 或 3.4+)并使用 pip install ruamel.yaml
安装它,并将以下内容保存为 expand.py
:
import sys
from ruamel.yaml import YAML
yaml = YAML(typ='safe')
yaml.default_flow_style=False
with open(sys.argv[1]) as fp:
data = yaml.load(fp)
with open(sys.argv[2], 'w') as fp:
yaml.dump(data, fp)
你已经可以做到:
python expand.py input.yaml output.yaml
这将为您提供在语义上等同于您所请求内容的 YAML(在 output.yaml
中,映射的键已排序,在此程序输出中,它们未排序)。
以上假设您的 YAML 中没有任何标签,也不关心保留任何注释。其中大部分,以及键顺序,可以通过使用标准 YAML()
实例的补丁版本来保留。打补丁是必要的,因为标准 YAML()
实例也在往返过程中保留合并,这正是您不想要的:
import sys
from ruamel.yaml import YAML, SafeConstructor
yaml = YAML()
yaml.Constructor.flatten_mapping = SafeConstructor.flatten_mapping
yaml.default_flow_style=False
yaml.allow_duplicate_keys = True
# comment out next line if you want "normal" anchors/aliases in your output
yaml.representer.ignore_aliases = lambda x: True
with open(sys.argv[1]) as fp:
data = yaml.load(fp)
with open(sys.argv[2], 'w') as fp:
yaml.dump(data, fp)
使用此输入:
default: &DEFAULT
URL: website.com
mode: production
site_name: Website
some_setting: h2i8yiuhef
some_other_setting: 3600 # an hour?
development:
<<: *DEFAULT
URL: website.local # local web
mode: dev
test:
<<: *DEFAULT
URL: test.website.qa
mode: test
这将给出此输出(请注意,对合并键的注释会重复):
default:
URL: website.com
mode: production
site_name: Website
some_setting: h2i8yiuhef
some_other_setting: 3600 # an hour?
development:
URL: website.local # local web
mode: dev
site_name: Website
some_setting: h2i8yiuhef
some_other_setting: 3600 # an hour?
test:
URL: test.website.qa
mode: test
site_name: Website
some_setting: h2i8yiuhef
some_other_setting: 3600 # an hour?
以上是本答案开头提到的 yaml merge-expand
命令的作用。
¹ 免责声明:我是该软件包的作者。
更新:2019-03-13 12:41:05
- 根据 Anthon 的评论修改了此答案,该评论正确识别了 PyYAML 的限制。 (见下文的陷阱)。
上下文
- YAML 文件
- Python 用于解析 YAML
问题
- 用户 jtYamlEnthusiast 希望输出带有别名、锚点和合并键的 non-DRY 版本的 YAML 文件。
解决方案
- 备选方案 1:使用 Anthon infra 推广的
ruamel
库。
- 备选方案 2:使用 Python
pprint.pformat
并简单地进行 load/dump 往返转换。
理由
- 如果您可以自行决定安装除 pyyaml 之外的另一个 python 库,并且您希望高度控制 "round-trip" YAML 转换(例如例如,YAML 注释的保存)。
- 如果您不需要对往返 YAML 进行严格控制,或者由于某些其他原因限制了 pyyaml,您可以直接加载和转储 YAML,以获得 "non-DRY" 输出。
陷阱
在撰写本文时,PyYAML
相对于 ruamel
库在处理 YAML v1.1 和 YAML v1.2
另请参阅
例子
##
import pprint
import yaml
##
myrawyaml = '''
default: &DEFAULT
URL: website.com
mode: production
site_name: Website
some_setting: h2i8yiuhef
some_other_setting: 3600
development:
<<: *DEFAULT
URL: website.local
mode: dev
test:
<<: *DEFAULT
URL: test.website.qa
mode: test
'''
##
pynative = yaml.safe_load(myrawyaml)
vout = pprint.pformat(pynative)
print(vout) ##=> this is non-DRY and just happens to be well-formed YAML syntax
print(yaml.safe_load(vout)) ##=> this proves we have well-formed YAML if it loads without exception
如果您出于某种原因需要将扩展的 YAML 作为 YAML 写回文件,您可以:
使用@Anthon 的回答。但是,如上所述,如果您无法安装软件包,则此方法可能不可行。
使用@dreftymac 的回答。看来这个答案对某些人有用,但对我没有用;根据我的理解,pprint.pformat
returns 参数作为其 Python 表示的字符串,而 yaml.safe_load
期望 Python 表示本身。当然,您可以 eval
pprint.pformat
返回的字符串,但即使对受信任的输入使用 eval
也会让人感到恶心。 (同样,答案有几个赞成票,所以也许我在这里遗漏了一些东西。)
或者,你也可以按照我的方法做:
import json
import yaml
def expand_yml(yml):
return yaml.dump(json.loads(json.dumps(yml)))
expand_yml(my_yml_with_aliases)
由于 JSON 可以(有一些例外,例如别名)被视为 YAML 的严格子集,因此这种方法通常应该有效。但是,如果性能是一个问题,或者如果您正在处理更复杂的 YAML,这种方法可能不适合您。
我正在寻找一种工具或流程,它可以轻松获取包含锚点、别名和合并键的 YAML 文件,并扩展别名并合并到一个平面 YAML 文件中。仍然有许多不完全支持合并的常用 YAML 解析。
我希望能够利用合并来保持干燥,但有些情况下需要将其构建到更详细的 "flat" YAML 文件中,以便可以使用通过依赖于不完整 YAML 解析器的其他工具。
示例源 YAML:
default: &DEFAULT
URL: website.com
mode: production
site_name: Website
some_setting: h2i8yiuhef
some_other_setting: 3600
development:
<<: *DEFAULT
URL: website.local
mode: dev
test:
<<: *DEFAULT
URL: test.website.qa
mode: test
期望的输出 YAML:
default:
URL: website.com
mode: production
site_name: Website
some_setting: h2i8yiuhef
some_other_setting: 3600
development:
URL: website.local
mode: dev
site_name: Website
some_setting: h2i8yiuhef
some_other_setting: 3600
test:
URL: test.website.qa
mode: test
site_name: Website
some_setting: h2i8yiuhef
some_other_setting: 3600
如果您的系统上安装了 python,您可以执行 pip install ruamel.yaml.cmd
¹ 然后:
yaml merge-expand input.yaml output.yaml
(将 output.yaml
替换为 -
以写入标准输出)。这实现了保留键顺序和注释的合并扩展。
以上其实是利用ruamel.yaml
¹的几行代码
因此,如果您有 Python(2.7 或 3.4+)并使用 pip install ruamel.yaml
安装它,并将以下内容保存为 expand.py
:
import sys
from ruamel.yaml import YAML
yaml = YAML(typ='safe')
yaml.default_flow_style=False
with open(sys.argv[1]) as fp:
data = yaml.load(fp)
with open(sys.argv[2], 'w') as fp:
yaml.dump(data, fp)
你已经可以做到:
python expand.py input.yaml output.yaml
这将为您提供在语义上等同于您所请求内容的 YAML(在 output.yaml
中,映射的键已排序,在此程序输出中,它们未排序)。
以上假设您的 YAML 中没有任何标签,也不关心保留任何注释。其中大部分,以及键顺序,可以通过使用标准 YAML()
实例的补丁版本来保留。打补丁是必要的,因为标准 YAML()
实例也在往返过程中保留合并,这正是您不想要的:
import sys
from ruamel.yaml import YAML, SafeConstructor
yaml = YAML()
yaml.Constructor.flatten_mapping = SafeConstructor.flatten_mapping
yaml.default_flow_style=False
yaml.allow_duplicate_keys = True
# comment out next line if you want "normal" anchors/aliases in your output
yaml.representer.ignore_aliases = lambda x: True
with open(sys.argv[1]) as fp:
data = yaml.load(fp)
with open(sys.argv[2], 'w') as fp:
yaml.dump(data, fp)
使用此输入:
default: &DEFAULT
URL: website.com
mode: production
site_name: Website
some_setting: h2i8yiuhef
some_other_setting: 3600 # an hour?
development:
<<: *DEFAULT
URL: website.local # local web
mode: dev
test:
<<: *DEFAULT
URL: test.website.qa
mode: test
这将给出此输出(请注意,对合并键的注释会重复):
default:
URL: website.com
mode: production
site_name: Website
some_setting: h2i8yiuhef
some_other_setting: 3600 # an hour?
development:
URL: website.local # local web
mode: dev
site_name: Website
some_setting: h2i8yiuhef
some_other_setting: 3600 # an hour?
test:
URL: test.website.qa
mode: test
site_name: Website
some_setting: h2i8yiuhef
some_other_setting: 3600 # an hour?
以上是本答案开头提到的 yaml merge-expand
命令的作用。
¹ 免责声明:我是该软件包的作者。
更新:2019-03-13 12:41:05
- 根据 Anthon 的评论修改了此答案,该评论正确识别了 PyYAML 的限制。 (见下文的陷阱)。
上下文
- YAML 文件
- Python 用于解析 YAML
问题
- 用户 jtYamlEnthusiast 希望输出带有别名、锚点和合并键的 non-DRY 版本的 YAML 文件。
解决方案
- 备选方案 1:使用 Anthon infra 推广的
ruamel
库。 - 备选方案 2:使用 Python
pprint.pformat
并简单地进行 load/dump 往返转换。
理由
- 如果您可以自行决定安装除 pyyaml 之外的另一个 python 库,并且您希望高度控制 "round-trip" YAML 转换(例如例如,YAML 注释的保存)。
- 如果您不需要对往返 YAML 进行严格控制,或者由于某些其他原因限制了 pyyaml,您可以直接加载和转储 YAML,以获得 "non-DRY" 输出。
陷阱
在撰写本文时,
PyYAML
相对于ruamel
库在处理 YAML v1.1 和 YAML v1.2另请参阅
例子
##
import pprint
import yaml
##
myrawyaml = '''
default: &DEFAULT
URL: website.com
mode: production
site_name: Website
some_setting: h2i8yiuhef
some_other_setting: 3600
development:
<<: *DEFAULT
URL: website.local
mode: dev
test:
<<: *DEFAULT
URL: test.website.qa
mode: test
'''
##
pynative = yaml.safe_load(myrawyaml)
vout = pprint.pformat(pynative)
print(vout) ##=> this is non-DRY and just happens to be well-formed YAML syntax
print(yaml.safe_load(vout)) ##=> this proves we have well-formed YAML if it loads without exception
如果您出于某种原因需要将扩展的 YAML 作为 YAML 写回文件,您可以:
使用@Anthon 的回答。但是,如上所述,如果您无法安装软件包,则此方法可能不可行。
使用@dreftymac 的回答。看来这个答案对某些人有用,但对我没有用;根据我的理解,
pprint.pformat
returns 参数作为其 Python 表示的字符串,而yaml.safe_load
期望 Python 表示本身。当然,您可以eval
pprint.pformat
返回的字符串,但即使对受信任的输入使用eval
也会让人感到恶心。 (同样,答案有几个赞成票,所以也许我在这里遗漏了一些东西。)
或者,你也可以按照我的方法做:
import json
import yaml
def expand_yml(yml):
return yaml.dump(json.loads(json.dumps(yml)))
expand_yml(my_yml_with_aliases)
由于 JSON 可以(有一些例外,例如别名)被视为 YAML 的严格子集,因此这种方法通常应该有效。但是,如果性能是一个问题,或者如果您正在处理更复杂的 YAML,这种方法可能不适合您。