Array does not have indent or space in PyYAML

Question:

In the code below I created the net_plan_dict variable dictionary and converted it into a YAML format file.
Inside the dictionary I have a field called addresses which is an array of three elements.
After creating the YAML file, the three array elements were not placed under the addresses field :

import yaml

net_plan_dict = {
    'networking': {
        'addresses': ['192.168.1.1', '192.168.1.2', "192.168.1.3"],
        'gateway4': '192.168.121.1'
    }
}

with open("new.yaml", "w") as f:
    yaml.dump(net_plan_dict, f)

Output of the above code is as follows (in the file below, the IPs are not below the address and have no space or indent).

new.yaml:

networking:
  addresses:
  - 192.168.1.1 <-------- does not have indent
  - 192.168.1.2
  - 192.168.1.3
  gateway4: 192.168.121.1


but my goal is to get this output file (how to create this file, when ips are under addresses field):

networking:
  addresses:
    - 192.168.1.1
    - 192.168.1.2
    - 192.168.1.3
  gateway4: 192.168.121.1

Asked By: omides248

||

Answers:

PyYAML’s dump() doesn’t have the fine control to have a different indent for
the mappings (2 positions) and sequences (4 positions), nor can it offset the sequence indicator (-) within
the space of the (sequence) indent).

If you want that kind of control over your output you should use ruamel.yaml (disclaimer: I am the author of that package):

import sys
import ruamel.yaml


net_plan_dict = {
    'networking': {
        'addresses': ['192.168.1.1', '192.168.1.2', "192.168.1.3"],
        'gateway4': '192.168.121.1'
    }
}


yaml = ruamel.yaml.YAML()
yaml.indent(mapping=2, sequence=4, offset=2)
yaml.dump(net_plan_dict, sys.stdout)

which gives:

networking:
  addresses:
    - 192.168.1.1
    - 192.168.1.2
    - 192.168.1.3
  gateway4: 192.168.121.1
Answered By: Anthon

I came up with another solution. Leaving it here for other poor souls. Use it like

yaml.dump(obj, Dumper=PrettyDumper)
import yaml.emitter
import yaml.serializer
import yaml.representer
import yaml.resolver


class IndentingEmitter(yaml.emitter.Emitter):
    def increase_indent(self, flow=False, indentless=False):
        """Ensure that lists items are always indented."""
        return super().increase_indent(
            flow=False,
            indentless=False,
        )


class PrettyDumper(
    IndentingEmitter,
    yaml.serializer.Serializer,
    yaml.representer.Representer,
    yaml.resolver.Resolver,
):
    def __init__(
        self,
        stream,
        default_style=None,
        default_flow_style=False,
        canonical=None,
        indent=None,
        width=None,
        allow_unicode=None,
        line_break=None,
        encoding=None,
        explicit_start=None,
        explicit_end=None,
        version=None,
        tags=None,
        sort_keys=True,
    ):
        IndentingEmitter.__init__(
            self,
            stream,
            canonical=canonical,
            indent=indent,
            width=width,
            allow_unicode=allow_unicode,
            line_break=line_break,
        )
        yaml.serializer.Serializer.__init__(
            self,
            encoding=encoding,
            explicit_start=explicit_start,
            explicit_end=explicit_end,
            version=version,
            tags=tags,
        )
        yaml.representer.Representer.__init__(
            self,
            default_style=default_style,
            default_flow_style=default_flow_style,
            sort_keys=sort_keys,
        )
        yaml.resolver.Resolver.__init__(self)

Shout out to saneyaml for the idea.

Answered By: Yourstruly
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.