Darby_321
Darby_321

Reputation: 27

Using ruamel.yaml to keep multiline strings with same indentation Python

I am trying to make up some YAML files and I am struggling to get the YAML files in a correct format. I am trying to get the strings of the values all be on the same indentation but I cannot seem to get it to work.

ResourceIndicators:
  version: 1.0.0
  name: This is the name
  neType: Text
  category: Text
  description: The is the first line of the sentence and then we continue 
    on to the second line but this line should be indented to match the 
    first line of the string. 

This is what I have but I am looking for:

ResourceIndicators:
  version: 1.0.0
  name: This is the name
  neType: Text
  category: Text
  description: The is the first line of the sentence and then we continue 
               on to the second line but this line should be indented to 
               match the first line of the string. 

Upvotes: 2

Views: 2449

Answers (1)

Anthon
Anthon

Reputation: 76578

There is no option or simple way in ruamel.yaml (or PyYAML) to get what you want.

The value for the description mapping key is a normal scalar, and you probably have set the output width to 70 (or similar) to get that result. The value is a plain style scalar, which can be broken on any space surrounded by non-space characters.

You might have considered using block style literal scalars, but although

  description: |-
               The is the first line of the sentence and then we continue
               on to the second line but this line should be indented to match the
               first line of the string.

almost looks similar, it actually loads with two extra newlines in that string. Even if you pre-process that scalar before dumping and post process it after loading to try and use an explicit block indentation indicator will not give you more than 9 positions (as it is restricted to a single digit) and you have more than that.

If the following format is acceptable:

ResourceIndicators:
  version: 1.0.0
  name: This is the name
  neType: Text
  category: Text
  description: |-
    The is the first line of the sentence and then we continue
    on to the second line but this line should be indented to
    match the first line of the string.

You can do that with a little function wrapped:

import sys
import textwrap
import ruamel.yaml
from ruamel.yaml.scalarstring import PreservedScalarString

yaml_str = """\
ResourceIndicators:
    version: 1.0.0
    name: This is the name
    neType: Text
    category: Text
    description: The is the first line of the sentence and then we continue
    on to the second line but this line should be indented to match the
    first line of the string.
"""

def wrapped(s, width=60):
    return PreservedScalarString('\n'.join(textwrap.wrap(s, width=width)))

yaml = ruamel.yaml.YAML()
yaml.preserve_quotes = True
data = yaml.load(yaml_str)
data['ResourceIndicators']['description'] = \
    wrapped(data['ResourceIndicators']['description'])
yaml.dump(data, sys.stdout)

But beware that after loading you have to replace the newlines in the value with spaces.

If that is not an option you need to create a class "IndentedPlainString" with a special representer that does the extra indentation.

Upvotes: 3

Related Questions