pythonyamlruamel.yaml

Ruamel.yaml adds id001 aliases?


I'm using ruamel.yaml to add an entry to a dictionary:

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

raw = """---
# Some config file..
# This is the main clients config
clients:
  foobar1:
    name: foo Bar 1
    auth: false
    count: 1290
  barboz:
    name: BarraBoz
    auth: true
    count: 19
uids_default: &uids_default
  - attribute_email: mail
    datafile: vars1
uids:
  - <<: *uids_default
    skip: true
  - <<: *uids_default
    issuer: http://foo.bar
"""

vanhelst = {"adfs": 124423423, "123": "adsfadsfasdf", "name": "Dirk Van helst"}

data = yaml.load(raw)
data["clients"].insert(len(data["clients"]), "vanhelst", vanhelst)

# Write to file
with open("/tmp/out.yml", "w") as f:
    yaml.dump(data, f)

That works, the entry is added correctly, the comment is preserved. But it also changes some anchors that are in the file, to add aliases (?) like id001:

# Some config file..
# This is the main clients config
clients:
  foobar1:
    name: foo Bar 1
    auth: false
    count: 1290
  barboz:
    name: BarraBoz
    auth: true
    count: 19
  vanhelst:
    adfs: 124423423
    '123': adsfadsfasdf
    name: Dirk Van helst
uids_default:
  - &id001
    attribute_email: mail
    datafile: vars1
uids:
  - <<: *id001
    skip: true
  - <<: *id001
    issuer: http://foo.bar

Is there a way to not add those, and not touch the anchors at all?


Solution

  • You have only one anchor and two aliases. The anchor is on a sequence which contains a single mapping. You then use that data structure (i.e. the sequence) as a value for the << merge key. I have not seen anybody do that before. That sequence is flattened by the merge preserving code (as it contains only one mapping), that is why the anchor shifts from the sequence to the mapping it is containing.

    So you can consider it is a bug in ruamel.yaml, that its merge key handling cannot cope with aliases of anchored sequences. If you would have two elements in the sequence the merge key entry would look like:

      - <<: [*id0001, *id0002]
    

    I would remove the sequence from the value of uids_default as long as you are not using the optional sequence. If you do things work:

    import sys
    import ruamel.yaml
    
    yaml_str = """\
    # Some config file..
    # This is the main clients config
    clients:
      foobar1:
        name: foo Bar 1
        auth: false
        count: 1290
      barboz:
        name: BarraBoz
        auth: true
        count: 19
    uids_default: &uids_default
        attribute_email: mail
        datafile: vars1
    uids:
      - <<: *uids_default
        skip: true
      - <<: *uids_default
        issuer: http://foo.bar
    """
    
    yaml = ruamel.yaml.YAML()
    yaml.preserve_quotes = True
    yaml.indent(mapping=2, sequence=4, offset=2)
    
    vanhelst = {"adfs": 124423423, "123": "adsfadsfasdf", "name": "Dirk Van helst"}
    
    data = yaml.load(yaml_str)
    data["clients"].insert(len(data["clients"]), "vanhelst", vanhelst)
    yaml.dump(data, sys.stdout)
    

    which gives:

    # Some config file..
    # This is the main clients config
    clients:
      foobar1:
        name: foo Bar 1
        auth: false
        count: 1290
      barboz:
        name: BarraBoz
        auth: true
        count: 19
      vanhelst:
        adfs: 124423423
        '123': adsfadsfasdf
        name: Dirk Van helst
    uids_default: &uids_default
      attribute_email: mail
      datafile: vars1
    uids:
      - <<: *uids_default
        skip: true
      - <<: *uids_default
        issuer: http://foo.bar
    

    If you really need a merge of multiple mappings, anchor them each with different anchors and put the sequence of the corresponding aliases after the merge key.


    You are still using .yml as an extension. Please read the FAQ on yaml.org, and follow its recommendation.