Ruamel.yaml: Как сохранить структуру dict в YAML

Я использую ruamel.yaml для редактирования файлов YAML и их дампа. Мне нужна помощь в том, как сохранить структуру исходного файла,

У меня есть файл YAML с содержимым ниже, однако это содержимое не изменяется, но когда я загружаю и выгружаю его после редактирования, структура этого содержимого меняется

    parameters: {
      "provision_pipeline": "provision-integrations",
      "enable_sfcd_ds_argo_operator": "false",
      "clustermanagement_helm_values_path": "sam/sam-helm-charts/kube-node-recycler-0.0.4-273/values.nodepool.yaml"
    }

Однако после того, как я выгружу, структура этого изменится на следующий формат:

    parameters: {"provision_pipeline": "provision-integrations", "enable_sfcd_ds_argo_operator": "false",
      "clustermanagement_helm_values_path": "sam/sam-helm-charts/kube-node-recycler-0.0.4-273/values.nodepool.yaml"}

Код:

def addTargetToBaseIntegFileAndUpdate(deploymentTarget, fi, env, samvmf_repo, folder, pipelineversionintegration, basefile):
    yamldata = OrderedDict()
    ryaml = rumel.yaml.YAML()
    ryaml.preserve_quotes = True
    ryaml.default_flow_style = False
    ryaml.indent(mapping=2)
        
    with open(basefile, "r") as file:
        yamldata = ryaml.load(file)
        deploymentTargets = yamldata["targets"]["stagger_groups"]
        target = ""
        doesFIExist = False
        fi_index = 0

        for index, sg in enumerate(deploymentTargets):
            if sg["name"] == env.lower():
                target = deploymentTargets[index]
                for i, fi_item in enumerate(target["falcon_instances"]):
                    if fi_item["name"] == fi.lower():
                        fi_index = i
                        doesFIExist = True
                        break
                if doesFIExist:
                    yamldata["targets"]["stagger_groups"][index]["f_instances"][fi_index]["f_domains"].append(deploymentTarget["f_instances"][0]["f_domains"][0])
                else:
                    yamldata["targets"]["stagger_groups"][index]["f_instances"].append(deploymentTarget["f_instances"][0])
                break

    with open(basefile, "w") as fileobj:
        ryaml.dump(yamldata, fileobj)

Это не структурное изменение, это просто изменение форматирования. Я не знаком с тем, какие параметры форматирования предлагает ruamel.yaml, но вам нужно изучить это, чтобы контролировать форматирование выходных пробелов.

Nick Bailey 21.03.2022 18:21

У вас есть два сопоставления в вашем YAML, одно с одним ключом (parameters) и одно с тремя ключами (provision_pipeline) и т. д. Первое сопоставление — это блочный стиль, второе — стиль потока. Какая программа заставляет вас сохранять это несоответствие?

Anthon 21.03.2022 19:43
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
0
2
63
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Как указал Ник Бейли в комментариях, это стилистическое изменение, а не структурное. То есть данные одни и те же, просто представлены по-разному.

Теперь, что касается этого стиля, YAML имеет два стиля представления структур данных:

  • Стиль блока: Каждый ключ/значение начинается с новой строки, а списки и словари (сопоставления) запускаются и останавливаются с помощью отступа. Обычно это предпочтительный стиль, так как он более удобочитаем.

  • Стиль потока: списки/сопоставления начинаются и заканчиваются квадратными скобками, а несколько ключей/значений разделяются запятыми, как в JSON. Разрывы строк не требуются между парами ключ/значение, но также не запрещены. Этот формат чаще используется для небольших и простых структур данных, особенно в одной строке, поскольку он может сэкономить место.

Исходный YAML, который вы показали, представляет собой одну пару ключ/значение в более крупном отображении блочного стиля, но само значение имеет блочный стиль не; это просто стиль потока с добавлением дополнительных разрывов строк. Я думаю, вы, вероятно, захотите этого, полностью в блочном стиле:

test:
  parameters:
    "provision_pipeline": "provision-integrations"
    "enable_sfcd_ds_argo_operator": "false"
    "clustermanagement_helm_values_path": "sam/sam-helm-charts/kube-node-recycler-0.0.4-273/values.nodepool.yaml"

ruamel.yaml в режиме по умолчанию (туда и обратно) сохраняет стиль потока или блока, в зависимости от того, что вы ему зададите, но я не знаю, как заставить его запоминать определенные разрывы строк, которые вы добавили в разделе потока. Посмотрите это сравнение:

import sys
from ruamel.yaml import YAML

yaml_string_1 = """\
test:
  parameters: {
    "provision_pipeline": "provision-integrations",
    "enable_sfcd_ds_argo_operator": "false",
    "clustermanagement_helm_values_path": "sam/sam-helm-charts/kube-node-recycler-0.0.4-273/values.nodepool.yaml"
  }
"""
yaml_string_2 = """\
test:
  parameters:
    "provision_pipeline": "provision-integrations"
    "enable_sfcd_ds_argo_operator": "false"
    "clustermanagement_helm_values_path": "sam/sam-helm-charts/kube-node-recycler-0.0.4-273/values.nodepool.yaml"
"""

