使用 ruamel.yaml 删除最后一个字典键时保留以下注释

Preserving following comments when removing last dict key with ruamel.yaml

我正在尝试使用 ruamel.yaml Python 库从大型 YAML 文件中的嵌套词典中删除一些 keys/value 对,同时保留周围的评论。这是我使用的代码的简化版本:

import sys
import ruamel.yaml

with open(sys.argv[1], 'r') as doc:
    parsed = ruamel.yaml.round_trip_load(doc, preserve_quotes=True)

    for item in parsed['items']:
        if item['color'] == 'blue':
            del item['color']

    yaml = ruamel.yaml.YAML(typ='rt')
    yaml.indent(sequence=4, offset=2)
    yaml.dump(parsed, sys.stdout)

... 以及我正在尝试编辑的随附文件(目的是删除 'color: blue' 行:

▶ cat items.yml
items:
  - name: a
    color: blue
    texture: smooth

  # This is a comment above 'c'
  # More comment
  - name: b
    texture: wrinkled
    color: yellow

对于那个特定的文件,代码可以满足我的要求:

▶ ./munge.py items.yml
items:
  - name: a
    texture: smooth

  # This is a comment above 'c'
  # More comment
  - name: b
    texture: wrinkled
    color: yellow

但是,如果 color: blue 是第一个字典中的 last key/value 对,则第二个项目之前的注释被吃掉:

▶ cat items.yml
items:
  - name: a
    texture: smooth
    color: blue

  # This is a comment above 'c'
  # More comment
  - name: b
    texture: wrinkled
    color: yellow
▶ ./munge.py items.yml
items:
  - name: a
    texture: smooth
  - name: b
    texture: wrinkled
    color: yellow

评论似乎附加到词典的最后 key/value 对,而不是表示为第二项之前的 'leading' 评论。

有没有办法在删除字典中的最后一个键时保留下一项之前的注释?

评论与最后解析的集合节点关联,基于 映射的键或序列的索引。你的评论 "before",实际上是在最后一个之后的行尾注释 第一个序列项的键值对,扩展到多个 行。

如果你使用print(parsed['items'][0].ca)打印dict like对象(一个CommentedMap)的注释属性,你会得到:

items={'color': [None, None, CommentToken("\n\n  # This is a comment above 'c'\n  # More comment\n", line: 5, col: 2), None]})

所以如果你从CommentedMap中删除键color,转储parsed将不再输出 评论,因为没有自动机制将评论与另一个键或某个更高的节点相关联。

您可以 "move" 通过跟踪循环中的前一个键来评论该评论:

parsed['items'][0].ca.items['texture'] = parsed['items'][0].ca.items.pop('color')

这需要您跟踪上一个密钥:

import sys
import ruamel.yaml

yaml_str = """\
items:
  - name: a
    texture: smooth
    color: blue

  # This is a comment associated with the last key of the preceding mapping
  # More of the same comment
  - name: b
    texture: wrinkled
    color: yellow
"""

yaml = ruamel.yaml.YAML()
yaml.indent(sequence=4, offset=2)
yaml.preserve_quotes = True
parsed = yaml.load(yaml_str)
prev = None
for item in parsed['items']:
    for key in item:
        if key == 'color' and item[key] == 'blue':
            if prev is not None:
                item.ca.items[prev] = item.ca.items.pop(key)
            del item['color']
            break
        prev = key
yaml.dump(parsed, sys.stdout)

给出:

items:
  - name: a
    texture: smooth

  # This is a comment associated with the last key of the preceding mapping
  # More of the same comment
  - name: b
    texture: wrinkled
    color: yellow

一些注意事项:

  • 将评论移至父项 (sequence/list/CommentedSeq) 是 可能,但不是那么微不足道

  • 没必要混老API (ruamel.yaml.round_trip_load()) 与新的 (yaml=YAML())

  • 你应该从 with 语句中获取 for 循环,文件 可以在 round_trip_load(doc) (或 yaml = ruamel.yaml.YAML(); yaml.load(doc)

  • 之后关闭
  • 官方推荐 扩展 YAML 文件的 .yaml 已近 13 年

并确保你有一个测试用例 and/or 固定你正在使用的 ruamel.yaml 版本 (ruamel.yaml<0.17) 因为这样的评论欺骗不能保证在未来版本。