如何通过字典递归并动态更新值,以便以后可以引用它们?

时间:2017-02-09 22:49:08

标签: python json dictionary recursion yaml

我正在尝试编写一个配置变量引擎,该引擎接受一个YAML文件(包含AWS配置变量)作为输入并转换为JSON,以便可以将其上传到HTTP k / v API(例如Consul)。我难倒的一个特性将允许开发人员在后续密钥中“包含”密钥集(用下划线标识,在最终有效负载中省略)。示例如下:

# Region
us-east-1:
  # Any key preceded by an underscore (_) is considered a "tag group" and will not be uploaded to Consul KV unless explicitly included.
  _taggroup1:
    key1: value1
    key2: value2
    key3: value3
  _taggroup2:
    key4: value1
    key5: value2
    key6: value3

  dev:
    _include: us-east-1/_taggroup1
  qa:
    _include:
      - us-east-1/_taggroup1
      - us-east-1/_taggroup2
    key6: baz
  prod:
    _include:
      - us-east-1/_taggroup1
      - us-east-1/_taggroup2

us-west-1:
  _taggroup1:
    key1: value1
    key2: value2
    key3: value3
  _taggroup2:
    key4: value1
    key5: value2
    key6: value3

  dev:
    _include:
      - us-west-1/_taggroup1
  qa:
    _include:
      - us-west-1/_taggroup1
      - us-west-1/_taggroup2
    key2: foo
  prod:
    _include:
      - us-west-1/_taggroup1
      - us-west-1/_taggroup2
    key4: foo
    key5: bar
    key1: undef

  us-west-1a:
    qa:
      _include: us-west-1/qa
    prod:
      _include: us-west-1/prod

  us-west-1b:
    _include: us-west-1/us-west-1a

正如您所看到的,我正在尝试构建一个配置文件,允许开发人员对变量进行分组,并在需要时随后包含/覆盖它们。

到目前为止,我为此实验编写的代码本质上是您的标准递归函数,其中添加了特定于此应用程序的内容:

# parse_input is a separate function that converts a YAML stream into
# an OrderedDict
original_dict = parse_input(stream1)

def print_dict(input_dict):

    new_dict = collections.OrderedDict()

    for key, value in input_dict.iteritems():
        if key.startswith('_'):
            if key == '_include':
                if isinstance(value, list):
                    for item in value:
                        x = dpath.util.get(original_dict, item)
                        for k, v in x.iteritems():
                            new_dict[k] = v
                else:
                    x = dpath.util.get(original_dict, value)
                    for k, v in x.iteritems():
                        new_dict[k] = v
            else:
                continue
            continue
        elif isinstance(value, dict):
            new_dict[key] = print_dict(value)
        else:
            new_dict[key] = value
    return new_dict

到目前为止我所取得的成果是这样的:

{
    "us-east-1": {
        "dev": {
            "key1": "value1", 
            "key2": "value2", 
            "key3": "value3"
        }, 
        "qa": {
            "key1": "value1", 
            "key2": "value2", 
            "key3": "value3", 
            "key4": "value1", 
            "key5": "value2", 
            "key6": "baz"
        }, 
        "prod": {
            "key1": "value1", 
            "key2": "value2", 
            "key3": "value3", 
            "key4": "value1", 
            "key5": "value2", 
            "key6": "value3"
        }
    }, 
    "us-west-1": {
        "dev": {
            "key1": "value1", 
            "key2": "value2", 
            "key3": "value3"
        }, 
        "qa": {
            "key1": "value1", 
            "key2": "foo", 
            "key3": "value3", 
            "key4": "value1", 
            "key5": "value2", 
            "key6": "value3"
        }, 
        "prod": {
            "key1": "undef", 
            "key2": "value2", 
            "key3": "value3", 
            "key4": "foo", 
            "key5": "bar", 
            "key6": "value3"
        }, 
        "us-west-1a": {
            "qa": {
                "_include": [
                    "us-west-1/_taggroup1", 
                    "us-west-1/_taggroup2"
                ], 
                "key2": "foo"
            }, 
            "prod": {
                "_include": [
                    "us-west-1/_taggroup1", 
                    "us-west-1/_taggroup2"
                ], 
                "key4": "foo", 
                "key5": "bar", 
                "key1": "undef"
            }
        }, 
        "us-west-1b": {
            "qa": {
                "_include": "us-west-1/qa"
            }, 
            "prod": {
                "_include": "us-west-1/prod"
            }
        }
    }
}

