Path: blob/develop/awscli/customizations/cloudformation/yamlhelper.py
1567 views
# Copyright 2012-2015 Amazon.com, Inc. or its affiliates. All Rights Reserved.1#2# Licensed under the Apache License, Version 2.0 (the "License"). You3# may not use this file except in compliance with the License. A copy of4# the License is located at5#6# http://aws.amazon.com/apache2.0/7#8# or in the "license" file accompanying this file. This file is9# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF10# ANY KIND, either express or implied. See the License for the specific11# language governing permissions and limitations under the License.12from botocore.compat import json13from botocore.compat import OrderedDict1415import yaml16from yaml.resolver import ScalarNode, SequenceNode171819def intrinsics_multi_constructor(loader, tag_prefix, node):20"""21YAML constructor to parse CloudFormation intrinsics.22This will return a dictionary with key being the intrinsic name23"""2425# Get the actual tag name excluding the first exclamation26tag = node.tag[1:]2728# Some intrinsic functions doesn't support prefix "Fn::"29prefix = "Fn::"30if tag in ["Ref", "Condition"]:31prefix = ""3233cfntag = prefix + tag3435if tag == "GetAtt" and isinstance(node.value, str):36# ShortHand notation for !GetAtt accepts Resource.Attribute format37# while the standard notation is to use an array38# [Resource, Attribute]. Convert shorthand to standard format39value = node.value.split(".", 1)4041elif isinstance(node, ScalarNode):42# Value of this node is scalar43value = loader.construct_scalar(node)4445elif isinstance(node, SequenceNode):46# Value of this node is an array (Ex: [1,2])47value = loader.construct_sequence(node)4849else:50# Value of this node is an mapping (ex: {foo: bar})51value = loader.construct_mapping(node)5253return {cfntag: value}545556def _dict_representer(dumper, data):57return dumper.represent_dict(data.items())585960def yaml_dump(dict_to_dump):61"""62Dumps the dictionary as a YAML document63:param dict_to_dump:64:return:65"""66FlattenAliasDumper.add_representer(OrderedDict, _dict_representer)67return yaml.dump(68dict_to_dump,69default_flow_style=False,70Dumper=FlattenAliasDumper,71)727374def _dict_constructor(loader, node):75# Necessary in order to make yaml merge tags work76loader.flatten_mapping(node)77return OrderedDict(loader.construct_pairs(node))787980class SafeLoaderWrapper(yaml.SafeLoader):81"""Isolated safe loader to allow for customizations without global changes.82"""8384pass8586def yaml_parse(yamlstr):87"""Parse a yaml string"""88try:89# PyYAML doesn't support json as well as it should, so if the input90# is actually just json it is better to parse it with the standard91# json parser.92return json.loads(yamlstr, object_pairs_hook=OrderedDict)93except ValueError:94loader = SafeLoaderWrapper95loader.add_constructor(yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG,96_dict_constructor)97loader.add_multi_constructor("!", intrinsics_multi_constructor)98return yaml.load(yamlstr, loader)99100101class FlattenAliasDumper(yaml.SafeDumper):102def ignore_aliases(self, data):103return True104105106