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

时间:2019-08-21 00:14:47

标签: python yaml ruamel.yaml

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

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)

...以及我要编辑的随附文件(目的是删除“颜色:蓝色”行:

▶ 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 键/值对,则第二个项目之前的注释会被吃掉

▶ 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

该注释似乎附加在字典的最后一个键/值对上,而不是表示为第二项之前的“前导”注释。

即使删除字典中的最后一个键,有什么方法可以保留下一个项目之前的注释?

1 个答案:

答案 0 :(得分:1)

注释基于以下内容与解析的最后一个集合节点相关联: 映射键或序列索引。你的评论 “之前”,实际上是最后一行之后的行尾注释 第一个序列项的键值对,扩展到多个 行。

如果使用CommentedMap打印类似dict的对象(print(parsed['items'][0].ca))的comment属性,则会得到:

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

因此,如果您从color中删除密钥CommentedMap,则转储parsed将不再输出 注释,因为没有自动机制将注释与另一个键或某个更高的上层节点相关联。

您可以通过跟踪循环中的上一个键来“移动”该评论:

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

一些注意事项:

  • 将注释移至父对象(序列/列表/ CommentedSeq)为 可能,但并不琐碎

  • 无需混合旧的API (ruamel.yaml.round_trip_load()和新的(yaml=YAML()

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

  • officially recommended extension YAML文件的.yaml已有近13年的历史

并确保您具有测试用例和/或固定正在使用的ruamel.yaml版本(ruamel.yaml<0.17),因为这样的注释技巧不能保证在将来的版本中保持相同的作用。