yaml 锚定义在 PyYAML 中加载

yaml anchors definitions loading in PyYAML

我正在使用 PyYAML。 有没有一种方法可以定义 YAML 锚点,它不会成为 yaml.load 加载的数据结构的一部分(我可以从字典中删除 "wifi_parm" 但寻找更聪明的方法)?

example.yaml:

wifi_parm: &wifi_params
  ssid: 1
  key: 2
test1:
  name: connectivity
  <<: *wifi_params
test2:
  name: connectivity_5ghz
  <<: *wifi_params

load_example.py:

import yaml
import pprint

with open('aaa.yaml', 'r') as f:
    result = yaml.load(f)
pprint.pprint(result)

打印:

{'test1': {'key': 2, 'name': 'connectivity', 'ssid': 1},
 'test2': {'key': 2, 'name': 'connectivity_5ghz', 'ssid': 1},
 'wifi_parm': {'key': 2, 'ssid': 1}}

我需要:

{'test1': {'key': 2, 'name': 'connectivity', 'ssid': 1},
 'test2': {'key': 2, 'name': 'connectivity_5ghz', 'ssid': 1}}

PyYAML 中的锚点信息在您从 yaml.load() 获得结果之前被丢弃。这是根据 PyYAML 遵循的 YAML 1.1 规范(... 锚点名称是序列化细节,一旦组合完成就会被丢弃 )。这在 YAML 1.2 规范(自 2009 年起)中没有改变。在 PyYAML 中,您不能通过遍历 result(递归地)并测试哪些值可能是锚点来执行此操作,而无需大量修改解析器。

在我的 ruamel.yaml(即 YAML 1.2)中,我保留了锚点和别名 对于实际用于别名映射或序列的锚点 (目前不为标量保留锚别名,"unused" 锚也不保留):

import ruamel.yaml

with open('aaa.yaml') as f:
    result = ruamel.yaml.round_trip_load(f)

ruamel.yaml.round_trip_dump(result, sys.stdout)

给出:

wifi_parm: &wifi_params
  ssid: 1
  key: 2
test1:
  <<: *wifi_params
  name: connectivity
test2:
  <<: *wifi_params
  name: connectivity_5ghz

您实际上可以遍历映射(或递归树)并找到锚节点并将其删除,而无需知道键名。

import ruamel.yaml
from ruamel.yaml.comments import merge_attrib

with open('aaa.yaml') as f:
    result = ruamel.yaml.round_trip_load(f)

keys_to_delete = []
for k in result:
    v = result[k]
    if v.yaml_anchor():
        keys_to_delete.append(k)
    for merge_data in v.merge:  # update the dict with the merge data 
        v.update(merge_data[1])
        delattr(v, merge_attrib)
for k in keys_to_delete:
    del result[k]

ruamel.yaml.round_trip_dump(result, sys.stdout)

给出:

test1:
  name: connectivity
  ssid: 1
  key: 2
test2:
  name: connectivity_5ghz
  ssid: 1
  key: 2

也可以通用和递归地执行此操作(即对于树中任何位置的锚点和别名)。更新会像上面一样简单,但是你需要跟踪如何删除一个键,这不一定是一个映射值,它可以是一个序列项。

我今天也想这样做,而不是像@Anthon 建议的那样切换到 ruamel.yaml,而是找到了 pyyaml-keep-anchors 存储库,这让我可以继续使用 pyyaml。这是该存储库中的示例,对我来说开箱即用。

import yaml
from yaml_keep_anchors.yaml_anchor_parser import AliasResolverYamlLoader

with open('example/example.yaml', 'r') as fh:
    data = yaml.load(fh, Loader=AliasResolverYamlLoader)

assert data['key_three'].anchor_name == 'anchor'
assert data['key_two']['sub_key'].anchor_name == 'anchor_val'