我想为YAML文件的键顺序和行距设置一个模板,并将其应用到我拥有的100个YAML文件的存储库中.总的来说,我想做以下几件事:

  1. 加载现有的YAML文件
  2. 根据模板对键和值进行重新排序
  3. 删除仅为换行符的所有注释
  4. 应用模板中的行距
  5. 保存YAML文件

我用的是python 3.10和ruamel.yaml版本.在一个非常基本的层面上,我理解ruamel.yaml中的YAML对象是基于一个有序字典的,接受的答案here似乎是一种确保字典键的特定顺序的简单方法,但我不知道如何将其应用于YAML对象.

为了维护注释,我假定可以复制.ca属性,尽管我不知道如何应用模板中的行距规则.

使问题更加复杂的是,一些键本身可能有多个值(我认为在ruamel.yaml中这些值应该是CommentedSequence),每个值都应该遵循模板顺序-最后一个键后面需要一个空行.

以下是模板的基本版本,它应该提供我所讨论的 struct 的概述:

template='''
name:
region:
origin:
description:

go_live_date:
status:

governance:
  business_owner:
    am:
    eu:
    ap:
  technical_owner:
    am:
    eu:
    ap:

architecture:
  protocol:
  platform:

environments:
  - name:
    description:
    tier:
    locations:

'''

在下面的示例中,键顺序错误,有缺失和双倍行距加上一些注释:

'''
name: MyApp
description: My wonderful application
origin: internal
governance:
  technical_owner:
    am:
      - Nico Ferrell
    ap:
      - Benedict Berger
      - Elsie Parsons
    eu:
      - Frances Case

  business_owner:
    eu:
      - Audrey Dalton
    am:
      - John Carpenter # to be updated

architecture:
  protocol: [TCP]
  platforms: [python_3_10, java_16]


status: in production
go_live_date: 2024-01-01
environments:
  - name: EU Prod
    description: production environment for EMEA
    tier: production
    locations: [ABC, XYZ]
  - name: EU UAT
    description: UAT environment for EMEA
    locations: [LMN]
    tier: uat
# further environmental details to be added
'''

将模板和步骤应用于此示例后,生成的文件应如下所示:

'''
name: MyApp
origin: internal
description: My wonderful application

status: in production
go_live_date: 2024-01-01

governance:
  technical_owner:
    am:
      - Nico Ferrell
    eu:
      - Frances Case
    ap:
      - Benedict Berger
      - Elsie Parsons

  business_owner:
    am:
      - John Carpenter # to be updated
    eu:
      - Audrey Dalton

architecture:
  protocol: [TCP]
  platforms: [python_3_10, java_16]

environments:
  - name: EU Prod
    description: production environment for EMEA
    tier: production
    locations: [ABC, XYZ]
  - name: EU UAT
    description: UAT environment for EMEA
    tier: uat
    locations: [LMN]
# further environmental details to be added

'''

我不知道该怎么处理这件事,希望能帮上忙

推荐答案

您可以通过编写一个程序来解决这个问题,使用键的顺序以模板的顺序重新插入示例的键. 您可以使用CommentedMap()实例上可用的.insert()方法,即 用于使用模板中的逆键顺序加载在位置0处插入的YAML映射.但是你 也可以用正常的按键顺序和弹出分配,那会得到后面的第一个键,然后跟着 直到第一把 keys 放在前面.

要执行该操作,您可以使用保存路径的函数来查找对应的 示例中对应的数据 struct ,或并行递归.

import sys
from pathlib import Path
import ruamel.yaml

yaml = ruamel.yaml.YAML()
yaml.indent(mapping=2, sequence=4, offset=2)
yaml.preserve_quotes = True
template = yaml.load(Path('template.yaml'))
data = yaml.load(Path('example.yaml'))

def reorder(t, d):
    if isinstance(t, dict):
        for k, v in t.items():
            try:
                dv = d.pop(k)
            except:
                # this handles e.g. the key 'region' that is missing from the example
                continue  
            d[k] = dv
            reorder(v, dv)
    elif isinstance(t, list):
        # assume the template has one element, the example multiple
        for idx, elem in enumerate(d):
            reorder(t[0], elem)

reorder(template, data)
yaml.dump(data, sys.stdout)

这提供了:

