Archit Jain
Archit Jain

Reputation: 2234

How to preserve indenting in key-value when dumping yaml

How to preserve indenting in key-value when dumping yaml ? I am using ruamel yaml

Code:

in_str='''Pets:
    Cat:
       Tom
    Mouse:
       Jerry
    Dog:
       Scooby
       '''


import ruamel.yaml, sys
results = ruamel.yaml.load(in_str, ruamel.yaml.RoundTripLoader, preserve_quotes=True)
results['Pets']['Bird']='Tweety'
ruamel.yaml.dump(results, sys.stdout, ruamel.yaml.RoundTripDumper, default_flow_style=True,indent=2, block_seq_indent=2)

Output :

Pets:
  Cat: Tom
  Mouse: Jerry
  Dog: Scooby
  Bird: Tweety

Expected Output:

Pets:
    Cat:
       Tom
    Mouse:
       Jerry
    Dog:
       Scooby
    Bird:
       Tweety

Upvotes: 2

Views: 4057

Answers (1)

Anthon
Anthon

Reputation: 76578

In order to achieve this you'll have to hook into the Emitter and have it insert a newline and appropriate indentation when processing a mapping value. This can be done with the old style API that you use, but is better done with the new ruamel.yaml API, which gives you e.g. the possibility to indent sequences and mappings with different values:

import sys
import ruamel.yaml
from ruamel.yaml.emitter import Emitter

class MyEmitter(Emitter):
    def expect_block_mapping_simple_value(self):
        # type: () -> None
        if getattr(self.event, 'style', None) != '?':
            # prefix = u''
            if self.indent == 0 and self.top_level_colon_align is not None:
                # write non-prefixed colon
                c = u' ' * (self.top_level_colon_align - self.column) + self.colon
            else:
                c = self.prefixed_colon
            self.write_indicator(c, False)
            # the next four lines force a line break and proper indent of the value
            self.write_line_break()
            self.indent += self.best_map_indent
            self.write_indent()
            self.indent -= self.best_map_indent
        self.states.append(self.expect_block_mapping_key)
        self.expect_node(mapping=True)


in_str='''\
Pets:
    Cat:
       Tom
    Mouse:
       - Jerry
       - 'Mickey'
    Dog:
       Scooby
       '''

yaml = ruamel.yaml.YAML()
yaml.Emitter = MyEmitter
yaml.indent(mapping=4, sequence=2, offset=0)
yaml.preserve_quotes = True
results = yaml.load(in_str)
results['Pets']['Bird']='Tweety'
yaml.dump(results, sys.stdout)

this gives:

Pets:
    Cat:
        Tom
    Mouse:
        - Jerry
        - 'Mickey'
    Dog:
        Scooby
    Bird:
        Tweety

Things to note:

  • You only have to handle simple scalar values (as opposed to mappings/sequences, which are already "pushed" to the next line in block sequence "mode")
  • The expect_block_mapping_simple_value is copied from the source and a few lines added. There is currently no "hook" to do this without duplicating some code.
  • You can play with the sequence and offset values for yaml.indent() if you have sequences and need different indentation for that.
  • All of this assumes consistent indenting, individual indentation is not preserved (i.e. if some values are indented with more or less than the four positions, they'll end up with 4 positions).

Upvotes: 2

Related Questions