1import yaml
2from yaml import ScalarNode, SequenceNode
3from six import string_types
4
5# This helper copied almost entirely from
6# https://github.com/aws/aws-cli/blob/develop/awscli/customizations/cloudformation/yamlhelper.py
7
8
9def yaml_parse(yamlstr):
10    """Parse a yaml string"""
11    yaml.SafeLoader.add_multi_constructor("!", intrinsics_multi_constructor)
12    return yaml.safe_load(yamlstr)
13
14
15def intrinsics_multi_constructor(loader, tag_prefix, node):
16    """
17    YAML constructor to parse CloudFormation intrinsics.
18    This will return a dictionary with key being the instrinsic name
19    """
20
21    # Get the actual tag name excluding the first exclamation
22    tag = node.tag[1:]
23
24    # Some intrinsic functions doesn't support prefix "Fn::"
25    prefix = "Fn::"
26    if tag in ["Ref", "Condition"]:
27        prefix = ""
28
29    cfntag = prefix + tag
30
31    if tag == "GetAtt" and isinstance(node.value, string_types):
32        # ShortHand notation for !GetAtt accepts Resource.Attribute format
33        # while the standard notation is to use an array
34        # [Resource, Attribute]. Convert shorthand to standard format
35        value = node.value.split(".", 1)
36
37    elif isinstance(node, ScalarNode):
38        # Value of this node is scalar
39        value = loader.construct_scalar(node)
40
41    elif isinstance(node, SequenceNode):
42        # Value of this node is an array (Ex: [1,2])
43        value = loader.construct_sequence(node)
44
45    else:
46        # Value of this node is an mapping (ex: {foo: bar})
47        value = loader.construct_mapping(node)
48
49    return {cfntag: value}
50