在使用 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 不会创建正常 Python dicts/lists 来自 YAML mappings/sequences,但其子类 (CommentedMap/CommentedSeq) 并附上由前一个索引的评论 这些容器中的元素。同时,像 __get__() 这样的 dunder 方法 允许(大多数)正常使用这些容器以使用和/或修改它们 你的程序,然后转储它们。

ruamel.yaml 对字符串、整数、浮点数进行子类化(并且在某种程度上 布尔值)以保留引号、指数、基数、任何 YAML 中可能出现的锚点等。但如果评论是 附加到标量,而不是它作为值的容器或 元素,将导致在分配新值时丢失该注释。那 如果你有 YAML:

a: 18  # soon to be 55
b: 42

将其加载到 data 并执行 data['a'] = 55 您的评论将会丢失。它是 不确定是否可以通过制作容器来改进这种行为 更聪明,值得研究,但前提是这样的标量是 mapping/sequence.

的一部分

除此之外None不能被子类化,所以没有地方附加 注释。布尔值也不能被子类化,但要保留锚 ruamel.yaml 将布尔值构造为 int 的子类,它允许正常使用,例如在 if 语句测试真值。 None 的典型用法 但是正在测试身份(使用 `... is None``)并且据我所知,没有办法伪造它。

所以 .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. 添加更完整的实现