I'm trying to create YAML from Python dictionaries. So far I've tried both PyYAML and ruamel.yaml and both have the same result: if the input dictionary does not contain a list, the output is not formatted correctly.
Here's the script:
from ruamel import yaml
import sys
yaml.dump({'name': 'Enterprise', 'class': 'Galaxy', 'armament': ['photon torpedoes','phasers'], 'number': 1701}, sys.stdout)
print('\n')
yaml.dump({'name': 'Enterprise', 'class': 'Galaxy', 'number': 1701}, sys.stdout)
And here's the output:
armament: [photon torpedoes, phasers]
class: Galaxy
name: Enterprise
number: 1701
{class: Galaxy, name: Enterprise, number: 1701}
The desired output is that the second YAML dump should be formatted like the first. What's going on here?
You are using the old style API and that defaults to output any leaf
nodes in flow style. In your case both the list/sequece [photon torpedoes, phasers]
and your seconds dump (where the root level is
the leaf node).
Assuming that you don't just want the explanation why this happens, but also how to change this behaviour, with the new API, using instances of ruamel.yaml.YAML
, the default is to make everything flow-style, including all leaf nodes:
from ruamel.yaml import YAML
import sys
yaml = YAML()
yaml.dump({'name': 'Enterprise', 'class': 'Galaxy', 'armament': ['photon torpedoes','phasers'], 'number': 1701}, sys.stdout)
print()
yaml.dump({'name': 'Enterprise', 'class': 'Galaxy', 'number': 1701}, sys.stdout)
giving:
name: Enterprise
class: Galaxy
armament:
- photon torpedoes
- phasers
number: 1701
name: Enterprise
class: Galaxy
number: 1701
Still not what you want :-)
You now have two options to get what you indicated: leaf node flow-style on your first dump and leaf node block-style on your second.
The first is to have one instance the default_flow_style
set used for the first dump and
the "normal" one for the second dump:
from ruamel.yaml import YAML
import sys
yaml = YAML()
yaml.default_flow_style = None
yaml.dump({'name': 'Enterprise', 'class': 'Galaxy', 'armament': ['photon torpedoes','phasers'], 'number': 1701}, sys.stdout)
print()
yaml = YAML()
yaml.dump({'name': 'Enterprise', 'class': 'Galaxy', 'number': 1701}, sys.stdout)
which gives:
name: Enterprise
class: Galaxy
armament: [photon torpedoes, phasers]
number: 1701
name: Enterprise
class: Galaxy
number: 1701
The second option is to explicitly set the flow-style on the object
that you want to output as sequence. Therefor you have to created a
ruamel.yaml.comments.CommentedSeq
instance, which is normally used
when loading to preserve flow/block-style, comments, etc.:
from ruamel.yaml import YAML, comments
import sys
yaml = YAML()
armaments = comments.CommentedSeq(['photon torpedoes','phasers'])
armaments.fa.set_flow_style()
yaml.dump({'name': 'Enterprise', 'class': 'Galaxy', 'armament': armaments, 'number': 1701}, sys.stdout)
print()
yaml.dump({'name': 'Enterprise', 'class': 'Galaxy', 'number': 1701}, sys.stdout)
which also gives:
name: Enterprise
class: Galaxy
armament: [photon torpedoes, phasers]
number: 1701
name: Enterprise
class: Galaxy
number: 1701
This second option of course gives you fine control (there is also a CommentedMap
) as you
can have those objects at all levels of your data hierarchy, not just at the leaves that are collections.
Please note that you don't have to go through any of these antics when loading the required output from a YAML file that has the formatting as you do want it. In that case the dict resp. list like instances are created with the right flow/block-style, so the output doesn't unexpectedly change when just changing/adding a value and dumping back.