如何在ruamel.yaml中显式编写两个引用

问题描述 投票:2回答:1

如果我有多个引用,当我使用Python中的ruaml.yaml将它们写入YAML文件时,我得到:

  <<: [*name-name, *help-name]

但我宁愿拥有

<<: *name-name
<<: *help-name

在写入文件时是否有实现此目的的选项?

UPDATE

descriptions:
  - &description-one-ref
    description: >

helptexts:
  - &help-one
    help_text: |

questions:
  - &question-one
    title: "title test"
    reference: "question-one-ref"
    field: "ChoiceField"
    choices:
      - "Yes"
      - "No"
    required: true
    <<: *description-one-ref
    <<: *help-one
    riskvalue_max: 10
    calculations:
      - conditions:
          - comparator: "equal"
            value: "Yes"
        actions:
          - riskvalue: 0
      - conditions:
          - comparator: "equal"
            value: "No"
        actions:
          - riskvalue: 10

目前我正在读取这样的文件并修改python中的特定值,然后想要将其写回。当我写作时,我遇到的问题是引用是列表而不是概述。

这意味着工作流程如下:我正在阅读doc

yaml = ruamel.yaml.YAML()
with open('test.yaml') as f:
    data = yaml.load(f)

for k in data.keys():
    if k == 'questions':
        q = data.get(k)
        for i in range(0, len(q)):
            q[i]['title'] = "my new title"

f.close()
g = open('new_file.yaml', 'w')
yaml(data)
g.close()
ruamel.yaml
1个回答
1
投票

不,没有这样的选项,因为它会导致无效的YAML文件。

<<是一个映射关键字,为此特别解释该值,假设解析器实现了language independent merge key specification。根据YAML specification,映射键必须是唯一的:

映射节点的内容是一组无序的键:值节点对,具有每个键唯一的限制。

ruamel.yaml(<0.15.75)不会在这样的重复键上引发错误是bug。在重复的普通键上,ruamel.yaml确实会抛出错误。该错误继承自PyYAML(符合规范,即使在重复的普通键上也不会产生错误)。


然而,通过一些预处理和后处理,您可以轻松实现您想要做的事情。诀窍是在解析之前使YAML有效,使得有问题的重复<<键唯一(但可识别)然后,当将YAML写回文件时,再次用<<: *替换这些唯一键。在下文中,第一次出现的<<: *[<<, 0]:取代,第二次出现在[<<, 1]:等。

*需要成为替换的一部分,因为文档中没有这些别名的锚点。

import sys
import subprocess
import ruamel.yaml

yaml = ruamel.yaml.YAML()
yaml.preserve_quotes = True
yaml.indent(sequence=4, offset=2)

class DoubleMergeKeyEnabler(object):
    def __init__(self):
        self.pat = '<<: '  # could be at the root level mapping, so no leading space
        self.r_pat = '[<<, {}]: '   # probably not using sequences as keys
        self.pat_nr = -1

    def convert(self, doc):
        while self.pat in doc:
            self.pat_nr += 1
            doc = doc.replace(self.pat, self.r_pat.format(self.pat_nr), 1)
        return doc

    def revert(self, doc):
        while self.pat_nr >= 0:
            doc = doc.replace(self.r_pat.format(self.pat_nr), self.pat, 1)
            self.pat_nr -= 1
        return doc


dmke = DoubleMergeKeyEnabler()

with open('test.yaml') as fp:
  # we don't do this line by line, that would not work well on flow style mappings
  orgdoc = fp.read()
  doc = dmke.convert(orgdoc)

data = yaml.load(doc)
data['questions'][0].anchor.always_dump = True
#######################################
# >>>> do your thing on data here <<< #
#######################################

with open('output.yaml', 'w') as fp:
    yaml.dump(data, fp, transform=dmke.revert)

res = subprocess.check_output(['diff', '-u', 'test.yaml', 'output.yaml']).decode('utf-8')
print('diff says:', res)

这使:

diff says:

这意味着往返文件是相同的(只要你在转储之前不改变任何东西)。

设置preserve_quotes并在ident()实例上调用YAML对于保留多余的引号是必要的。保持缩进。

由于锚question-one没有别名,因此您需要通过将该属性上的always_dump设置为True来显式启用转储。如果有必要,你可以递归地走过data并在anchor.always_dump = True时设置.anchor.value is not None

© www.soinside.com 2019 - 2024. All rights reserved.