【问题标题】:Preserving following comments when removing last dict key with ruamel.yaml使用 ruamel.yaml 删除最后一个 dict 键时保留以下注释
【发布时间】:2021-09-26 09:29:04
【问题描述】:

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

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

看起来评论附加到字典的最后一个键/值对,而不是表示为第二项之前的“前导”评论。

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

【问题讨论】:

    标签: python yaml ruamel.yaml


    【解决方案1】:

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

    如果您使用print(parsed['items'][0].ca) 打印dict 对象(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将不再输出 评论,因为没有自动机制将评论与另一个键或某个更高的节点相关联。

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

    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))之后关闭

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

    并确保您有一个测试用例和/或固定您正在使用的 ruamel.yaml 版本 (ruamel.yaml<0.17),因为这样的评论技巧不能保证在未来的版本中保持相同的工作方式。

    【讨论】:

    • 完美,谢谢!将注释“移动”到前一个键的解决方案正是我所需要的——我正在研究如何通过 API 来做,但没有意识到我可以直接安全地操作 item.ca.items。谢谢!
    猜你喜欢
    • 2023-03-13
    • 1970-01-01
    • 2021-11-02
    • 2017-07-31
    • 1970-01-01
    • 2023-04-08
    • 1970-01-01
    • 2022-07-19
    相关资源
    最近更新 更多