Any yaml libraries in Python that support dumping of long strings as block literals or folded blocks?

Viewed 13482

I'd like to be able to dump a dictionary containing long strings that I'd like to have in the block style for readability. For example:

foo: |
  this is a
  block literal
bar: >
  this is a
  folded block

PyYAML supports the loading of documents with this style but I can't seem to find a way to dump documents this way. Am I missing something?

3 Answers

This can be relatively easily done, the only "hurdle" being how to indicate which of the spaces in the string, that needs to be represented as a folded scalar, needs to become a fold. The literal scalar has explicit newlines containing that information, but this cannot be used for folded scalars, as they can contain explicit newlines e.g. in case there is leading whitespace and also needs a newline at the end in order not to be represented with a stripping chomping indicator (>-)

import sys
import ruamel.yaml

folded = ruamel.yaml.scalarstring.FoldedScalarString
literal = ruamel.yaml.scalarstring.LiteralScalarString

yaml = ruamel.yaml.YAML()

data = dict(
    foo=literal('this is a\nblock literal\n'), 
    bar=folded('this is a folded block\n'),
)

data['bar'].fold_pos = [data['bar'].index(' folded')]

yaml.dump(data, sys.stdout)

which gives:

foo: |
  this is a
  block literal
bar: >
  this is a
  folded block

The fold_pos attribute expects a reversable iterable, representing positions of spaces indicating where to fold.

If you never have pipe characters ('|') in your strings you could have done something like:

import re

s = 'this is a|folded block\n'
sf = folded(s.replace('|', ' '))  # need to have a space!
sf.fold_pos = [x.start() for x in re.finditer('\|', s)]  # | is special in re, needs escaping


data = dict(
    foo=literal('this is a\nblock literal\n'), 
    bar=sf,  # need to have a space
)

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

which also gives exactly the output you expect

Related