Search code examples
pythonyamlruamel.yaml

How to add internal whitespace to YAML sequence with Python ruamel


I'm using ruamel.yaml to format YAML documents in Python:

#!/usr/bin/env python
import sys

from ruamel.yaml import *

yaml = YAML()

numbers = CommentedSeq()
numbers.fa.set_flow_style()
numbers.extend([1, 2, 3])

root = CommentedMap()
root['numbers'] = numbers
yaml.dump(root, sys.stdout)

I followed the advice in this accepted answer to format the sequence inline. This outputs the following:

numbers: [1, 2, 3]

How can I add internal whitespace, and output this instead:

numbers: [ 1, 2, 3 ]

Solution

  • In ruamel.yaml>=0.17.33 you can set the indicators on the Emitter class variable to influence this:

    import sys
    
    from ruamel.yaml import YAML, CommentedSeq
    
    yaml = YAML()
    yaml.Emitter.flow_seq_start = '[ '      # defaults to '['
    yaml.Emitter.flow_seq_end = ' ]'        # defaults to ']'
    yaml.Emitter.flow_seq_separator = ' ,'  # defaults to ','
    
    numbers = CommentedSeq()
    numbers.fa.set_flow_style()
    numbers.extend([1, 2, 3])
    
    root = dict(numbers=numbers)
    
    yaml.dump(root, sys.stdout)
    

    which gives:

    numbers: [ 1 , 2 , 3 ]
    

    In ruamel.yaml<=0.17.32 the bracket open and bracket closed were hardcoded in methods in the emitter, so the only thing you can do is provide alternative methods for those

    import sys
    
    from ruamel.yaml import *
    
    class MyEmitter(ruamel.yaml.emitter.RoundTripEmitter):
        flow_start = '[ '   # these class attributes don't exist in RoundTripEmitter
        flow_end = ' ]'
    
        def expect_flow_sequence(self, force_flow_indent = False) -> None:
            if force_flow_indent:
                self.increase_indent(flow=True, sequence=True)
            ind = self.indents.seq_flow_align(
                self.best_sequence_indent, self.column, force_flow_indent
            )
            self.write_indicator(' ' * ind + self.flow_start, True, whitespace=True)
            if not force_flow_indent:
                self.increase_indent(flow=True, sequence=True)
            self.flow_context.append('[')
            self.state = self.expect_first_flow_sequence_item
    
        def expect_first_flow_sequence_item(self) -> None:
            if isinstance(self.event, SequenceEndEvent):
                self.indent = self.indents.pop()
                popped = self.flow_context.pop()
                assert popped == '['
                self.write_indicator(self.flow_end, False)
                if self.event.comment and self.event.comment[0]:
                    # eol comment on empty flow sequence
                    self.write_post_comment(self.event)
                elif self.flow_level == 0:
                    self.write_line_break()
                self.state = self.states.pop()
            else:
                if self.canonical or self.column > self.best_width:
                    self.write_indent()
                self.states.append(self.expect_flow_sequence_item)
                self.expect_node(sequence=True)
    
        def expect_flow_sequence_item(self) -> None:
            if isinstance(self.event, SequenceEndEvent):
                self.indent = self.indents.pop()
                popped = self.flow_context.pop()
                assert popped == '['
                if self.canonical:
                    self.write_indicator(',', False)
                    self.write_indent()
                self.write_indicator(self.flow_end, False)
                if self.event.comment and self.event.comment[0]:
                    # eol comment on flow sequence
                    self.write_post_comment(self.event)
                else:
                    self.no_newline = False
                self.state = self.states.pop()
            else:
                self.write_indicator(',', False)
                if self.canonical or self.column > self.best_width:
                    self.write_indent()
                self.states.append(self.expect_flow_sequence_item)
                self.expect_node(sequence=True)
    
    
    yaml = YAML()
    yaml.Emitter = MyEmitter
    
    numbers = CommentedSeq()
    numbers.fa.set_flow_style()
    numbers.extend([1, 2, 3])
    
    root = dict(numbers=numbers)
    
    yaml.dump(root, sys.stdout)
    

    which gives:

    numbers: [ 1, 2, 3 ]