如何使用 ruamel.yaml 在 Python 中向 YAML 插入注释行?
How to insert a comment line to YAML in Python using ruamel.yaml?
我有这样的结构,我想使用 ruamel.yaml
:
添加注释行
xyz:
a: 1 # comment 1
b: 2
test1:
test2:
test3: 3
现在,我想插入注释行(不是 eol_comments),使其看起来像这样:
xyz:
a: 1 # comment 1
b: 2
# before test1 (top level)
test1:
# before test2
test2:
# after test2
test3: 3
我知道,我可以使用 ruamel.yaml
添加 eol_comments,但我没有找到添加整个注释行的方法。
ruamel.yaml<=0.12.18
中确实没有在键前行插入注释的功能,但是有在结构开头设置注释的功能:.yaml_set_start_comment
。这样您就可以设置要添加的三个评论中的两个:
import sys
import ruamel.yaml
yaml_str = """\
xyz:
a: 1 # comment 1
b: 2
test1:
test2:
test3: 3
"""
data = ruamel.yaml.round_trip_load(yaml_str)
data['test1'].yaml_set_start_comment('before test2', indent=2)
data['test1']['test2'].yaml_set_start_comment('after test2', indent=4)
ruamel.yaml.round_trip_dump(data, sys.stdout)
给出:
xyz:
a: 1 # comment 1
b: 2
test1:
# before test2
test2:
# after test2
test3: 3
实际上有一个 "comment" 由 xyz
和 test1
的值之间的空行构成,但是如果您将注释附加到该结构然后插入一个新键在 test1
之前,事情没有如您所愿地出现。因此,要做的是在键 test1
之前明确插入注释。您可以往返加载您的预期输出以查看内部 Comment
应该是什么样子:
yaml_str_out = """\
xyz:
a: 1 # comment 1
b: 2
# before test1 (top level)
test1:
# before test2
test2:
# before test3
test3: 3
"""
test = ruamel.yaml.round_trip_load(yaml_str_out)
print(test.ca)
给出(将其包装起来以便于查看):
Comment(comment=None,
items={'test1': [None,
[CommentToken(value='# before test1 (top level)\n')],
None,
[CommentToken(value='# before test2\n')]]})
如你所见,# before test2
被认为是键后的注释。
做 test['test1'].yaml_set_start_comment('xxxxx', indent=2)
会
没有任何影响,因为与 test1
相关的评论否决了它并且 # xxxxx
将 不会 出现在转储中。
根据这些信息和一些背景知识,我改编了一些来自 yaml_set_start_comment()
的代码(假设原始导入和 yaml_str
):
def yscbak(self, key, before=None, indent=0, after=None, after_indent=None):
"""
expects comment (before/after) to be without `#` and possible have multiple lines
"""
from ruamel.yaml.error import Mark
from ruamel.yaml.tokens import CommentToken
def comment_token(s, mark):
# handle empty lines as having no comment
return CommentToken(('# ' if s else '') + s + '\n', mark, None)
if after_indent is None:
after_indent = indent + 2
if before and before[-1] == '\n':
before = before[:-1] # strip final newline if there
if after and after[-1] == '\n':
after = after[:-1] # strip final newline if there
start_mark = Mark(None, None, None, indent, None, None)
c = self.ca.items.setdefault(key, [None, [], None, None])
if before:
for com in before.split('\n'):
c[1].append(comment_token(com, start_mark))
if after:
start_mark = Mark(None, None, None, after_indent, None, None)
if c[3] is None:
c[3] = []
for com in after.split('\n'):
c[3].append(comment_token(com, start_mark))
if not hasattr(ruamel.yaml.comments.CommentedMap,
'yaml_set_comment_before_after_key'):
ruamel.yaml.comments.CommentedMap.yaml_set_comment_before_after_key = yscbak
data = ruamel.yaml.round_trip_load(yaml_str)
data.yaml_set_comment_before_after_key('test1', 'before test1 (top level)',
after='before test2', after_indent=2)
data['test1']['test2'].yaml_set_start_comment('after test2', indent=4)
ruamel.yaml.round_trip_dump(data, sys.stdout)
并得到:
xyz:
a: 1 # comment 1
b: 2
# before test1 (top level)
test1:
# before test2
test2:
# after test2
test3: 3
使用 hasattr
的测试是为了确保在添加到 ruamel.yaml
时不会覆盖这样的函数
顺便说一句:所有评论都是 YAML 中的行尾评论,其中一些评论之前可能只是有效的 YAML。
我有这样的结构,我想使用 ruamel.yaml
:
xyz:
a: 1 # comment 1
b: 2
test1:
test2:
test3: 3
现在,我想插入注释行(不是 eol_comments),使其看起来像这样:
xyz:
a: 1 # comment 1
b: 2
# before test1 (top level)
test1:
# before test2
test2:
# after test2
test3: 3
我知道,我可以使用 ruamel.yaml
添加 eol_comments,但我没有找到添加整个注释行的方法。
ruamel.yaml<=0.12.18
中确实没有在键前行插入注释的功能,但是有在结构开头设置注释的功能:.yaml_set_start_comment
。这样您就可以设置要添加的三个评论中的两个:
import sys
import ruamel.yaml
yaml_str = """\
xyz:
a: 1 # comment 1
b: 2
test1:
test2:
test3: 3
"""
data = ruamel.yaml.round_trip_load(yaml_str)
data['test1'].yaml_set_start_comment('before test2', indent=2)
data['test1']['test2'].yaml_set_start_comment('after test2', indent=4)
ruamel.yaml.round_trip_dump(data, sys.stdout)
给出:
xyz:
a: 1 # comment 1
b: 2
test1:
# before test2
test2:
# after test2
test3: 3
实际上有一个 "comment" 由 xyz
和 test1
的值之间的空行构成,但是如果您将注释附加到该结构然后插入一个新键在 test1
之前,事情没有如您所愿地出现。因此,要做的是在键 test1
之前明确插入注释。您可以往返加载您的预期输出以查看内部 Comment
应该是什么样子:
yaml_str_out = """\
xyz:
a: 1 # comment 1
b: 2
# before test1 (top level)
test1:
# before test2
test2:
# before test3
test3: 3
"""
test = ruamel.yaml.round_trip_load(yaml_str_out)
print(test.ca)
给出(将其包装起来以便于查看):
Comment(comment=None,
items={'test1': [None,
[CommentToken(value='# before test1 (top level)\n')],
None,
[CommentToken(value='# before test2\n')]]})
如你所见,# before test2
被认为是键后的注释。
做 test['test1'].yaml_set_start_comment('xxxxx', indent=2)
会
没有任何影响,因为与 test1
相关的评论否决了它并且 # xxxxx
将 不会 出现在转储中。
根据这些信息和一些背景知识,我改编了一些来自 yaml_set_start_comment()
的代码(假设原始导入和 yaml_str
):
def yscbak(self, key, before=None, indent=0, after=None, after_indent=None):
"""
expects comment (before/after) to be without `#` and possible have multiple lines
"""
from ruamel.yaml.error import Mark
from ruamel.yaml.tokens import CommentToken
def comment_token(s, mark):
# handle empty lines as having no comment
return CommentToken(('# ' if s else '') + s + '\n', mark, None)
if after_indent is None:
after_indent = indent + 2
if before and before[-1] == '\n':
before = before[:-1] # strip final newline if there
if after and after[-1] == '\n':
after = after[:-1] # strip final newline if there
start_mark = Mark(None, None, None, indent, None, None)
c = self.ca.items.setdefault(key, [None, [], None, None])
if before:
for com in before.split('\n'):
c[1].append(comment_token(com, start_mark))
if after:
start_mark = Mark(None, None, None, after_indent, None, None)
if c[3] is None:
c[3] = []
for com in after.split('\n'):
c[3].append(comment_token(com, start_mark))
if not hasattr(ruamel.yaml.comments.CommentedMap,
'yaml_set_comment_before_after_key'):
ruamel.yaml.comments.CommentedMap.yaml_set_comment_before_after_key = yscbak
data = ruamel.yaml.round_trip_load(yaml_str)
data.yaml_set_comment_before_after_key('test1', 'before test1 (top level)',
after='before test2', after_indent=2)
data['test1']['test2'].yaml_set_start_comment('after test2', indent=4)
ruamel.yaml.round_trip_dump(data, sys.stdout)
并得到:
xyz:
a: 1 # comment 1
b: 2
# before test1 (top level)
test1:
# before test2
test2:
# after test2
test3: 3
使用 hasattr
的测试是为了确保在添加到 ruamel.yaml
顺便说一句:所有评论都是 YAML 中的行尾评论,其中一些评论之前可能只是有效的 YAML。