python use Pyyaml and keep format

python, yaml

Solution

If you use `ruamel.yaml` ¹, you can relatively easily achieve this, by combining this and this answer here on StackOverlow.

By default `ruamel.yaml` normalizes to an indent of 2, and drops superfluous quotes. As you don't seem to want that, you have to either explicitly set the indent, or have `ruamel.yaml` analyse the input, and tell it to preserve quotes:

import sys
import ruamel.yaml
import ruamel.yaml.util

yaml_str = """\
nas:
    mount_dir: '/nvr'
    mount_dirs: ['/mount/data0', '/mount/data1', '/mount/data2']
"""

result, indent, block_seq_indent = ruamel.yaml.util.load_yaml_guess_indent(
    yaml_str, preserve_quotes=True)
result['nas']['mount_dirs'][0] = "haha"
ruamel.yaml.round_trip_dump(result, sys.stdout, indent=indent,
                            block_seq_indent=block_seq_indent)

instead of the `load_yaml_guess_indent()` invocation you can do:

result = ruamel.yaml.round_trip_load(yaml_str, preserve_quotes=True)
indent = 4
block_sequence_indent = None 

If you want `haha` to be (single) quoted in the output make it a `SingleQuotedScalarString`:

result['nas']['mount_dirs'][0] = \
       ruamel.yaml.scalarstring.SingleQuotedScalarString("haha")

with that the output will be:

nas:
    mount_dir: '/nvr'
    mount_dirs: ['haha', '/mount/data1', '/mount/data2']

(given that your short example input has no block style sequences, the `block_sequence_indent` cannot be determined and will be None)

When using the newer API you have control over the indent of the mapping and sequences seperately:

yaml = ruamel.yaml.YAML()
yaml.indent(mapping=4, sequence=6, offset=3)  # not that that looks nice
data = yaml.load(some_stream)
yaml.dump(data, some_stream)

This will make your YAML formatted consistently if it wasn't so to begin with, and make no further changes after the first round-trip.

¹ Disclaimer: I am the author of that package.

Problem

Here is a config file, I use PyYAML to change some value from it and then I write some config, but it will change my format, it confuses me. ``` $ results.yaml nas: mount_dir: '/nvr' mount_dirs: ['/mount/data0', '/mount/data1', '/mount/data2'] # yaml.py import yaml.py conf = open("results.conf", "r") results = yaml.load(conf) conf.close() result['nas']['mount_dirs'][0]= "haha" with open('/home/zonion/speedio/speedio.conf', 'w') as conf: yaml.dump(speedio, conf, default_flow_style=False) conf.close() ``` but it change my format,what should I do? ``` # cat results.conf nas: mount_dir: /nvr mount_dirs: - haha - /mount/data1 - /mount/data2 ```

Original source

Related problems