Как я могу получить родительский узел в yaml.loader add_contructor? - PullRequest
1 голос
/ 31 марта 2019

Я создаю шаблон YAML, который позволит использовать определенные !Tags.Способ обработки тегов при загрузке файла YAML в python заключается в добавлении конструктора: yaml.add_constructor.Я хотел бы добавить конструктор ScalarNode, но возвращаемое значение должно основываться на данных, которые будут присутствовать в качестве родственного элемента оцениваемого узла.

def sibling_constructor(loader, node):
    value = loader.construct_scalar(node)
    # How to get parent node's child nodes?
    return value + ' are dangerous.'

yaml.add_constructor('!Sibling', sibling_constructor)    
return yaml.load(myFile)

Yaml:

Thing:
  ParentThing:
    Child1: Fast cars
    Child2: Slow cars
    Child3: !Sibling Child1

После обработки файла YAML я ожидаю, что словарь в памяти будет выглядеть примерно так:

{
  'Thing': {
    'ParentThing': {
        'Child1': 'Fast cars',
        'Child2': 'Slow cars',
        'Child3': 'Fast cars are dangerous.'
    }
  }
}

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

1 Ответ

0 голосов
/ 31 марта 2019

В модуле YAML нет функций для доступа к брату или родитель из sibling_constructor, как это должно быть доступно через параметр loader или node.

Параметр node является экземпляром Node и его атрибуты tag, value, start_mark, end_mark, comment, anchor не содержат контекстной информации (mark s содержат указатели на источник stream, comment возможный комментарий конца строки в этой строке).

То, что параметр loader не имеет этой информации, гораздо меньше Понятно, что это гораздо более сложная структура данных. А то что ты наверное не понимаю, что процесс строительства это глубина во-первых, так ваш родитель не был построен, и поэтому есть нет точки входа в структуру данных, загруженную из YAML, пока не будет обработан последний узел, после чего его родитель может быть полностью создан, затем его родитель и т.д., пока не закончится рекурсия и построенные данные не функция load().


Но не вся надежда потеряна, если вы используете загрузчик туда и обратно по умолчанию, у вас есть доступ к тегу без необходимости добавления конструктора, чтобы вы могли легко выполнить этап последующей обработки.
Предполагая, что ваш ввод находится в файле input.yaml:

import sys
from pprint import pprint
from pathlib import Path
import ruamel.yaml

input_path = Path('input.yaml')

def sibling(d):
    # recurse into a data structure loaded from YAML
    if isinstance(d, dict):
        for k, v in d.items():
            try:
                if v._yaml_tag.value == '!Sibling':
                    d[k] = d[v.value] + ' are dangerous.'
            except (AttributeError, ):
                pass
            sibling(v)
    elif isinstance(d, list):
        for item in d:
            sibling(item)



yaml = ruamel.yaml.YAML()
data = yaml.load(input_path)
sibling(data)

pprint(data)
print('-------')
yaml.dump(data, sys.stdout)

дает:

{'Thing': {'ParentThing': {'Child1': 'Fast cars',
                           'Child2': 'Slow cars',
                           'Child3': 'Fast cars are dangerous.'}}}
-------
Thing:
  ParentThing:
    Child1: Fast cars
    Child2: Slow cars
    Child3: Fast cars are dangerous.

Хотя можно зарегистрировать новый метод, который создает отображение (construct_yaml_map) и присоединяет dict существо созданный для экземпляра Constructor, этот dict, однако, получает обновляется за один раз, когда все пары ключ / значение сопоставления были Решили:

def construct_yaml_map(self, node):
    data = self.yaml_base_dict_type()  # type: Dict[Any, Any]
    self._tw_current_dict = data
    yield data
    value = self.construct_mapping(node)
    data.update(value)

ruamel.yaml.SafeConstructor.add_constructor(u'tag:yaml.org,2002:map', construct_yaml_map)

yaml.add_constructor('!Sibling', sibling_constructor, constructor=ruamel.yaml.SafeConstructor)
return yaml.safe_load(myFile)

, поэтому, когда вы добавите вышеприведенный код в ваш код и ваш sibling_constructor будет называться loader._tw_current_dict доступен, но пустой.

(Вы должны использовать SafeLoader, нет необходимости делать все небезопасным, используя старый метод load, унаследованный от PyYAML)

Возможно, вы сможете подключиться к construct_mapping, я кратко попробовал и застрял (и я утверждаю, что довольно хорошо знаком с исходным кодом модуля YAML).


Одна вещь, которую вы могли бы рассмотреть, это изменить ваш ввод YAML так:

Thing:
  ParentThing:
    Child1: &child Fast cars
    Child2: Slow cars
    Child3: !Sibling *child

т.е. используя нормальный механизм ссылок в YAML якорей и псевдонимов. Это псевдоним *child конечно можно разрешить в момент создания значения для Child3.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...