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:
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
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.yaml.comments.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.yaml.comments.CommentedMap(**kw)
common = CM(x=1)
common.yaml_set_anchor('ANCHOR')
data = CM(base=common, object=CM(y=2))
setattr(data['object'], ruamel.yaml.comments.merge_attrib, [(0, common)])
yaml = ruamel.yaml.YAML()
yaml.dump(data, sys.stdout)
which gives:
base: &ANCHOR
x: 1
object:
<<: *ANCHOR
y: 2
.merge
attribute0
, determines the ordering.Pin the version of ruamel.yaml
you are using. Internals like these will change without notice