正如你所看到的,我似乎已经到了一半。我的问题是,在我最初的实验中,我通过在引用包含集时引用函数中的original_dict变量来获得有利结果(使用dpath返回键)。这很快就会变成一个问题,因为函数会更深入地解析(例如,在这种情况下是特定于AZ的变量),因为我不知道如何动态更新原始字典中的键,或者以其他方式跟踪更改,因此函数将注入带有_include键的密钥集,无法重新评估它们。

如何根据引用原始字典消除,而是动态跟踪更改,以便在树中更深入地正确评估_include个键?

1 个答案:

答案 0 :(得分:1)

我认为此代码可以解决您遇到的问题。关键更改是使用print_dict的结果递归到dpath。我还崩溃了一些冗余的代码。

<强>代码:

import yaml
import collections
import json
import dpath

with open('data.yml', 'rb') as f:
    original_dict = yaml.load(f)

def print_dict(input_dict):

    new_dict = collections.OrderedDict()

    for key, value in input_dict.iteritems():
        if key.startswith('_'):
            if key == '_include':
                if not isinstance(value, list):
                    value = [value]
                for item in value:
                    x = print_dict(dpath.util.get(original_dict, item))
                    for k, v in x.iteritems():
                        new_dict[k] = v
        elif isinstance(value, dict):
            new_dict[key] = print_dict(value)
        else:
            new_dict[key] = value
    return new_dict

print(json.dumps(print_dict(original_dict), indent=2))

<强>输出:

{
  "us-east-1": {
    "qa": {
      "key3": "value3", 
      "key2": "value2", 
      "key1": "value1", 
      "key6": "baz", 
      "key5": "value2", 
      "key4": "value1"
    }, 
    "prod": {
      "key3": "value3", 
      "key2": "value2", 
      "key1": "value1", 
      "key6": "value3", 
      "key5": "value2", 
      "key4": "value1"
    }, 
    "dev": {
      "key3": "value3", 
      "key2": "value2", 
      "key1": "value1"
    }
  }, 
  "us-west-1": {
    "qa": {
      "key2": "value2", 
      "key3": "value3", 
      "key1": "value1", 
      "key6": "value3", 
      "key5": "value2", 
      "key4": "value1"
    }, 
    "us-west-1b": {
      "qa": {
        "key2": "value2", 
        "key3": "value3", 
        "key1": "value1", 
        "key6": "value3", 
        "key5": "value2", 
        "key4": "value1"
      }, 
      "prod": {
        "key1": "value1", 
        "key3": "value3", 
        "key2": "value2", 
        "key6": "value3", 
        "key5": "bar", 
        "key4": "foo"
      }
    }, 
    "prod": {
      "key1": "value1", 
      "key3": "value3", 
      "key2": "value2", 
      "key6": "value3", 
      "key5": "bar", 
      "key4": "foo"
    }, 
    "us-west-1a": {
      "qa": {
        "key2": "value2", 
        "key3": "value3", 
        "key1": "value1", 
        "key6": "value3", 
        "key5": "value2", 
        "key4": "value1"
      }, 
      "prod": {
        "key1": "value1", 
        "key3": "value3", 
        "key2": "value2", 
        "key6": "value3", 
        "key5": "bar", 
        "key4": "foo"
      }
    }, 
    "dev": {
      "key3": "value3", 
      "key2": "value2", 
      "key1": "value1"
    }
  }
}