admin管理员组

文章数量:1355731

I want to create data in python and write it to a file as a yaml-document using anchors and merges in the output.

I think this could be possible with ruamel YAML, because, as described in ruamels official examples:

  • it can load yaml-documents with anchors and merges transparently (transparent in the sense that access to the data is the same whether it is defined through anchors or not)
  • it can round-trip yaml-files while preserving anchors (so loading a yaml-file which includes anchors can be read and rewritten to disk and still have anchors)

This means ruamel.yaml must have an internal representation of the yaml-data that includes and understands anchors (unlike PyYAML, which only reads anchors but does not preserve them). There does not seem to be a documented way to create such anchors from python code.

The most minimal file I want to be able to create would look like this:

base: &ANCHOR
  x: 1

object:
  <<: *ANCHOR
  y: 2

I want to create data in python and write it to a file as a yaml-document using anchors and merges in the output.

I think this could be possible with ruamel YAML, because, as described in ruamels official examples:

  • it can load yaml-documents with anchors and merges transparently (transparent in the sense that access to the data is the same whether it is defined through anchors or not)
  • it can round-trip yaml-files while preserving anchors (so loading a yaml-file which includes anchors can be read and rewritten to disk and still have anchors)

This means ruamel.yaml must have an internal representation of the yaml-data that includes and understands anchors (unlike PyYAML, which only reads anchors but does not preserve them). There does not seem to be a documented way to create such anchors from python code.

The most minimal file I want to be able to create would look like this:

base: &ANCHOR
  x: 1

object:
  <<: *ANCHOR
  y: 2
Share Improve this question edited Mar 28 at 22:51 julaine asked Mar 28 at 8:41 julainejulaine 1,7561 gold badge14 silver badges31 bronze badges 3
  • 1 You should include a minimal example YAML document of the output that you want to create. Additionally you should make sure this round-trips without changes (if ruamel.yaml cannot round-trip it, it is far less likely you can generate it from scratch). – Anthon Commented Mar 28 at 8:59
  • @Anthon Good tip to say I can probably only create things I can roundtrip. – julaine Commented Mar 28 at 22:29
  • @Anthon After further consideration, I have added an example to the question (and deleted my comment arguing against the need for an example). Turns out the yaml-spec is much bigger than I thought and my question was therefore not as clear as I thought. – julaine Commented Mar 28 at 22:54
Add a comment  | 

1 Answer 1

Reset to default 1

When you round-trip your data using the following program:

import sys  
import ruamel.yaml

yaml_str = """\ 
base: &ANCHOR  
x: 1

object:  
<<: \*ANCHOR  
y: 2  
"""

yaml = ruamel.yaml.YAML()  
data = yaml.load(yaml_str)  
yaml.dump(data, sys.stdout)

you see that the output matches the input:

base: &ANCHOR
  x: 1

object:
  <<: *ANCHOR
  y: 2

You could actually create a new YAML instance for dumping and still get the same output. This means that the information about anchors and merging is somewhere in the data structure under data. So you should inspect various items.

print('anchor', data['base'].anchor)
print('type', type(data['base'].anchor))
print('keys', list(data['object'].keys()))
print('merge', data['object'].merge)
print('merge type', type(data['object'].merge))
print('ids', id(data['object'].merge[0][1]), id(data['base']))

which gives:

anchor Anchor('ANCHOR')
type <class 'ruamel.yaml.anchor.Anchor'>
keys ['y', 'x']
merge [(0, {'x': 1})]
merge type <class 'list'>
ids 4304434048 4304434048

The above is normally an incremental process (even for me, having some knowledge about the internals). And it helps to look at the source, especially construct_mapping in constructor.py and CommentedMap in comments. py

With the above information, lets first tackle the anchor ( you can have merges without anchor/alias, but they don't make much sense).

import sys
import ruamel.yaml

def CM(**kw):
    return ruamel.yamlments.CommentedMap(**kw)

common = CM(x=1)
common.yaml_set_anchor('ANCHOR')
data = CM(base=common, object=CM(tmp=common, y=2))
yaml = ruamel.yaml.YAML()
yaml.dump(data, sys.stdout)

which gives:

base: &ANCHOR
  x: 1
object:
  tmp: *ANCHOR
  y: 2

Creating the merge key, can be done by defining the CommentedMap with only the y key and adding the merge attribute:

import sys
import ruamel.yaml

def CM(**kw):
    return ruamel.yamlments.CommentedMap(**kw)

common = CM(x=1)
common.yaml_set_anchor('ANCHOR')
data = CM(base=common, object=CM(y=2))
setattr(data['object'], ruamel.yamlments.merge_attrib, [(0, common)])
yaml = ruamel.yaml.YAML()
yaml.dump(data, sys.stdout)

which gives:

base: &ANCHOR
  x: 1
object:
  <<: *ANCHOR
  y: 2
  1. There is no setter for the .merge attribute
  2. that attribute is a list of because in YAML the merge key can have have either a mapping or a list of mappings as value. IIRC the integer 0, determines the ordering.

Pin the version of ruamel.yaml you are using. Internals like these will change without notice

本文标签: pythonHow do I write out an anchor using ruamelyamlStack Overflow