rally/rally/cli/yamlutils.py

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)