python pyyaml: dump in block style, but how to keep any list as single line output?

108 Views Asked by At

I have a yaml file that also needs to be compatible to an existing black-box software that requires lists to appear on a single line, i.e. not block style, while everything else is expected in block style. pyyaml has no trouble reading this file, but I have not succeeded to write the affected keys containing lists as a string literal via dump() -- though print() shows it gets the right input. Escaping the square brackets also did not help. Here is my present code reduced to the relevant section with just one list:

import sys
import yaml


# given rc or yaml file / lists are one-liners. This needs to be preserved
'''
model:
  options:
    serial: false
  output:
    steps: ['apri', 'apos']
  transport:
    serial: false
observations:
  co2:
    units: ppm
'''

ymlContents = {
                        'model':
                            {'options':{'serial': False},
                              'output':{
                                                'steps': ['apri', 'apos']
                                                },
                              'transport':{'serial':False}
                            },
                        'observations':
                            {'co2':{'units': 'ppm'}
                            }
                        }

class AsLiteralString(str):
  pass

def representAsLiteralString(dumper, data):
  return dumper.represent_scalar("tag:yaml.org,2002:str", data, style="") 

yaml.add_representer(str, representAsLiteralString)
yaml.representer.SafeRepresenter.add_representer(str, representAsLiteralString)


try:
    def list2stringConverter(v):
        assert isinstance(v, list)
        myStr=""+chr(91) # [
        for elem in v:
            myStr+=chr(39)+elem+chr(39)+","  #  # '   ',
        rtrnStr=myStr[:-1]+chr(93) #"]"
        return (rtrnStr)  # rtrnStr is now as desired:    steps: ['apri', 'apos']

    def list2escapedStringConverter(v):
        assert isinstance(v, list)
        myStr=""+chr(92)+chr(91) # [
        for elem in v:
            myStr+=chr(39)+elem+chr(39)+","  #  # '   ',
        rtrnStr=myStr[:-1]+chr(92)+chr(93) #"]"
        return (rtrnStr)  # rtrnStr is now as desired:    steps: ['apri', 'apos']

    s=list2stringConverter(ymlContents['model']['output']['steps'])
    sEsc=list2escapedStringConverter(ymlContents['model']['output']['steps'])
    print(f"s={s}")  # all sweet so far
    print(f"sEsc={sEsc}")  # all sweet so far
    ymlContents['model']['output']['stepsStdStr']=s
    ymlContents['model']['output']['stepsLiteralStr']=AsLiteralString(s)
    print("standard dump:")
    yaml.dump(ymlContents, sys.stdout, default_flow_style=True)
    print("dump with default_flow_style=False:")
    yaml.dump(ymlContents, sys.stdout, default_flow_style=False)
    print("dump with default_flow_style=False and escaped square brackets:")
    ymlContents['model']['output']['stepsStdStr']=sEsc
    ymlContents['model']['output']['stepsLiteralStr']=AsLiteralString(sEsc)
    yaml.dump(ymlContents, sys.stdout, default_flow_style=False)
    # with open('outFile.yml', 'w') as outFile:
    '''  ...but we must have the following output format to be compatible to rc file format:
        model:
          output:
            steps: ['apri', 'apos']
    '''
    #    yaml.dump(ymlContents, outFile) #, default_flow_style=False)

except:
    sTxt="Fatal Error: Failed to dump yaml file."
print("Done.")

I read some 20 potential solutions, most of them on stackoverflow, but I could not achieve the desired output. Presently the different approaches from my code produce the following output:

s=['apri','apos']
sEsc=\['apri','apos'\]
standard dump:
{model: {options: {serial: false}, output: {steps: [apri, apos], stepsLiteralStr: !!python/object/new:__main__.AsLiteralString [
        '[''apri'',''apos'']'], stepsStdStr: '[''apri'',''apos'']'}, transport: {
      serial: false}}, observations: {co2: {units: ppm}}}
dump with default_flow_style=False:
model:
  options:
    serial: false
  output:
    steps:
    - apri
    - apos
    stepsLiteralStr: !!python/object/new:__main__.AsLiteralString
    - '[''apri'',''apos'']'
    stepsStdStr: '[''apri'',''apos'']'
  transport:
    serial: false
observations:
  co2:
    units: ppm
dump with default_flow_style=False and escaped square brackets:
model:
  options:
    serial: false
  output:
    steps:
    - apri
    - apos
    stepsLiteralStr: !!python/object/new:__main__.AsLiteralString
    - \['apri','apos'\]
    stepsStdStr: \['apri','apos'\]
  transport:
    serial: false
observations:
  co2:
    units: ppm
Done.

I have read that ruamel.yaml might do the trick, but I can't have it on the target system that I'm stuck with.

I have also had a look at Specifying styles for portions of a PyYAML dump, which looked promising, but I need a solution for any list entry at any nesting level, that is to say, I may not know the key name in advance. Thus that solution did not work for me.

Why is it so hard to write that list as a string literal or am I missing something really stupid here? Many thanks for any constructive ideas!

0

There are 0 best solutions below