fuel-web/nailgun/nailgun/utils/uniondict.py

72 lines
2.1 KiB
Python

# Copyright 2016 Mirantis, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import collections
import itertools
import yaml
class UnionDict(collections.Mapping):
"""Object, which acts like read-only union of several dicts.
This is an object which acts like a deep merge of several
dicts. It's needed to replace real dict, merged with help
of utils.dict_merge in LCM code.
"""
def __init__(self, *dicts):
for d in dicts:
if not isinstance(d, dict):
raise ValueError("UnionDict can only be apllied to dicts.")
self.dicts = list(dicts)
self.dicts.reverse()
self.keys = lambda: set(itertools.chain.from_iterable(dicts))
def __getitem__(self, key):
values = []
for d in self.dicts:
try:
value = d[key]
except KeyError:
continue
values.append(value)
if not isinstance(value, dict):
return values[0]
if len(values) == 0:
raise KeyError(key)
elif len(values) == 1:
return values[0]
values.reverse()
return UnionDict(*values)
def __iter__(self):
return iter(self.keys())
def __len__(self):
return len(self.keys())
def __repr__(self):
items = ['{!r}: {!r}'.format(k, v) for k, v in self.items()]
return '{{{}}}'.format(', '.join(items))
def uniondict_representer(dumper, data):
return dumper.represent_mapping(u'tag:yaml.org,2002:map', data)
yaml.add_representer(UnionDict, uniondict_representer)