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
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
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.
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
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
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.