【问题标题】:Tool to automatically expand YAML merges?自动扩展 YAML 合并的工具?
【发布时间】:2017-12-11 14:47:05
【问题描述】:

我正在寻找一种工具或流程,它可以轻松获取包含锚点、别名和合并键的 YAML 文件,并扩展别名并合并到一个平面 YAML 文件中。仍然有许多不完全支持合并的常用 YAML 解析。

我希望能够利用合并来保持干燥,但在某些情况下,需要将其构建到更详细的“平面”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 yaml reformatting


    【解决方案1】:

    如果您的系统上安装了 python,您可以执行 pip install ruamel.yaml.cmd¹ 然后:

    yaml merge-expand input.yaml output.yaml
    

    (将output.yaml 替换为- 以写入标准输出)。这实现了合并扩展,保留了密钥顺序和 cmets。

    上面实际上是利用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 中没有任何标签,也不关心保留任何 cmets。其中大部分以及密钥顺序可以通过使用标准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
    

    这将给出此输出(请注意,合并的键上的 cmets 会重复):

    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 命令的作用。


    ¹ 免责声明:我是该软件包的作者。

    【讨论】:

    • 谢谢。我会看看这个。这听起来很适合我的用例,因为我可以看到它很好地融入了构建过程。
    • yaml merge-expand 似乎对我不起作用。我得到的只是合并键重命名为 *id001、*id002、*id003 等。
    • @jthompson 您是否按照介绍的方式运行上述程序?我只是(重新)在一个干净的 virtualenv 中尝试了这个,这对我有用。
    • 谢谢,现在我可以重现了,我会尽快调查原因(如“未来几天”,必须先完成一些工作)
    • 我一直专注于“扩展合并”,并没有解决(重新)创建“正常”锚/别名的问题。我已经修复了 yaml 命令 (pip install -U ruamel.yaml.cmd) 并在上面的代码中添加了单行修复。
    【解决方案2】:

    更新:2019-03-13 12:41:05

    • 此答案已根据Anthon 的评论进行了修改,该评论正确识别了 PyYAML 的限制。 (请参阅下文中的陷阱)。

    上下文

    • YAML 文件
    • 用于解析 YAML 的 Python

    问题

    • 用户 jtYamlEnthusiast 希望输出带有别名、锚点和合并键的 YAML 文件的 non-DRY 版本。

    解决方案

    • 备选方案 1:使用 Anthon infra 推广的 ruamel 库。
    • 备选方案 2:使用 Python pprint.pformat 并简单地执行加载/转储往返转换。

    基本原理

    • ruamel 库非常棒,如果您可以自行决定安装除 pyyaml 之外的另一个 python 库,并且您希望高度控制“往返”YAML 转换(例如保留 YAML cmets,例如)。
    • 如果您不需要对往返 YAML 进行严格控制,或者由于某些其他原因受限于 pyyaml,您可以直接加载和转储 YAML,以获得“非 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
    

    【讨论】:

    • 这不起作用,PyYAML 无法加载 YAML 1.2 文件(要么抱怨它不能在显式文档上,要么静默失败)
    • 嗯...我无法在我的机器上重现故障场景。由于 unicode 文字,使用 pprint.pformat 可能会出现问题。
    • 那是因为您的输入没有任何内容将其标记为 YAML 1.2。尝试使其成为一个完整的指令文档,或插入一个八进制值,如0o52(应加载为整数 42)。声称应该有效的是您的第一个项目符号,这当然并不意味着 每个 YAML 文档都失败
    • 啊……我明白你的意思。这是 PyYAML 长期存在的问题。我将修改答案以解决您提出的这一点,这是一个合理的观点。
    • YAML 1.2 规范确实由来已久,距今已近 10 年前的 2009 年发布。
    【解决方案3】:

    如果您出于某种原因需要将扩展​​的 YAML 作为 YAML 写回文件,您可以:

    • 使用@Anthon 的答案。但是,如上所述,如果您无法安装软件包,这种方法可能不可行。

    • 使用@dreftymac 的答案。看来这个答案对某些人有用,但对我没有用;据我了解,pprint.pformat 将参数作为其 Python 表示的字符串返回,yaml.safe_load 期望 Python 表示本身。当然,您可以evalpprint.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,那么这种方法可能不适合您。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2022-01-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-09-19
      • 1970-01-01
      相关资源
      最近更新 更多