8

I use PyYAML to output a python dictionary to YAML format:

import yaml
d = { 'bar': { 'foo': 'hello', 'supercalifragilisticexpialidocious': 'world' } }
print yaml.dump(d, default_flow_style=False)

The output is:

bar:
  foo: hello
  supercalifragilisticexpialidocious: world

But I would like:

bar:
  foo                                : hello
  supercalifragilisticexpialidocious : world

Is there a simple solution to that problem, even a suboptimal one?

mouviciel
  • 66,855
  • 13
  • 106
  • 140
  • 1
    After a quick look at the `PyYAML` sources, I don't thing this would be simple to achieve. It would involve, at least, creating a custom `Emitter` (or patching the existing one). – Pedro Romano Nov 08 '12 at 20:06

2 Answers2

6

Ok, here is what I've come up with so far.

My solution involves two steps. The first step defines a dictionary representer for adding trailing spaces to keys. With this step, I obtain quoted keys in the output. This is why I add a second step for removing all these quotes:

import yaml
d = {'bar': {'foo': 'hello', 'supercalifragilisticexpialidocious': 'world'}}


# FIRST STEP:
#   Define a PyYAML dict representer for adding trailing spaces to keys

def dict_representer(dumper, data):
    keyWidth = max(len(k) for k in data)
    aligned = {k+' '*(keyWidth-len(k)):v for k,v in data.items()}
    return dumper.represent_mapping('tag:yaml.org,2002:map', aligned)

yaml.add_representer(dict, dict_representer)


# SECOND STEP:
#   Remove quotes in the rendered string

print(yaml.dump(d, default_flow_style=False).replace('\'', ''))
mbarkhau
  • 8,190
  • 4
  • 30
  • 34
mouviciel
  • 66,855
  • 13
  • 106
  • 140
0

I found https://github.com/jonschlinkert/align-yaml for JavaScript and I translated it to Python at

https://github.com/eevleevs/align-yaml-python

It does not use PyYAML, apply it directly to the YAML output, without parsing.

A copy of the function below:

import re

def align_yaml(str, pad=0):
    props = re.findall(r'^\s*[\S]+:', str, re.MULTILINE)
    longest = max([len(i) for i in props]) + pad
    return ''.join([i+'\n' for i in map(lambda str:
            re.sub(r'^(\s*.+?[^:#]: )\s*(.*)', lambda m:
                    m.group(1) + ''.ljust(longest - len(m.group(1)) + 1) + m.group(2),
                str, re.MULTILINE)
        , str.split('\n'))])
Giulio
  • 469
  • 5
  • 15
  • 3
    While this link may answer the question, it is better to include the essential parts of the answer here and provide the link for reference. Link-only answers can become invalid if the linked page changes. - [From Review](/review/low-quality-posts/25166423) – Andreas Jan 23 '20 at 08:35
  • Easy to do, but I kind of disagree with the concept. The copied code is likely to get obsolete. I guess the choice should also depend on the reputation of the site being linked? – Giulio Jan 23 '20 at 09:04
  • No, the site link itself can still be provided just as a supplementary. Having the essential part of the answer here is a must – Andreas Jan 23 '20 at 09:40