How to dump Scalar node

Viewed 49

I would like to get the following output in file configuration.yaml:

platform: manual
name: "AB"
code: !secret AB
arming_time: 15
delay_time: 15
trigger_time: 2
disarmed:
  trigger_time: 0
armed_home:
  arming_time: 0
  delay_time: 0

I need this output. However, when I use dump() for a dictionary with a node in the YAML file, I receive the following output (check the value for 'code'):

  armed_home:
    arming_time: 0
    delay_time: 0
  arming_time: 15
  code: !!python/object:yaml.nodes.ScalarNode
    end_mark: !!python/object:yaml.error.Mark
      buffer: null
      column: 24
      index: 101
      line: 5
      name: ch/standart/alarm_control_panel.yaml
      pointer: null
    start_mark: !!python/object:yaml.error.Mark
      buffer: null
      column: 10
      index: 87
      line: 5
      name: ch/standart/alarm_control_panel.yaml
      pointer: null
    style: null
    tag: '!secret'
    value: home_alarm
  delay_time: 15
  disarmed:
    trigger_time: 0
  name: Home Alarm
  platform: manual
  trigger_time: 2

My code is here for your reference. "secret" is a folder. Maybe, I need to create a new Class(). That will dump() only the tag and value of the node. So that, the output will be the same as the desired one.

import yaml
from typing import Any, IO
import json
import sys
import os
from pathlib import Path
import ast

class Loader(yaml.SafeLoader):
    """YAML Loader with `!include` constructor."""

    def __init__(self, stream: IO) -> None:
        """Initialise Loader."""

        try:
            self._root = os.path.split(stream.name)[0]
        except AttributeError:
            if AttributeError == IsADirectoryError:
                print('lol')
            else:
                self._root = os.path.curdir

        super().__init__(stream)


def construct_include(loader: Loader, node: yaml.Node) -> Any:
    """Include file referenced at node."""
    try:
        filename = os.path.abspath(os.path.join(loader._root, loader.construct_scalar(node)))
        extension = os.path.splitext(filename)[1].lstrip('.')
        return node
    except IsADirectoryError:
        print('lol')

yaml.add_constructor('!secret', construct_include, Loader)

p = os.path.abspath('configuration.yaml')

with open(p, 'r') as f:
    data = yaml.load(f, Loader=Loader)
d = {}
for i in data:
    d[i] = data[i]
file = open("configuration.yaml", "w")
yaml.dump(d, file)
1 Answers

You can use the built-in TaggedScalar and DoubleQuotedScalarString, to get exactly the output that you want:

import sys
import ruamel.yaml

TS = ruamel.yaml.comments.TaggedScalar
DS = ruamel.yaml.scalarstring.DoubleQuotedScalarString
            
d = dict(
  platform = 'manual',
  name =     DS('AB'),
  code = TS('AB', tag='!secret'),
  arming_time = 15,
  delay_time = 15,
  trigger_time = 2,
  disarmed = dict(trigger_time = 0),
  armed_home = dict(arming_time = 0, delay_time = 0),
)

yaml = ruamel.yaml.YAML()
# yaml.indent(mapping=2, sequence=4, offset=2) 
yaml.dump(d, sys.stdout)

which gives:

platform: manual
name: "AB"
code: !secret AB
arming_time: 15
delay_time: 15
trigger_time: 2
disarmed:
  trigger_time: 0
armed_home:
  arming_time: 0
  delay_time: 0

Your indentation of two positions is the default, so you don't need to call .indent().

You can stream to file using yaml.dump(d, Path('configuration.yaml'))

Related