Как разобрать вложенный словарь внутри списка в yaml?

Я разбираю файл YAML для поиска значений по любому ключу. В настоящее время я могу анализировать любой словарь на первом уровне, но не могу анализировать вложенные словари.

Я попытался изменить пример на https://stackoverflow.com/a/55608627, чтобы проанализировать словарь внутри списка. , однако это приводит к ошибке:

AttributeError: 'CommentedSeq' object has no attribute 'items'

Глядя на канонический вывод из http://yaml-online-parser.appspot.com/ это показывает, что есть карта и последовательность, которые я не смог объяснить.

Немодифицированная функция синтаксического анализа не выдает никаких ошибок, однако ничего не видит внутри списка.

Модифицированная функция синтаксического анализа возвращает указанную выше ошибку AttributeError.

Пример файла YAML: https://pastebin.com/BhwyPa7V

Полный проект: https://github.com/Just-Insane/helm-vault/blob/master/vault.py

Функция разбора (без изменений):

def dict_walker(node, pattern, path=None):
    path = path if path is not None else ""
    for key, value in node.items():
        if isinstance(value, dict):
            dict_walker(value, pattern=pattern, path=f"{path}/{key}")
        elif value == pattern:
            if action == "enc":
                node[key] = input(f"Input a value for {path}/{key}: ")
                vault_write(node[key], path, key)
            elif (action == "dec") or (action == "view") or (action == "edit"):
                value = vault_read(path, key)
                node[key] = value

Функция разбора (модифицированная):

def dict_walker(node, pattern, path=None):
    path = path if path is not None else ""
    for key, value in node.items():
        if isinstance(value, dict):
            dict_walker(value, pattern=pattern, path=f"{path}/{key}")
        elif isinstance(value, list):
            for item in value:
                for value in dict_walker(value, pattern=pattern, path=f"{path}/{key}"):
                    if value == pattern:
                        if action == "enc":
                            node[key] = input(f"Input a value for {path}/{key}: ")
                            vault_write(node[key], path, key)
                        elif (action == "dec") or (action == "view") or (action == "edit"):
                            value = vault_read(path, key)
                            node[key] = value
        elif value == pattern:
            if action == "enc":
                node[key] = input(f"Input a value for {path}/{key}: ")
                vault_write(node[key], path, key)
            elif (action == "dec") or (action == "view") or (action == "edit"):
                value = vault_read(path, key)
                node[key] = value

Ожидаемые результаты:

Вложенный словарь анализируется, и значения внутри могут быть успешно изменены.

Фактические результаты:

  1. При использовании немодифицированного кода значения внутри списка вообще не видны.

  2. При использовании модифицированного кода возникает ошибка атрибута, вызванная CommentedSeq. Непонятно, почему он не анализируется как список.


person Just-Insane    schedule 14.04.2019    source источник


Ответы (1)


Как я указал в ответе, на который вы ссылаетесь, синтаксический анализ полностью выполняется еще до возврата метода yaml.load(). Что вы делаете, так это просматриваете загруженные данные.

Ваш dict_walker() основан на функции find(), которая работает только для довольно неинтересного ввода YAML из вопроса, на который я ответил. Предполагается, что YAML:

состоит только из (простых) скаляров, которые являются строками, сопоставлениями и ключами сопоставления, которые являются скалярами.

Представленная там функция lookup может обрабатывать последовательности, так что это то, на чем вам нужно основывать свою функцию dict_walker() (и у этой функции есть подходящее имя: она может проходить только по словарям, поскольку вызов метода .items() на узле предполагает, что node это dict).

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

import sys
from pathlib import Path
import ruamel.yaml

in_file = Path('input.yaml')

action = "view"

def vault_read(path, key):
    # dummy function to show functionality
    vault = {
        ("/spec/acme", "email"): "repl_0",
        ("/spec/acme/dns01/providers/0/cloudflare", "email"): "repl_1",
        ("/spec/acme/dns01/providers/0/cloudflare/apiKeySecretRef", "key"): 42,
    }
    return vault.get((path, key), "not found")

def walk_data(node, pattern, path=None):
   if path is None:
       path = ""
   if isinstance(node, dict):
       for key, value in node.items():
           if value == pattern:
               if action == "enc":
                   node[key] = input(f"Input a value for {path}/{key}: ")
                   vault_write(node[key], path, key)
               elif (action == "dec") or (action == "view") or (action == "edit"):
                   node[key] = vault_read(path, key)
           else:
               walk_data(value, pattern, path=f"{path}/{key}")
   elif isinstance(node, list):
       for idx, item in enumerate(node):
           walk_data(item, pattern, path=f"{path}/{idx}")


yaml = ruamel.yaml.YAML()
data = yaml.load(in_file)

walk_data(data, "changeme")

yaml.dump(data, sys.stdout)

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

apiVersion: certmanager.k8s.io/v1alpha1
kind: ClusterIssuer
metadata:
  name: letsencrypt-production
spec:
  acme:
    # You must replace this email address with your own.
    # Let's Encrypt will use this to contact you about expiring
    # certificates, and issues related to your account.
    email: repl_0
    server: https://acme-v02.api.letsencrypt.org/directory
    privateKeySecretRef:
      # Secret resource used to store the account's private key.
      name: letsencrypt-production
    # Enable the HTTP01 challenge mechanism for this Issuer
    dns01:
      providers:
      - name: prod-cloudflare
        cloudflare:
          email: repl_1
          apiKeySecretRef:
            name: cloudflare-api-key-secret
            key: 42
person Anthon    schedule 14.04.2019