Python 向使用PyYaml生成的YAML添加注释

Python 向使用PyYaml生成的YAML添加注释,python,yaml,pyyaml,Python,Yaml,Pyyaml,我正在使用PyYaml从我自己的python对象创建Yaml文档。 例如,我的对象: 变成: 到目前为止还不错 但我还没有找到一种方法,可以通过编程方式将注释添加到生成的yaml中,使其看起来像: boby: # this is the name age: 34 # in years 查看PyYaml文档和代码,我没有找到这样做的方法 有什么建议吗?您可能对MyObj类有一些representer,因为默认情况下,使用PyYAML转储(print(yaml.dump(M

我正在使用PyYaml从我自己的python对象创建Yaml文档。 例如,我的对象:

变成:

到目前为止还不错

但我还没有找到一种方法,可以通过编程方式将注释添加到生成的yaml中,使其看起来像:

boby:       # this is the name
   age: 34  # in years
查看PyYaml文档和代码,我没有找到这样做的方法


有什么建议吗?

您可能对MyObj类有一些representer,因为默认情况下,使用PyYAML转储(
print(yaml.dump(MyObj()))
)将为您提供:

!!python/object:__main__.MyObj {}
PyYAML只能对所需输出中的注释做一件事:丢弃它们。如果您想读回所需的输出,则结束 如果一个dict包含一个dict(
{'boby':{'age':34}
,您将不会得到一个
MyObj()
实例,因为没有标记信息)

我开发的PyYAML的增强版本()可以在YAML中读取注释,保存注释,并在转储时编写注释。 如果读取所需的输出,结果数据将看起来(和行为)像包含dict的dict,但实际上有更复杂的数据结构可以处理注释。但是,当ruamel.yaml要求您转储
MyObj
的实例时,您可以创建该结构,如果您当时添加注释,您将获得所需的输出

from __future__ import print_function

import sys
import ruamel.yaml
from ruamel.yaml.comments import CommentedMap


class MyObj():
    name = "boby"
    age = 34

    def convert_to_yaml_struct(self):
        x = CommentedMap()
        a = CommentedMap()
        x[data.name] = a
        x.yaml_add_eol_comment('this is the name', 'boby', 11)
        a['age'] = data.age
        a.yaml_add_eol_comment('in years', 'age', 11)
        return x

    @staticmethod
    def yaml_representer(dumper, data, flow_style=False):
        assert isinstance(dumper, ruamel.yaml.RoundTripDumper)
        return dumper.represent_dict(data.convert_to_yaml_struct())


ruamel.yaml.RoundTripDumper.add_representer(MyObj, MyObj.yaml_representer)

ruamel.yaml.round_trip_dump(MyObj(), sys.stdout)
其中打印:

boby:      # this is the name
  age: 34  # in years

无需等待创建
CommentedMap
实例,直到您想要表示
MyObj
实例。例如,我会将
name
age
制作成属性,这些属性从相应的
CommentedMap
获取/设置值。这样,在调用
yaml\u representer
静态方法来表示
MyObj
实例之前,您可以更轻松地添加注释;它有点复杂,但没有ruamel复杂,因为它完全与普通的pyyamlapi一起工作,并且没有往返注释(因此它不是一个合适的答案)。总体而言,它可能还没有那么健壮,因为我还没有进行过广泛的测试,但对于我的用例来说,它似乎已经足够好了,即我希望dicts/mappings能够有注释,无论是对于整个映射,还是对于每项注释

我相信,在这种有限的上下文中,使用类似的方法也可以进行往返评论,但我没有尝试过,因为目前我还没有使用过这种方法

最后,虽然这个解决方案没有实现向列表/序列中的项目添加每个项目的注释(因为这不是我目前需要的),但它可以很容易地进行扩展

首先,与ruamel一样,我们需要一种
CommentedMapping
类,它将注释与。有很多可能的方法;我的只是其中之一:

from collections.abc导入映射,可变映射
类CommentedMapping(可变映射):
def uu init uu(self,d,comment=None,comments={}):
self.mapping=d
self.comment=注释
self.comments=注释
def get_注释(自我,*路径):
如果不是路径:
返回self.comment
#在self中查找键(递归),并提出一个
#KeyError或其他执行选项(如果该键不存在)
#存在于嵌套结构中
sub=自映射
对于路径中的p:
如果isinstance(sub,CommentedMapping):
#颠覆注释复制
sub=sub.mapping[p]
其他:
sub=sub[p]
注释=无
如果len(路径)==1:
comment=self.comments.get(路径[0])
如果注释为“无”:
comment=self.comments.get(路径)
回复评论
定义获取项目(自身,项目):
val=self.mapping[项目]
if(isinstance(val,(dict,Mapping))和
不存在(val,CommentedMapping)):
注释=自我。获取注释(项目)
comments={k[1::]:v代表k,v在self.comments.items()中
如果isinstance(k,tuple)和len(k)>1和k[0]==item}
val=self.\uuuu类(val,comment=comment,comments=comments)
返回值
定义设置项(自身、项、值):
self.mapping[item]=值
定义项目(自身,项目):
del self.mapping[项目]
对于列表中的k(自我评论):
如果k==项或(i实例(k,元组)和k以及k[0]==项):
del self.comments[关键字]
定义(自我):
返回iter(自映射)
定义(自我):
返回len(自映射)
定义报告(自我):
返回f'{type(self)。{u name}({self.mapping},comment={self.comment!r},comments={self.comments})'
这个类既有一个
.comment
属性,以便它可以携带映射的总体注释,也有一个
.comments
属性,其中包含每个键的注释。它还允许通过将键路径指定为元组,为嵌套dict中的键添加注释。例如,
comments={('c','d'):'comment'}
允许在
'c'
处为嵌套dict中的键
'd'
指定注释。从
CommentedMapping
获取项目时,如果项目的值是dict/Mapping,则它也会以保留其注释的方式包装在
CommentedMapping
中。这对于递归调用嵌套结构的YAML representer非常有用

接下来,我们需要实现一个自定义YAML,它负责将对象序列化到YAML的整个过程。转储程序是一个复杂的类,由其他四个类组成:an、A、A和A。其中,我们只需实施前三项
Resolver
s更关心的是,例如像
1
这样的隐式标量如何解析为正确的类型,以及确定各种值的默认标记。这里没有涉及到这件事

冷杉
from __future__ import print_function

import sys
import ruamel.yaml
from ruamel.yaml.comments import CommentedMap


class MyObj():
    name = "boby"
    age = 34

    def convert_to_yaml_struct(self):
        x = CommentedMap()
        a = CommentedMap()
        x[data.name] = a
        x.yaml_add_eol_comment('this is the name', 'boby', 11)
        a['age'] = data.age
        a.yaml_add_eol_comment('in years', 'age', 11)
        return x

    @staticmethod
    def yaml_representer(dumper, data, flow_style=False):
        assert isinstance(dumper, ruamel.yaml.RoundTripDumper)
        return dumper.represent_dict(data.convert_to_yaml_struct())


ruamel.yaml.RoundTripDumper.add_representer(MyObj, MyObj.yaml_representer)

ruamel.yaml.round_trip_dump(MyObj(), sys.stdout)
boby:      # this is the name
  age: 34  # in years
>>> import yaml
>>> d = CommentedMapping({
...     'a': 1,
...     'b': 2,
...     'c': {'d': 3},
... }, comment='my commented dict', comments={
...     'a': 'a comment',
...     'b': 'b comment',
...     'c': 'long string ' * 44,
...     ('c', 'd'): 'd comment'
... })
>>> print(yaml.dump(d, Dumper=CommentedDumper))
# my commented dict
# a comment
a: 1
# b comment
b: 2
# long string long string long string long string long string long string long
# string long string long string long string long string long string long string
# long string long string long string long string long string long string long
# string long string long string long string long string long string long string
# long string long string long string long string long string long string long
# string long string long string long string long string long string long string
# long string long string long string long string long string
c:
  # d comment
  d: 3