name: MyApp
origin: internal
description: My wonderful application
go_live_date: 2024-01-01


status: in production
governance:
  business_owner:
    am:
      - John Carpenter # to be updated

    eu:
      - Audrey Dalton
  technical_owner:
    am:
      - Nico Ferrell
    eu:
      - Frances Case

    ap:
      - Benedict Berger
      - Elsie Parsons
architecture:
  platforms: [python_3_10, java_16]
  protocol: [TCP]
environments:
  - name: EU Prod
    description: production environment for EMEA
    tier: production
    locations: [ABC, XYZ]
  - name: EU UAT
    description: UAT environment for EMEA
    tier: uat
# further environmental details to be added
    locations: [LMN]

这将按模板的顺序获取密钥,但不处理空行. 那 是故意的,因为我们只递归到模板数据 struct 和后面的换行符 "John Carpenter"是序列的一部分,而不是模板的一部分. (你可以拨打print(data['governance']['business_owner']['am'].ca)查询) 由于ruamel.yaml当前处理注释的方式,将它们附加到最后一个完全解析的 node ,即注释# further.. 与关键的‘层’相关联,并通过重新排序来适当地改变位置(尽管这可能不是你想要的).

由于ruamel.yaml被认为是更新现有YAML(配置)文件中的值 尽可能多地保留(键顺序,注释,空行), 当然,没有做任何接近这一点,你会有一些工作,做其他步骤.

我会先浏览一下结果示例数据,然后打印你找到的注释:

def remove_empty_lines(d):
    if isinstance(d, dict):
        for k, v in d.items():
            if d.ca.comment:
                print('comment', d.ca.comment)
            if (itemc := d.ca.items.get(k)) is not None:
                print('itemc', v, itemc)
            remove_empty_lines(v)
    elif isinstance(d, list):
        for idx, elem in enumerate(d):
            if d.ca.comment:
                print('lcomment', d.ca.comment)
            if (itemc := d.ca.items.get(idx)) is not None:
                print('litemc', elem, itemc)
            remove_empty_lines(elem)

remove_empty_lines(data)

这提供了:

itemc in production [None, [CommentToken('\n\n', line: 22, col: 0)], None, None]
litemc John Carpenter [CommentToken('# to be updated\n\n', line: 17, col: 23), None, None, None]
litemc Frances Case [CommentToken('\n\n', line: 11, col: 8), None, None, None]
itemc uat [None, None, CommentToken('\n# further environmental details to be added\n', line: 35, col: 0), None]

因此,您将需要判断这些项目,并更新CommentToken.例如,通过使用

print(dir(data['governance']['business_owner']['am'].ca.items[0][0]))
print(data['governance']['business_owner']['am'].ca.items[0][0].value)

你会看到.value属性包含了实际的注释, 你可以go 掉虚假的换行符.

完成后,再次遍历模板和数据,判断模板 用于注释,并插入/更新示例.确保创建新的CommentTokens Do not从模板中复制它们.你可以找到here个这样的例子

Python相关问答推荐

如何根据日期和时间将状态更新为已过期或活动?

如何根据参数推断对象的返回类型?

删除所有列值,但判断是否存在任何二元组

什么相当于pytorch中的numpy累积ufunc

处理带有间隙(空)的duckDB上的重复副本并有效填充它们

用合并列替换现有列并重命名

在Wayland上使用setCellWidget时,try 编辑QTable Widget中的单元格时,PyQt 6崩溃

将9个3x3矩阵按特定顺序排列成9x9矩阵

关于Python异步编程的问题和使用await/await def关键字

Pandas—在数据透视表中占总数的百分比

有没有一种ONE—LINER的方法给一个框架的每一行一个由整数和字符串组成的唯一id?

SQLAlchemy bindparam在mssql上失败(但在mysql上工作)

Autocad使用pyautocad/comtypes将对象从一个图形复制到另一个图形

如何在信号的FFT中获得正确的频率幅值

为什么后跟inplace方法的`.rename(Columns={';b';:';b';},Copy=False)`没有更新原始数据帧?

Seaborn散点图使用多个不同的标记而不是点

Django抛出重复的键值违反唯一约束错误

为什么在生成时间序列时,元组索引会超出范围?

了解如何让库认识到我具有所需的依赖项

将参数从另一个python脚本中传递给main(argv