使用 Ruamel 插入密钥

Insert a key using Ruamel

我正在使用 Ruamel Python 库以编程方式编辑人工编辑的 YAML 文件。源文件具有按字母顺序排序的键。

我不确定这是一个基本的 Python 问题,还是一个 Ruamel 问题,但我尝试对 Ruamel 的 OrderedDict 结构进行排序的所有方法对我来说都失败了。

我很困惑,例如,为什么以下基于 this recipe 的代码不起作用:

import ruamel.yaml
import collections

def read_file(f):
    with open(f, 'r') as _f:
        return ruamel.yaml.round_trip_load(
            _f.read(),
            preserve_quotes=True
        )

def write_file(f, data):
    with open(f, 'w') as _f:
        _f.write(ruamel.yaml.dump(
            data,
            Dumper=ruamel.yaml.RoundTripDumper,
            explicit_start=True,
            width=1024
        ))

data = read_file('in.yaml')
data = collections.OrderedDict(sorted(data.items(), key=lambda t: t[0]))
write_file('out.yaml', data)

但是给出这个输入文件:

---
bananas: 1
apples: 2

生成以下输出文件:

--- !!omap
- apples: 2
- bananas: 1

即它把我的文件变成了 YAML 有序地图。

有没有简单的方法来做到这一点?另外,我可以简单地以某种方式插入到数据结构中吗?

如果您 round_trip ruamel.yaml¹ 中的映射,则映射不会表示为 collections.OrderedDict(),而是表示为 ruamel.yaml.comments.CommentedMap()。后者可以是 collections.OrderedDict() 的子类,具体取决于您使用的 Python 版本(例如,在 Python 2 中,它使用来自 ruamel.ordereddict 的更快的 C 实现)

表示器不会自动将 "normal" 有序字典(无论是来自 collections 还是 ruamel.ordereddict)解释为特殊 round_trip_dump 模式。但是如果你删除 collections:

import ruamel.yaml

def read_file(f):
    with open(f, 'r') as _f:
        return ruamel.yaml.round_trip_load(
            _f.read(),
            preserve_quotes=True
        )

def write_file(f, data):
    with open(f, 'w') as _f:
        ruamel.yaml.dump(
            data,
            stream=_f,
            Dumper=ruamel.yaml.RoundTripDumper,
            explicit_start=True,
            width=1024
        )

data = read_file('in.yaml')
data = ruamel.yaml.comments.CommentedMap(sorted(data.items(), key=lambda t: t[0]))
write_file('out.yaml', data)

您的 out.yaml 将是:

---
apples: 2
bananas: 1

请注意,我还删除了您 write_file 例程中的一个低效问题。如果你没有指定一个流,所有的数据将首先被流式传输到一个 StringIO 实例(在内存中)然后返回(你用 _f.write() 写入一个流,这样效率更高直接写入流。

至于你的最后一个问题:是的,你可以使用以下方式插入:

data.insert(1, 'apricot', 3)

¹ 免责声明:我是这两本书的作者 ruamel.yaml as well as ruamel.ordereddict