如何解决ruamel.yaml:将注释固定到下一个数据项而不是前一个数据项
我在使用 ruamel.yaml 和往返加载器时观察到了一个有点令人困惑的行为。 这可能是因为 ruamel.yaml 自动确定评论应该连接到哪个数据项并非易事。
在下面的例子中,我想一直保留评论。如果我告诉 ruamel.yaml 它应该考虑与下一个数据项相关的所有评论(即评论在“其他”之前),这应该是可能的。
这能做到吗?
如果是:如何?
data_was_a_dict = """\
---
data: was_a_dict
main_dict:
data:
some: data
# this comment gets always lost
other: data
"""
data_was_a_str = """\
---
data: was_a_str
main_dict:
data: a_string
# this gets shifted or stays
other: data
"""
import ruamel.yaml,sys
yaml = ruamel.yaml.YAML()
for text in [data_was_a_dict,data_was_a_str]:
for new_data in ["new_text",{"something": "else"}]:
data = yaml.load(text)
data["main_dict"]["data"] = new_data
yaml.dump(data,sys.stdout)
print("==========================")
输出:
data: was_a_dict
main_dict:
data: new_text
other: data
==========================
data: was_a_dict
main_dict:
data:
something: else
other: data
==========================
data: was_a_str
main_dict:
data: new_text
# this gets shifted or stays
other: data
==========================
data: was_a_str
main_dict:
data:
# this gets shifted or stays
something: else
other: data
==========================
==========================================
感谢 Anthon 的更新:
def replace_dict(target,key,new_dict):
def get_last_key(dct):
keys = [key for key in dct.keys()]
return keys[-1]
old_dict = target[key]
if old_dict and new_dict:
# if new_dict is empty,we will lose the comment.
# That's fine for Now since this should not happen in my case and I don't kNow yet where to attach
# the comment in that case
last_comment = old_dict.ca.items.get(get_last_key(old_dict),None)
if last_comment:
actual_comment = last_comment[2]
actual_comment.value = clean_comment(actual_comment.value)
if actual_comment.value:
if not isinstance(new_dict,ruamel.yaml.comments.CommentedMap):
new_dict = ruamel.yaml.comments.CommentedMap(new_dict)
new_dict.ca.items[get_last_key(new_dict)] = last_comment
target[key] = new_dict
def clean_comment(txt: str) -> str:
_,_,after = txt.partition("\n")
if after:
return "\n" + after
return ""
data_was_a_dict = """\
---
main_dict:
place: holder
sub_dict: # this stays
item1: value1
item2: value2 # this is removed
# this stays
other: data
"""
import ruamel.yaml,sys
import json
yaml = ruamel.yaml.YAML()
data = yaml.load(data_was_a_dict)
replace_dict(data["main_dict"],"sub_dict",{"item_new": "value_new"})
yaml.dump(data,sys.stdout)
给予
main_dict:
place: holder
sub_dict: # this stays
item_new: value_new
# this stays
other: data
解决方法
我不确定以下关于保留评论的 documented 行为有何令人困惑:
除非您严重改变组件的结构,否则这种保存通常不会被破坏 (删除字典中的一个键,删除列表条目)。重新分配值或替换列表项等都可以。
在您转储的四种组合中的三种中,您首先替换了一个 简单值乘以复合值,否则删除包含的复合值 一共评论信息。
在当前所有版本(即 "\n\n# this gets shifted or stays" 的值的扩展行尾注释,因为它以换行符开头,这意味着没有实际的 在与之关联的键的行尾添加注释..
在您的 data_was_a_dict
中,与键 some
关联的注释以及您是否替换
CommentedMap
(dict
子类型,带有对
它的 .ca
属性)与字符串或字典没有区别,因为带有注释的数据结构被完全替换。
在您的 data_was_a_str
YAML 文档中,它与键 data
相关联
在“CommentedMap
的级别高于其他文档”。如果你更换
它的值与另一个字符串的输出将类似于输入。如果你
添加一个全新的子结构,注释被解释为介于
键及其(复合)值。
要获得您所期望的结果,您必须检查是否有与
键 data
并将其移动到与键 something
相关联,这可以
不是普通 dict
上的键(它必须是 CommentedMap
)。
在删除/覆盖附加注释的数据结构的组合中,您必须检查注释并在删除之前移动它。在用复合值替换简单值的组合中,您可以在赋值之后移动注释(给定合适的复合值,如 CommentedMap
)。所以是的,您想要的是可能的,但并非微不足道,这些将依赖于未记录的功能,这些功能将在即将推出的版本中发生变化。
import sys
import ruamel.yaml
data_was_a_dict = """\
data: was_a_dict
main_dict:
data:
some: data
# this comment gets always lost
other: data
"""
data_was_a_str = """\
data: was_a_str
main_dict:
data: a_string
# this gets shifted or stays
other: data
"""
yaml = ruamel.yaml.YAML()
data = yaml.load(data_was_a_dict)
print(data['main_dict']['data'].ca)
data = yaml.load(data_was_a_str)
print(data['main_dict'].ca)
给出:
Comment(comment=None,items={'some': [None,None,CommentToken('\n\n# this comment gets always lost\n',line: 5,col: 0),None]})
Comment(comment=None,items={'data': [None,CommentToken('\n\n# this gets shifted or stays\n',line: 4,None]})
我正在考虑替换 ruamel.yaml 的评论扫描和附件,这将允许 用户可选择拆分(在第一个空行上),允许用户 指定将评论信息附加到前面和/或后面的“数据”。 分配可能会受到缩进级别的影响。文档 甚至可能会更新以反映往返将支持更少 重组数据时保留评论的严格限制。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。