ruamel.yaml:将注释固定到下一个数据项而不是前一个
ruamel.yaml: pin comment to next data item instead of previous one
h我在将 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 保留评论的行为:
This preservation is normally not broken unless you severely alter the structure of a component
(delete a key in a dict, remove list entries). Reassigning values or replacing list items, etc., is fine.
在您转储的四个组合中的三个中,您首先替换了一个
简单值乘以复合值,或者删除包含的复合值
评论信息一并。
在当前的所有版本中(即 <0.18),ruamel.yaml 附上扫描件
评论解析评论时存在的标记。没有
您的下一个数据项的令牌(尚未),因此目前无法附加此
到“下一个数据项”。 ruamel.yaml<0.18中的实际评论信息为
一个扩展的行尾注释,其值类似于 "\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
)。
在您 delete/overwrite 附加评论的数据结构的组合中,您必须检查评论并在删除之前移动它。在将简单值替换为复合值的组合中,您可以在分配后移动注释(给定一个合适的复合值,如 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, None, CommentToken('\n\n# this gets shifted or stays\n', line: 4, col: 0), None]})
我正在考虑更换 ruamel.yaml 的评论扫描和附件,这将允许
用户可选择拆分(在第一个空行),允许用户
指定将评论信息附加到“数据”之后的前面 and/or。
分配可能会受到缩进级别的影响。文档
甚至可能会更新以反映往返将支持更少
重组数据时对保留注释的严格限制。
h我在将 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 保留评论的行为:
This preservation is normally not broken unless you severely alter the structure of a component (delete a key in a dict, remove list entries). Reassigning values or replacing list items, etc., is fine.
在您转储的四个组合中的三个中,您首先替换了一个 简单值乘以复合值,或者删除包含的复合值 评论信息一并。
在当前的所有版本中(即 <0.18),ruamel.yaml 附上扫描件
评论解析评论时存在的标记。没有
您的下一个数据项的令牌(尚未),因此目前无法附加此
到“下一个数据项”。 ruamel.yaml<0.18中的实际评论信息为
一个扩展的行尾注释,其值类似于 "\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
)。
在您 delete/overwrite 附加评论的数据结构的组合中,您必须检查评论并在删除之前移动它。在将简单值替换为复合值的组合中,您可以在分配后移动注释(给定一个合适的复合值,如 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, None, CommentToken('\n\n# this gets shifted or stays\n', line: 4, col: 0), None]})
我正在考虑更换 ruamel.yaml 的评论扫描和附件,这将允许 用户可选择拆分(在第一个空行),允许用户 指定将评论信息附加到“数据”之后的前面 and/or。 分配可能会受到缩进级别的影响。文档 甚至可能会更新以反映往返将支持更少 重组数据时对保留注释的严格限制。