DeepCopy YAML с комментарием списка верхнего уровня в Python - PullRequest
0 голосов
/ 20 июня 2019

Я пытаюсь выгрузить следующий файл yaml в несколько (разных) файлов, используя ruamel.yaml:

C:
  f:
  # comment
  - - l1
    - l2: '5'

Если я попытаюсь выполнить deepcopy, комментарий приведет к ошибке дляверсия с глубоким копированием:

import copy

from ruamel.yaml import YAML

yaml = YAML()

conf = None
with open("input.yaml", 'r') as inf:
    conf = yaml.load(inf)
conf2 = copy.deepcopy(conf)

with open("out1.yaml", 'w') as outf:
    yaml.dump(conf, outf)

try:
    with open("out2.yaml", 'w') as outf:
        yaml.dump(conf2, outf)
    print("all good")
except AssertionError:
    raise SystemExit("Deep copy failed")

Есть ли альтернатива для выполнения выгрузки загруженного YAML с такими комментариями в более чем один файл?

1 Ответ

2 голосов
/ 23 июня 2019

Эта проблема в основном возникает из-за ruamel.yaml разработки не начинайте со спецификации, к которой относится структура данных вашего комментария # comment (и не имеет его сейчас). Дело в том, что это проще добавить комментарий к уже созданному узлу (т.е. что-то в Документ YAML до того, как парсер встретит комментарий), чем добавить его на следующий узел (который в конце документа может не отображаться вверх).

В вашем примере комментарий может быть связан с ключом f, чтобы первый элемент последовательности одного элемента или первый элемент из двойной последовательности элементов. ruamel.yaml пытается сделать некоторую перестановку, и это имеет в результате в прошлом в одном комментарии, связанные с два последовательных узла, и это то, что merge_comments пытается решить, посмотрев равны ли эти комментарии.

Это пробел, но это работает достаточно хорошо если вы используете ruamel.yaml по назначению «загрузить-изменить-сохранить», но для глубокого копирования не сохраняет эту ссылку, что приводит к неравенству и, следовательно, AssertionError

Быстрое и грязное решение для этого состоит в том, чтобы сделать merge_comments запретом:

import copy
from ruamel.yaml import YAML

yaml = YAML()

conf = None
with open("input.yaml", 'r') as inf:
    conf = yaml.load(inf)
conf2 = copy.deepcopy(conf)

with open("out1.yaml", 'w') as outf:
    yaml.dump(conf, outf)

yaml.representer.merge_comments = lambda x, y: None

try:
    with open("out2.yaml", 'w') as outf:
        yaml.dump(conf2, outf)
    print("all good")
except AssertionError:
    raise SystemExit("Deep copy failed")

, что дает:

all good

С содержанием out1.yaml:

C:
  f:
  # comment
  - - l1
    - l2: '5'

и out2.yaml:

C:
  f:
  # comment
  - - l1
    - l2: '5'

Эта проблема «улучшена» как __deepcopy__ для представление списка и отображение, делает глубокую копию атрибуты, которые содержат комментарий, поток, формат, привязку и т. д. информацию без следования совету в документации :

Если для реализации deepcopy () необходимо сделать глубокую копию компонент, он должен вызывать функцию deepcopy () с компонентом в качестве первого аргумента и словарь памятки в качестве второго аргумента.

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

# this documents has some non-trivial
# comment lines

# first item follows
- 42
# end of first item

# second item follows
- 196
# end of second item

# final comment of the document

В настоящее время вышеприведенное будет загружаться с тремя (многострочными) комментариями, но IMO было бы более уместно интерпретировать это как шесть комментариев. Основной задачей является определение разбиения таких комментариев без интерпретации значения комментария, а только с использованием пустых строк.

Кроме того, для назначения узла может учитываться уровень отступа комментария.

...