使用 ruamel.yaml 删除最后一个 dict 键时保留以下注释 [英] Preserving following comments when removing last dict key with ruamel.yaml
问题描述
我正在尝试使用 ruamel.yaml
Python 库从大型 YAML 文件中的嵌套字典中删除一些键/值对,同时保留周围的注释.这是我正在使用的代码的简化版本:
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
但是,如果 color: blue
是第一个 dict 中的最后键/值对,则第二项之前的注释会被吃掉:
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?
推荐答案
评论与最后解析的集合节点相关联,基于映射的键或序列的索引.你的评论before",实际上是最后一个后的行尾注释第一个序列项的键值对,扩展到多个线.
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.
如果你使用print(parsed['items'][0].ca)
打印dict类对象(一个CommentedMap
)的comment属性,你会得到:
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]})
所以如果你从 CommentedMap
中删除键 color
,dumping parsed
将不再输出评论,因为没有自动机制将评论与另一个键或某个更高的节点相关联.
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)
给出:
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)是可能,但不是那么简单
moving the comment to the parent (sequence/list/CommentedSeq) is possible, but not as trivial
不需要混合旧的API(ruamel.yaml.round_trip_load()
) 和新的 (yaml=YAML()
)
there is no need to mix the old API
(ruamel.yaml.round_trip_load()
) with the new (yaml=YAML()
)
你应该从 with
语句中取出 for 循环,文件可以在 round_trip_load(doc)
之后关闭(或 yaml = ruamel.yaml.YAML(); yaml.load(doc)
)
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)
)
官方推荐扩展用于 YAML 文件的 .yaml
已近 13 年
the officially recommended
extension
for YAML files has been .yaml
for close to 13 years
并确保您有一个测试用例和/或固定您正在使用的 ruamel.yaml 版本 (ruamel.yaml<0.17
) 因为这样的评论技巧不能保证以同样的方式工作在未来的版本中.
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.
这篇关于使用 ruamel.yaml 删除最后一个 dict 键时保留以下注释的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!