How to use ruamel.yaml to dump literal scalars
Asked Answered
S

1

8

I've searched and found "similar" posts, but nothing that answers my question directly. I also found a stackoverflow post here, but no answers.

I need to write to a yaml file using the following format:

any_value: 123.4
data_points: |-
  0.0, 1.0
  0.1, 1.5
  0.2, 1.7

If I use a long string containing \n in it, it will just print it as-is:

any_value: 123.4
data_points: "0.0, 1.0\n0.1, 1.5\n0.2, 1.7"

Which is not what I want. I can also pass it as a list where each item looks like this

['0.0, 1.0', '0.1, 1.5', '0.2, 1.7']

But that only results in

any_value: 123.4
data_points:
- 0.0, 1.0
- 0.1, 1.5
- 0.2, 1.7

It kinda looks like the first one, but it isn't. How do I get ruamel.yaml to dump it the way I want?

Sultana answered 14/6, 2018 at 8:16 Comment(1)
That other question is about folded scalars, you have a literal block style scalar. Folded scalars are currently not round-tripped as folded scalars, partly because it is more difficult to keep the information on where to fold, but also because folded scalars are far less often used (and useful).Tungstate
T
16

What you want are not folded scalars (which would have > instead of |) but block style literal scalars.

The general way to solve this is to look at whether ruamel.yaml can round-trip what you want:

import sys
import ruamel.yaml

yaml_str = """\
any_value: 123.4
data_points: |-
  a
  b
"""

yaml = ruamel.yaml.YAML()
data = yaml.load(yaml_str)
yaml.dump(data, sys.stdout)

this gives:

any_value: 123.4
data_points: |-
  a
  b

As the output looks like the input, examine the type that ruamel.yaml uses to preserve the literal style information:

print(type(data['data_points']))

which shows what ruamel.yaml uses interally:

<class 'ruamel.yaml.scalarstring.LiteralScalarString'>

With that knowledge you can do:

from ruamel.yaml.scalarstring import LiteralScalarString

data['data_points'] = LiteralScalarString("""\
0.0, 1.0
0.1, 1.5
0.2, 1.7""")

yaml.dump(data, sys.stdout)

resulting in:

any_value: 123.4
data_points: |-
  0.0, 1.0
  0.1, 1.5
  0.2, 1.7

Please note that the dash in |- indicates that your scalar doesn't have a final new-line. That is why your Python multi-line string should not have one either.

There is also a function walk_tree() in ruamel/yaml/scalarstring.py, which might be of help, it:

walks over a simple yaml tree (recursing in dict values and list items) and converts strings that have multiple lines to literal scalars

Tungstate answered 14/6, 2018 at 8:43 Comment(2)
If this solves your issue, please consider marking this answer as accepted (by clicking the "v" on the left next to the top of the answer. Then others know that this worked for you without having to read the comments.Tungstate
Should now use LiteralScalarString rather than PreservedScalarString - 3e6f42ae9afMcdonald

© 2022 - 2024 — McMap. All rights reserved.