ruamel yaml 克隆一个节点而不合并锚点

huangapple go评论98阅读模式
英文:

ruamel yaml cloning a node without merging anchors

问题

我理解你想要的是如何在YAML文件中进行深度复制并修改节点名称。下面是你提供的代码的翻译部分:

import copy
import sys

import ruamel.yaml

yaml_str = """
main: &base
  key1: test
  key2: test

dev:
  <<: *base
  key3: test
"""

def main():
    yaml = ruamel.yaml.YAML(typ='rt')
    original_yaml = yaml.load(yaml_str)

    copied_stanza = copy.deepcopy(original_yaml['dev'])

    original_yaml['stage'] = copied_stanza

    yaml.dump(original_yaml, sys.stdout)

if __name__ == "__main__":
    main()

请注意,这只是代码的翻译部分,我不会回答关于代码的问题。如果你有任何其他翻译需求,请告诉我。

英文:

I have a YAML file with a similar structure:

main: &base
  key1: test
  key2: test

dev:
  <<: *base
  key3: test

What I'm trying to achieve is cloning the node dev by performing a deep copy of all its properties - including the merge operator - and changing its name to stage.
The desired result is the following:

main: &base
  key1: test
  key2: test

dev:
  <<: *base
  key3: test

stage:
  <<: *base
  key3: test

I've tried doing this using ruamel.yaml library by running the following code snippet:

import copy
import sys

import ruamel.yaml


yaml_str = """\
main: &base
  key1: test
  key2: test

dev:
  <<: *base
  key3: test

"""

def main():
    yaml = ruamel.yaml.YAML(typ='rt')
    original_yaml = yaml.load(yaml_str)

    copied_stanza = copy.deepcopy(original_yaml['dev'])

    original_yaml['stage'] = copied_stanza

    yaml.dump(original_yaml, sys.stdout)


if __name__ == "__main__":
    main()

However I get the following result:

main: &base
  key1: test
  key2: test

dev:
  <<: *base
  key3: test

stage:
  <<:
    key1: test
    key2: test

  key3: test

  key1: test
  key2: test

which is different from what I want since it's flattening the merge operator in a very weird way.

I believe the issue is the way I'm copying the node, however I've tried searching the web for hours and tried a plethora of different approaches without any luck.

Any advice?

UPDATE

After Anthon's answer, I was able to get closer to the desired result.
However if the node that I'm trying to copy has one more level of depth (see dev.key4 in this example):

main: &base
  key1: test
  key2: test

dev:
  <<: *base
  key3: test
  key4:
    subkey1: test

then the generated yaml will start creating anchors and aliases for all those nested nodes:

main: &base
  key1: test
  key2: test

dev:
  <<: *base
  key3: test
  key4: &id001
    subkey1: test

stage:
  <<: *base
  key3: test
  key4: *id001

How can I avoid the creation of those?

UPDATE 2

I adapted the solution provided by Anthon to handle any level of depth using a recursive function:

import sys

import ruamel.yaml


yaml_str = """\
main: &base
  key1: test
  key2: test

dev:
  <<: *base
  key3: test
  key4:
    subkey1: test

"""


def create_copied_stanza(dev):
    copied_stanza = ruamel.yaml.comments.CommentedMap()
    for k, v in dev.items():
        if k in dev._ok:
            if isinstance(v, ruamel.yaml.comments.CommentedMap):  # Add your condition here
                copied_stanza[k] = create_copied_stanza(v)
            else:
                copied_stanza[k] = v
    return copied_stanza

def main():
    yaml = ruamel.yaml.YAML(typ='rt')
    original_yaml = yaml.load(yaml_str)
    dev = original_yaml['dev']
    copied_stanza = create_copied_stanza(dev)
    copied_stanza._yaml_merge = dev._yaml_merge
    original_yaml['stage'] = copied_stanza
    yaml.dump(original_yaml, sys.stdout)


if __name__ == "__main__":
    main()

答案1

得分: 1

以下是翻译好的部分:

"Your YAML 中的映射被加载为 ruamel.yaml.comments.CommentedMap 实例,并且它们实现了 __deepcopy__,以便保留合并键。

为了获得您想要的结果,您目前需要复制映射拥有的键以及包含合并信息的属性。
请确保固定您的 ruamel.yaml 版本,因为这些内部实现可能会在将来更改。

如果您的合并键不在根级别,您需要编写一个递归处理任何映射的函数,但对于您的 YAML,这不是必要的。"

请注意,代码部分未被翻译,仅提供文本内容。

英文:

The mappings in your YAML are loaded as ruamel.yaml.comments.CommentedMap instances and they
have __deepcopy__ implemented just so as to preserve the merge key.

To get what you want you currently need to copy the keys owned by the
mapping and the attribute that contains the merge information.
Make sure to pin your ruamel.yaml version as these internals might change
in the future.

import sys

import ruamel.yaml


yaml_str = """\
main: &base
  key1: test
  key2: test

dev:
  <<: *base
  key3: test

"""

yaml = ruamel.yaml.YAML(typ='rt')
original_yaml = yaml.load(yaml_str)
dev = original_yaml['dev']
copied_stanza = ruamel.yaml.comments.CommentedMap({k: v for k, v in dev.items() if k in dev._ok})
copied_stanza._yaml_merge = dev._yaml_merge
original_yaml['stage'] = copied_stanza
yaml.dump(original_yaml, sys.stdout)

which gives:

main: &base
  key1: test
  key2: test

dev:
  <<: *base
  key3: test

stage:
  <<: *base
  key3: test

If your merge key is not at the root level, you have to make a function that does this for you, recursing in
any mappings,
but for your YAML this is not necessary.

huangapple
  • 本文由 发表于 2023年6月14日 23:58:42
  • 转载请务必保留本文链接:https://go.coder-hub.com/76475475.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定