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

℡╲_俬逩灬. 提交于 2021-02-05 11:52:53

问题


I'm trying to use the ruamel.yaml Python library to remove some keys/value pairs from nested dictionaries within a large YAML file, while retaining surrounding comments. Here's a simplified version of the code I'm using:

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)

... and an accompanying file that I'm trying to edit (the intent is to remove the 'color: blue' line:

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

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

With that specific file, the code does what I want:

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

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

However, if color: blue is the last key/value pair in the first dict, the comment preceding the second item gets eaten:

▶ 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

It looks like the comment is being attached to the last key/value pair of the dictionary, rather than being represented as a 'leading' comment preceding the second item.

Is there any way to preserve the comment that precedes the next item even when removing the last key in the dict?


回答1:


Comments are associated with the last collection node parsed, based on the key of a mapping or the the index of a sequence. Your comments "before", are actually end-of-line comments following the last key-value pair of the first sequence item, extending over multiple lines.

If you print the comment attribute of the dict like object (a CommentedMap), using print(parsed['items'][0].ca), you'll get:

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

So if you delete the key color from the CommentedMap, dumping parsed will no longer output the comment, as there is no automatic mechanism to associate the comment with another key or some higher up node.

You can "move" that comment by keeping track of the previous key in your loop:

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

and that requires you to keep track of the previous key:

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)

which gives:

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

A few notes:

  • moving the comment to the parent (sequence/list/CommentedSeq) is possible, but not as trivial

  • there is no need to mix the old API (ruamel.yaml.round_trip_load()) with the new (yaml=YAML())

  • you should get the for loop out of the with statement, the file can be closed after round_trip_load(doc) (or yaml = ruamel.yaml.YAML(); yaml.load(doc) )

  • the officially recommended extension for YAML files has been .yaml for close to 13 years

And make sure you have a test case and/or pin the ruamel.yaml version you are using (ruamel.yaml<0.17) as such comment trickery is not guaranteed to keep working the same way in future versions.



来源:https://stackoverflow.com/questions/57582926/preserving-following-comments-when-removing-last-dict-key-with-ruamel-yaml

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!