69 lines
2.3 KiB
Python
69 lines
2.3 KiB
Python
# 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 json
|
|
|
|
import yaml
|
|
from yaml import constructor
|
|
from yaml import loader
|
|
from yaml import nodes
|
|
from yaml import parser
|
|
from yaml import resolver
|
|
|
|
ParserError = parser.ParserError
|
|
|
|
|
|
# NOTE(andreykurilin): Jinja2 uses __repr__ methods of objects while rendering
|
|
# templates. Such behaviour converts OrderedDict to the string like
|
|
# "OrderedDict([('foo', 'xxx'), ('bar', 'yyy')])"
|
|
# which breaks json/yaml load.
|
|
# In 99% of cases, we are rendering templates based on the dicts obtained
|
|
# after yaml.safe_load which uses collections.OrderedDict , so writing here
|
|
# the workaround with overridden __repr__ method looks like the best choice.
|
|
class OrderedDict(collections.OrderedDict):
|
|
"""collections.OrderedDict with __repr__ like in the regular dict."""
|
|
def __repr__(self):
|
|
return json.dumps(self, sort_keys=False)
|
|
|
|
|
|
def _construct_mapping(loader, node, deep=False):
|
|
keys = []
|
|
if isinstance(node, nodes.MappingNode):
|
|
for key_node, value_node in node.value:
|
|
key = loader.construct_object(key_node, deep=deep)
|
|
if key in keys:
|
|
raise constructor.ConstructorError(
|
|
"while constructing a mapping",
|
|
node.start_mark,
|
|
"the key (%s) is redefined" % key,
|
|
key_node.start_mark)
|
|
keys.append(key)
|
|
return OrderedDict(loader.construct_pairs(node))
|
|
|
|
|
|
class _SafeLoader(loader.SafeLoader):
|
|
pass
|
|
|
|
|
|
_SafeLoader.add_constructor(resolver.BaseResolver.DEFAULT_MAPPING_TAG,
|
|
_construct_mapping)
|
|
|
|
|
|
def safe_load(stream):
|
|
"""Load stream to create python object
|
|
|
|
:param stream: json/yaml stream.
|
|
:returns: dict object
|
|
"""
|
|
return yaml.load(stream, _SafeLoader)
|