Search code examples
pythonpython-3.xyamlruamel.yaml

How can I preserve or add quotes around values in a YAML file without stripping comments as a side effect using ruamel.yaml?


I am trying to generate a YAML file, which does not fall foul of the Norway problem described here: https://hitchdev.com/strictyaml/why/implicit-typing-removed/

I am running ruamel.yaml 0.17.21 in python 3.8.

Here is the code I am running:

import sys
from ruamel.yaml import YAML, round_trip_dump

data = [
    [1, """first value""", "first comment"],
    [2, '"second value"', "second comment"],
    ["abc", "None", 'this is a "comment"'],
    ["""def1""", "None", 'this is a "comment"'],
]
yaml = YAML()
yaml.preserve_quotes = True
code = yaml.load(
    """\
    en:
    """
)
for i, item in enumerate(data):
    code.insert(i + 1, str(item[0]), "" + str(item[1]) + "", comment=item[2])

print("\nthis strips quotes; but preserves comments:")
yaml.dump(code, sys.stdout)

print("\nthis preserves quotes; but strips comments:")
round_trip_dump(code, sys.stdout, default_style="'")

print("\ndesired result:")
print(
    "'en':\n
    '1': 'first value'  # first comment\n
    '2': 'second value' # second comment\n
    'abc': 'None' # this is a ""comment""\n
    'def1': 'None' # this is a ""comment"""
)

Solution

  • Thanks to @Anthon and @JonSG for constructive feedback. Based on that, I found this article How to correctly output strings with ruamel.yaml

    My working solution is therefore:

    import sys
    from ruamel.yaml import YAML, scalarstring
    
    DQ = scalarstring.DoubleQuotedScalarString
    
    data = [
        [1, """first value""", "first comment"],
        [2, '"second value"', "second comment"],
        ["abc", "None", 'this is a "comment"'],
        ["""def1""", "None", 'this is a "comment"'],
    ]
    yaml = YAML()
    code = yaml.load(
        """\
        en:
        """
    )
    for i, item in enumerate(data):
        code.insert(i + 1, DQ(item[0]), DQ(str(item[1])), comment=item[2])
    
    yaml.dump(code, sys.stdout)
    
    """
    which produces:
    
    en:
    "1": "first value"  # first comment
    "2": "\"second value\"" # second comment
    "abc": "None" # this is a "comment"
    "def1": "None" # this is a "comment"
    """