Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ruamel.yaml equivalent of sort_keys?

I'm trying to dump a Python dict to a YAML file using ruamel.yaml. I'm familiar with the json module's interface, where pretty-printing a dict is as simple as

import json
with open('outfile.json', 'w') as f:
    json.dump(mydict, f, indent=4, sort_keys=True)

With ruamel.yaml, I've gotten as far as

import ruamel.yaml
with open('outfile.yaml', 'w') as f:
    ruamel.yaml.round_trip_dump(mydict, f, indent=2)

but it doesn't seem to support the sort_keys option. ruamel.yaml also doesn't seem to have any exhaustive docs, and searching Google for "ruamel.yaml sort" or "ruamel.yaml alphabetize" didn't turn up anything at the level of simplicity I'd expect.

Is there a one-or-two-liner for pretty-printing a YAML file with sorted keys?

(Note that I need the keys to be alphabetized down through the whole container, recursively; just alphabetizing the top level is not good enough.)


Notice that if I use round_trip_dump, the keys are not sorted; and if I use safe_dump, the output is not "YAML-style" (or more importantly "Kubernetes-style") YAML. I don't want [] or {} in my output.

$ pip freeze | grep yaml
ruamel.yaml==0.12.5

$ python
>>> import ruamel.yaml
>>> mydict = {'a':1, 'b':[2,3,4], 'c':{'a':1,'b':2}}
>>> print ruamel.yaml.round_trip_dump(mydict)  # right format, wrong sorting
a: 1
c:
  a: 1
  b: 2
b:
- 2
- 3
- 4

>>> print ruamel.yaml.safe_dump(mydict)  # wrong format, right sorting
a: 1
b: [2, 3, 4]
c: {a: 1, b: 2}
like image 276
Quuxplusone Avatar asked Oct 24 '16 20:10

Quuxplusone


1 Answers

You need some recursive function that handles mappings/dicts, sequence/list:

import sys
import ruamel.yaml

CM = ruamel.yaml.comments.CommentedMap

yaml = ruamel.yaml.YAML()

data = dict(a=1, c=dict(b=2, a=1), b=[2, dict(e=6, d=5), 4])
yaml.dump(data, sys.stdout)

def rec_sort(d):
    try:
        if isinstance(d, CM):
            return d.sort()
    except AttributeError:
        pass
    if isinstance(d, dict):
        # could use dict in newer python versions
        res = ruamel.yaml.CommentedMap()
        for k in sorted(d.keys()):
            res[k] = rec_sort(d[k])
        return res
    if isinstance(d, list):
        for idx, elem in enumerate(d):
            d[idx] = rec_sort(elem)
    return d

print('---')

yaml.dump(rec_sort(data), sys.stdout)

which gives:

a: 1
c:
  b: 2
  a: 1
b:
- 2
- e: 6
  d: 5
- 4
---
a: 1
b:
- 2
- d: 5
  e: 6
- 4
c:
  a: 1
  b: 2

The commented map is the structure ruamel.yaml uses when doing a round-trip (load+dump) and round-tripping is designed to keep the keys in the order that they were during loading.

The above should do a reasonable job preserving comments on mappings/sequences when you load data from a commented YAML file

like image 175
Anthon Avatar answered Sep 20 '22 02:09

Anthon