использование конструктора pyyaml ​​или ruamel.yaml в качестве привязки к псевдониму

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

Я черпал вдохновение из Есть ли способ построить объект с помощью PyYAML Construction_mapping после завершения загрузки всех узлов? но все еще не нашел радости.

Ниже приведен пример кода:

class L2D(dict):
    def __repr__(self):
        return('L2D({})'.format(dict.__repr__(self)))

def l2d_constructor(loader, node):
    print("constructing")
    instance = L2D.__new__(L2D)
    yield instance
    state = loader.construct_sequence(node, deep=True)
    instance.__init__(state)

yaml.add_constructor(u'!l2d', l2d_constructor)

print(yaml.load('''
a: !l2d
  - [e, f]
  - [g, h]
'''))

print("============")

print(yaml.load('''
a: &other !l2d
  - [e, f]
  - [g, h]
b:
  <<: *other
  a: b
  c: d
'''))

Первая загрузка работает, но, хотя я ожидаю, что вывод второй загрузки будет

constructing
{'a': L2D({'g': 'h', 'e': 'f'}), 'b': {'a': 'b', 'g': 'h', 'e': 'f', 'c': 'd'}}

вместо этого я получаю

constructing
Traceback (most recent call last):
  File "test2.py", line 41, in <module>
    '''))
  File "/tmp/tmp.1oRXCix7X3/venv/lib/python3.5/site-packages/ruamel/yaml/main.py", line 86, in load
    return loader.get_single_data()
  File "/tmp/tmp.1oRXCix7X3/venv/lib/python3.5/site-packages/ruamel/yaml/constructor.py", line 56, in get_single_data
    return self.construct_document(node)
  File "/tmp/tmp.1oRXCix7X3/venv/lib/python3.5/site-packages/ruamel/yaml/constructor.py", line 65, in construct_document
    for dummy in generator:
  File "/tmp/tmp.1oRXCix7X3/venv/lib/python3.5/site-packages/ruamel/yaml/constructor.py", line 494, in construct_yaml_map
    value = self.construct_mapping(node)
  File "/tmp/tmp.1oRXCix7X3/venv/lib/python3.5/site-packages/ruamel/yaml/constructor.py", line 265, in construct_mapping
    self.flatten_mapping(node)
  File "/tmp/tmp.1oRXCix7X3/venv/lib/python3.5/site-packages/ruamel/yaml/constructor.py", line 240, in flatten_mapping
    % subnode.id, subnode.start_mark)
ruamel.yaml.constructor.ConstructorError: while constructing a mapping
  in "<unicode string>", line 8, column 3:
      <<: *other
      ^ (line: 8)
expected a mapping for merging, but found sequence
  in "<unicode string>", line 5, column 5:
      - [e, f]
        ^ (line: 5)

Печать constructing предполагает, что конструктор выполнил свою работу, но я подозреваю, что псевдоним пытается получить данные из неизмененного дерева yaml, а не результирующие данные из конструктора.

Есть ли способ заставить это работать?


person tommyvn    schedule 24.02.2017    source источник


Ответы (1)


Чтобы использовать функцию слияния YAML, ваш привязанный «тип» должен быть сопоставлением (Python dict), а пары ключ/значение этого сопоставления вставляются в другое сопоставление в точке, где вы это делаете:

<<: *other

Ваш привязанный тип представляет собой последовательность, и это не разрешено при использовании функции слияния.

Вам следует просмотреть документацию по слиянию, где видно, что привязанный тип всегда является сопоставлением.

person Anthon    schedule 24.02.2017
comment
Я понимаю, как работает функция сопоставления, тег !l2d и функция l2d_constructor изменяют последовательность a на dict, как вы можете видеть в результирующем объекте python. Я надеялся, что преобразование функции-конструктора из последовательности в сопоставление будет означать, что я мог бы обрабатывать значение a как сопоставление в yaml, но я все чаще думаю, что на этом этапе синтаксический анализ уже слишком поздний, что << обрабатывается до !l2d и соответствующую функцию-конструктор. - person tommyvn; 24.02.2017
comment
Да, интерпретация << выполняется синтаксическим анализатором, он берет последовательность, а не то, что из нее сделал тег. Одна вещь, которую вы можете сделать, это изменить << на что-то другое, уникальное (например, >>) и пометить сопоставление для загрузки объекта, похожего на словарь, который интерпретирует значение для >>. Или вы можете заменить средство слияния в constructor.py, чтобы поступить правильно. - person Anthon; 25.02.2017