Search code examples
pythonpython-2.7yamlpyyaml

Python yaml generate few values in quotes


I am using the yaml module in my Python script to generate a YAML file. The following is an example:

import yaml
class MyDumper(yaml.Dumper):

    def increase_indent(self, flow=False, indentless=False):
        return super(MyDumper, self).increase_indent(flow, False)

foo = {
    'instance_type': 'test',
    'hostname': "\"testhost\"",
    'name': 'foo',
    'my_list': [
        {'foo': 'test', 'bar': 'test2'},
        {'foo': 'test3', 'bar': 'test4'}],
    'hello': 'world',
}

print yaml.dump(foo, Dumper=MyDumper, default_flow_style=False)

Output:

hello: world
hostname: '"testhost"'
instance_type: test
my_list:
  - bar: test2
    foo: test
  - bar: test4
    foo: test3
name: foo

In above output hostname value has single and double quotes, I want only double quotes.

Expected output:

hello: world
hostname: "testhost"
instance_type: test
my_list:
  - bar: test2
    foo: test
  - bar: test4
    foo: test3
name: foo

Solution

  • You cannot force quotes in YAML by quoting parts of your data as you do. As the quotes force the dumper to apply quoting to the scalar (i.e. can no longer use plain scalars as for the other string values in your yaml file).

    You need to make a type that is dumped with quotes. Most easily that is done using ruamel.yaml (disclaimer: I am the author of that enhanced version of PyYAML, supporting YAML 1.2, support round-trip preservation of comments and quotes etc).

    import sys
    import ruamel.yaml
    from ruamel.yaml.scalarstring import DoubleQuotedScalarString as dq
    
    
    yaml = ruamel.yaml.YAML()
    yaml.indent(sequence=4, offset=2)
    
    foo = {
        'instance_type': 'test',
        'hostname': dq("testhost"),
        'name': 'foo',
        'my_list': [
            {'foo': 'test', 'bar': 'test2'},
            {'foo': 'test3', 'bar': 'test4'}],
        'hello': 'world',
    }
    
    
    yaml.dump(foo, sys.stdout)
    

    which gives:

    instance_type: test
    hostname: "testhost"
    name: foo
    my_list:
      - foo: test
        bar: test2
      - foo: test3
        bar: test4
    hello: world
    

    You can also easily load that output and dump it generating exactly same ouput:

    from ruamel.yaml.compat import StringIO
    
    buf = StringIO()
    yaml.dump(foo, buf)
    
    yaml.preserve_quotes = True
    data = yaml.load(buf.getvalue())
    yaml.dump(data, sys.stdout)