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



我正在尝试使用ruamel.yamlPython库从大型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是第一个字典中的最后一个键/值对,则第二个项前面的注释将被吃掉:

▶ 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

看起来注释被附加到字典的最后一个键/值对,而不是表示为第二项之前的"前导"注释。

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

注释与最后一个解析的集合节点相关联,基于 映射的键或序列的索引。您的意见 "之前",实际上是最后一个之后的行尾注释 第一个序列项的键值对,跨多个序列项扩展 线。

如果您使用print(parsed['items'][0].ca)打印类似字典的对象(CommentedMap(的注释属性,您将获得:

items={'color': [None, None, CommentToken("nn  # This is a comment above 'c'n  # More commentn", 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

一些注意事项:

  • 将注释移动到父项(序列/列表/注释序列(是 可能,但不是微不足道的

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

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

  • 官方推荐的 外延 对于 YAML 文件已经.yaml了近 13 年

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

最新更新