yaml = YAML()
for yaml_string in [yaml_string_1, yaml_string_2]:
    output = yaml.load(yaml_string)
    yaml.dump(output, sys.stdout)
    print()

Выход:

test:
  parameters: {provision_pipeline: provision-integrations, enable_sfcd_ds_argo_operator: 'false',
    clustermanagement_helm_values_path: sam/sam-helm-charts/kube-node-recycler-0.0.4-273/values.nodepool.yaml}

test:
  parameters:
    provision_pipeline: provision-integrations
    enable_sfcd_ds_argo_operator: 'false'
    clustermanagement_helm_values_path: sam/sam-helm-charts/kube-node-recycler-0.0.4-273/values.nodepool.yaml

Вы также можете, конечно, добавить preserve_quotes и любые другие опции, которые вам нужны.

Оригинальный YAML не является потоковым. Это блочный стиль для сопоставления корневого уровня (с одним ключом parameters) и стиль потока для сопоставления, которое является значением этого ключа.

Anthon 21.03.2022 19:45

Спасибо, что указали на мою двусмысленность, уточню.

CrazyChucky 21.03.2022 19:48

Я не хотел быть педантичным. Но это кажется значительным, так как я понимаю проблему, если OP обрабатывает данные дальше, например. анализатор JSON, который понимает только полный стиль потока. Но это наполовину, так что на самом деле нет четкой причины, почему бы не сделать его полностью блочным, как вы предлагаете.

Anthon 21.03.2022 20:19
Ответ принят как подходящий

ruamel.yaml не сохраняет новые строки между элементами сопоставления стилей потока. Единственное на них влияет yaml.width, поэтому вы получаете перенос строк, которые становятся слишком длинными. Например. с вашим вводом, если вы установите ширину на 40, вы получите:

parameters: {"provision_pipeline": "provision-integrations",
  "enable_sfcd_ds_argo_operator": "false",
  "clustermanagement_helm_values_path": "sam/sam-helm-charts/kube-node-recycler-0.0.4-273/values.nodepool.yaml"}

Но нет элемента управления, который даст вам первую пару ключ-значение на новой строке или закрытие. фигурная скобка на отдельной строке.

Ваше добавление ryaml.default_flow_style = False влияет только на совершенно новые диктовки и списки, которые вы добавляете в структура данных.

Вам следует подумать о переходе на блочный стиль и отбросить все несущественные кавычки, что делает YAML и менее подробный, и более читаемый. Для программы, которая загружает данные, это не имеет значения, и преобразование легко выполняется путем загрузки в обычном безопасном режиме (который не устанавливает информацию о блочном/потоковом стиле). на загруженных данных):

import sys
import pathlib
import ruamel.yaml

basefile = pathlib.Path('input.yaml')

data = ruamel.yaml.YAML(typ='safe').load(basefile)
yaml = ruamel.yaml.YAML()
yaml.dump(data, sys.stdout)

который дает:

parameters:
  provision_pipeline: provision-integrations
  enable_sfcd_ds_argo_operator: 'false'
  clustermanagement_helm_values_path: sam/sam-helm-charts/kube-node-recycler-0.0.4-273/values.nodepool.yaml

Строковый скаляр 'false' должен быть заключен в кавычки, чтобы его не путали с логическим значением false.

Если вышеуказанное улучшение неприемлемо, т.е. если дальнейшая обработка выполняется с чем-то кроме полного синтаксического анализатора YAML, вы можете выполнить постобработку вывода:

import sys
import pathlib
import ruamel.yaml

basefile = pathlib.Path('input.yaml')

def splitflowmap(s):
    res = []
    for line in s.splitlines():
        if ': {' in line and line[-1] == '}':
            start, rest = line.split(': {', 1)
            start = start + ': {'
            indent = '  '  # two spaces more than the start
            for idx, ch in enumerate(start):
                if ch != ' ':
                    break
                indent += ' '
            res.append(start)
            rest = rest[:-1]  # cut of }\n
            for x in rest.split(', '):  # if you always have quotes it is safer to split on '", "'
                res.append(f'{indent}{x},')
            res[-1] = res[-1][:-1]  # delete trailing comma
            res.append(f'{indent[2:]}}}')  # re-add the cut of }\n on a line of its own
            continue
        res.append(line)
    return '\n'.join(res) + '\n'

yaml = ruamel.yaml.YAML()
yaml.preserve_quotes = True
yaml.width = 2**16
data = yaml.load(basefile)
yaml.dump(data, sys.stdout, transform=splitflowmap)

который дает:

parameters: {
  "provision_pipeline": "provision-integrations",
  "enable_sfcd_ds_argo_operator": "false",
  "clustermanagement_helm_values_path": "sam/sam-helm-charts/kube-node-recycler-0.0.4-273/values.nodepool.yaml"
}

Другие вопросы по теме