原则上是可以实现的,因为您可以往返这样的“文件开头”注释,但在当前的ruamel.yaml 0.10中不支持得很好,特别是从头开始(即不改变现有文件)。下面是一个简单而相对不错的解决方案,但我想先介绍一个丑陋的解决方法以及逐步完成此操作的步骤。
丑陋的方式:
要做到这一点的丑陋方法是,在将YAML数据写入文件之前,只需将注释添加到文件中。也就是说,插入:
f.write('
在ruamel.yaml.dump(...)
之前:
步骤:
为了在数据结构中插入注释,以避免上述的hack方法,您首先需要确保您的d
数据是CommentedMap
类型。如果您将带有注释的YAML加载回c
后,将d
变量与具有注释的变量进行比较,您会发现它们之间的差异。
import ruamel.yaml
from ruamel.yaml.comments import Comment, CommentedSeq, CommentedMap
d = CommentedMap()
for m in ['B1', 'B2', 'B3']:
d2 = {}
for f in ['A1', 'A2', 'A3']:
d2[f] = CommentedSeq(['test', 'test2'])
if f != 'A2':
d2[f].fa.set_flow_style()
d[m] = d2
yaml_str = ruamel.yaml.dump(d, Dumper=ruamel.yaml.RoundTripDumper,
default_flow_style=False, width=50, indent=8)
assert not hasattr(d, Comment.attrib)
comment = 'Data for Class A'
commented_yaml_str = '# ' + comment + '\n' + yaml_str
c = ruamel.yaml.load(commented_yaml_str, Loader=ruamel.yaml.RoundTripLoader)
assert hasattr(c, Comment.attrib)
print c.ca
print d.ca
assert hasattr(d, Comment.attrib)
这将打印:
Comment(comment=[None, [CommentToken(value=u'# Data for Class A\n')]],
items={})
Comment(comment=None,
items={})
一个Comment
有一个属性comment
,需要设置为一个由EOL注释(始终只有一个)和前面行注释的列表(以CommentTokens
的形式)组成的2个元素列表。
要创建一个CommentToken
,你需要一个(虚假的)起始标记,告诉它从哪一列开始:
from ruamel.yaml.error import StreamMark
start_mark = StreamMark(None, None, None, 0, None, None) # column 0
现在您可以创建令牌:
from ruamel.yaml.tokens import CommentToken
ct = CommentToken('# ' + comment + '\n', start_mark, None)
在您的CommentedMap中,将令牌分配为前一个列表的第一个元素:
d.ca.comment = [None, [ct]]
print d.ca
给你:
Comment(comment=[None, [CommentToken(value='# Data for Class A\n')]],
items={})
最后:
print ruamel.yaml.dump(d, Dumper=ruamel.yaml.RoundTripDumper)
gives:
B1:
A1: [test, test2]
A3: [test, test2]
A2:
- test
- test2
B2:
A1: [test, test2]
A3: [test, test2]
A2:
- test
- test2
B3:
A1: [test, test2]
A3: [test, test2]
A2:
- test
- test2
当然,你不需要创建c
对象,这只是为了说明。
应该使用什么:为了使整个练习变得更容易,您可以忘记细节,并将以下方法补丁到CommentedBase
中:
from ruamel.yaml.comments import CommentedBase
def set_start_comment(self, comment, indent=0):
"""overwrites any preceding comment lines on an object
expects comment to be without `#` and possible have mutlple lines
"""
from ruamel.yaml.error import StreamMark
from ruamel.yaml.tokens import CommentToken
if self.ca.comment is None:
pre_comments = []
self.ca.comment = [None, pre_comments]
else:
pre_comments = self.ca.comments[1]
if comment[-1] == '\n':
comment = comment[:-1]
start_mark = StreamMark(None, None, None, indent, None, None)
for com in comment.split('\n'):
pre_comments.append(CommentToken('# ' + com + '\n', start_mark, None))
if not hasattr(CommentedBase, 'set_start_comment'):
CommentedBase.set_start_comment = set_start_comment
然后只需要执行:
d.set_start_comment('Data for Class A')