繁体   English   中英

使用 ruamel.yaml 格式化时保留仅带有注释的 YAML 文件?

[英]Preserve YAML files with only comments when formatting using ruamel.yaml?

我想在只有评论的 YAML 文件中保留评论。 使用我当前的设置, ruamel.yaml 在格式化这样的文件时输出 null。 有没有好的方法可以做到这一点? 这是我到目前为止所拥有的:

from ruamel.yaml import YAML

def round_trip(sout, sin, idt):
    yaml = YAML()
    assert idt >= 2
    yaml.indent(mapping=idt, sequence=idt, offset=idt-2)
    yaml.preserve_quotes = True

    data = yaml.load(sin)
    if data is not None:
        yaml.dump(data, sout)
    else:
        print("the file is empty") # needs fixing: should dump original file

注释不会保留,因为您的实例data上没有放置它们的位置。 在往返模式下ruamel.yaml不会从 YAML 映射/序列创建普通的 Python 字典/列表,而是创建其子类( CommentedMap / CommentedSeq )并附加由这些容器中的前一个元素索引的注释。 同时,像__get__()这样的 dunder 方法允许(大多数)正常使用这些容器在您的程序中使用和/或修改它们,然后转储它们。

ruamel.yaml对字符串、整数、浮点数(以及某些扩展的布尔值)进行子类化,以保留 YAML 中可能出现的引号、指数、基数、任何锚点等信息。 但是如果将注释附加到标量,而不是它作为值或元素的容器,将导致在分配新值时丢失该注释。 也就是说,如果您有 YAML:

a: 18  # soon to be 55
b: 42

将其加载到data并执行data['a'] = 55您的评论将丢失。 不确定是否可以通过使容器更智能来改进这种行为,这值得研究,但前提是这样的标量是映射/序列的一部分。

除此之外None不能被子类化,所以没有地方附加评论。 Booleans 也不能被子类化,但为了保留锚点ruamel.yamlruamel.yaml构造为int的子类,这允许正常使用,例如在if语句中测试真值。 然而None的典型用法是测试身份(使用 `... is None`),而 AFAIK 没有办法伪造它。

所以.load()没有办法给你一些带有评论信息的东西。 但是您确实使用了YAML()实例,并且 IMO 最好对其进行子类化以保留评论信息。 它当前存储有关上次加载的文档的一些信息,例如文档 YAML 版本指令(如果提供)( %YAML 1.1

import sys
import ruamel.yaml

yaml_str = """\
# this document is, by default,
# round-tripped to null
"""

class YAML(ruamel.yaml.YAML):
    def load(self, stream):
        if not hasattr(stream, 'read') and hasattr(stream, 'open'):
            # pathlib.Path() instance
            data = super().load(stream)
            if data is None:
                buf = stream.read_text()
        elif isinstance(stream, str):
            data = super().load(stream)
            buf = stream
        else:  # buffer stream data
             buf = stream.read()
             data = super().load(buf)
        if data is None and buf.strip():
             self._empty_commented_doc = buf
        return data

    def dump(self, data, stream=None, transform=None):
        # dump to stream or Path
        if not hasattr(self, '_empty_commented_doc'):  # the simple case
            return super().dump(data, stream=stream, transform=transform)
        # doesn't handle transform
        if not hasattr(stream, 'read') and hasattr(stream, 'open'):
            with stream.open('w') as fp:
                fp.write(self._empty_commented_doc)
                super().dump(data, stream)
        else:
            stream.write(self._empty_commented_doc)
            if data is not None:
                super().dump(data, stream)


yaml = YAML()
# yaml.indent(mapping=4, sequence=4, offset=2)
# yaml.preserve_quotes = True
data = yaml.load(yaml_str)
yaml.dump(data, sys.stdout)
data = True
print('----------------')
yaml.dump(data, sys.stdout)

这使:

# this document is, by default,
# round-tripped to null
----------------
# this document is, by default,
# round-tripped to null
true
...

以上内容也可以扩展到处理根级标量文档,我正在考虑向 ruamel.yaml 添加一个更完整的实现。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM