We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
method to load yaml from a string
*cfn-lint version: cfn-lint 0.4.2*

*Description of issue.*

When consuming cfn-lint as a module there is currently only an interface to load yaml from a file(`cfnlint.core.decode.cfn_yaml.load`). In our project([taskcat](https://github.com/aws-quickstart/taskcat/)) we have a need to load templates from strings as well. 

While we could just do something like below, think it would be useful to others to have a simpler interface for yaml strings:

```python
from cfnlint.decode.cfn_yaml import MarkedLoader, multi_constructor
loader = MarkedLoader(my_yaml_string, None)
loader.add_multi_constructor('!', multi_constructor)
template = loader.get_single_data()
```


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `src/cfnlint/decode/cfn_yaml.py`
Content:
```
1 """
2   Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 
4   Permission is hereby granted, free of charge, to any person obtaining a copy of this
5   software and associated documentation files (the "Software"), to deal in the Software
6   without restriction, including without limitation the rights to use, copy, modify,
7   merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
8   permit persons to whom the Software is furnished to do so.
9 
10   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
11   INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
12   PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
13   HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
14   OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
15   SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
16 """
17 import logging
18 import six
19 from yaml.composer import Composer
20 from yaml.reader import Reader
21 from yaml.scanner import Scanner
22 from yaml.resolver import Resolver
23 from yaml import ScalarNode
24 from yaml import SequenceNode
25 from yaml import MappingNode
26 from yaml.constructor import SafeConstructor
27 from yaml.constructor import ConstructorError
28 import cfnlint
29 from cfnlint.decode.str_node import str_node
30 
31 try:
32     from yaml.cyaml import CParser as Parser  # pylint: disable=ungrouped-imports
33     cyaml = True
34 except ImportError:
35     from yaml.parser import Parser  # pylint: disable=ungrouped-imports
36     cyaml = False
37 
38 UNCONVERTED_SUFFIXES = ['Ref', 'Condition']
39 FN_PREFIX = 'Fn::'
40 
41 LOGGER = logging.getLogger(__name__)
42 
43 
44 class CfnParseError(ConstructorError):
45     """
46     Error thrown when the template contains Cfn Error
47     """
48     def __init__(self, filename, message, line_number, column_number, key=' '):
49 
50         # Call the base class constructor with the parameters it needs
51         super(CfnParseError, self).__init__(message)
52 
53         # Now for your custom code...
54         self.filename = filename
55         self.line_number = line_number
56         self.column_number = column_number
57         self.message = message
58         self.match = cfnlint.Match(
59             line_number + 1, column_number + 1, line_number + 1,
60             column_number + 1 + len(key), filename, cfnlint.ParseError(), message=message)
61 
62 
63 class NodeConstructor(SafeConstructor):
64     """
65     Node Constructors for loading different types in Yaml
66     """
67 
68     def __init__(self, filename):
69         # Call the base class constructor
70         super(NodeConstructor, self).__init__()
71 
72         self.filename = filename
73 
74     # To support lazy loading, the original constructors first yield
75     # an empty object, then fill them in when iterated. Due to
76     # laziness we omit this behaviour (and will only do "deep
77     # construction") by first exhausting iterators, then yielding
78     # copies.
79     def construct_yaml_map(self, node):
80 
81         # Check for duplicate keys on the current level, this is not desirable
82         # because a dict does not support this. It overwrites it with the last
83         # occurance, which can give unexpected results
84         mapping = {}
85         for key_node, value_node in node.value:
86             key = self.construct_object(key_node, False)
87             value = self.construct_object(value_node, False)
88 
89             if key in mapping:
90                 raise CfnParseError(
91                     self.filename,
92                     'Duplicate resource found "{}" (line {})'.format(key, key_node.start_mark.line + 1),
93                     key_node.start_mark.line, key_node.start_mark.column, key)
94             mapping[key] = value
95 
96         obj, = SafeConstructor.construct_yaml_map(self, node)
97         return obj
98 
99     def construct_yaml_str(self, node):
100         obj = SafeConstructor.construct_yaml_str(self, node)
101         assert isinstance(obj, (six.string_types))
102         return str_node(obj, node.start_mark, node.end_mark)
103 
104     def construct_yaml_null_error(self, node):
105         """Throw a null error"""
106         raise CfnParseError(
107             self.filename,
108             'Null value at line {0} column {1}'.format(node.start_mark.line, node.start_mark.column),
109             node.start_mark.line, node.start_mark.column, ' ')
110 
111 
112 NodeConstructor.add_constructor(
113     u'tag:yaml.org,2002:map',
114     NodeConstructor.construct_yaml_map)
115 
116 NodeConstructor.add_constructor(
117     u'tag:yaml.org,2002:str',
118     NodeConstructor.construct_yaml_str)
119 
120 NodeConstructor.add_constructor(
121     u'tag:yaml.org,2002:null',
122     NodeConstructor.construct_yaml_null_error)
123 
124 
125 class MarkedLoader(Reader, Scanner, Parser, Composer, NodeConstructor, Resolver):
126     """
127     Class for marked loading YAML
128     """
129     # pylint: disable=non-parent-init-called,super-init-not-called
130     def __init__(self, stream, filename):
131         Reader.__init__(self, stream)
132         Scanner.__init__(self)
133         if cyaml:
134             Parser.__init__(self, stream)
135         else:
136             Parser.__init__(self)
137         Composer.__init__(self)
138         SafeConstructor.__init__(self)
139         Resolver.__init__(self)
140         NodeConstructor.__init__(self, filename)
141 
142 
143 def multi_constructor(loader, tag_suffix, node):
144     """
145     Deal with !Ref style function format
146     """
147 
148     if tag_suffix not in UNCONVERTED_SUFFIXES:
149         tag_suffix = '{}{}'.format(FN_PREFIX, tag_suffix)
150 
151     constructor = None
152 
153     if tag_suffix == 'Fn::GetAtt':
154         constructor = construct_getatt
155     elif isinstance(node, ScalarNode):
156         constructor = loader.construct_scalar
157     elif isinstance(node, SequenceNode):
158         constructor = loader.construct_sequence
159     elif isinstance(node, MappingNode):
160         constructor = loader.construct_mapping
161     else:
162         raise 'Bad tag: !{}'.format(tag_suffix)
163 
164     return {tag_suffix: constructor(node)}
165 
166 
167 def construct_getatt(node):
168     """
169     Reconstruct !GetAtt into a list
170     """
171 
172     if isinstance(node.value, (six.text_type, six.string_types)):
173         return node.value.split('.')
174     if isinstance(node.value, list):
175         return [s.value for s in node.value]
176 
177     raise ValueError('Unexpected node type: {}'.format(type(node.value)))
178 
179 
180 def load(filename):
181     """
182     Load the give YAML file
183     """
184     fp = open(filename)
185     loader = MarkedLoader(fp.read(), filename)
186     loader.add_multi_constructor('!', multi_constructor)
187     template = loader.get_single_data()
188     # Convert an empty file to an empty dict
189     if template is None:
190         template = {}
191 
192     return template
193
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/src/cfnlint/decode/cfn_yaml.py b/src/cfnlint/decode/cfn_yaml.py
--- a/src/cfnlint/decode/cfn_yaml.py
+++ b/src/cfnlint/decode/cfn_yaml.py
@@ -177,12 +177,11 @@
     raise ValueError('Unexpected node type: {}'.format(type(node.value)))
 
 
-def load(filename):
+def loads(yaml_string, fname=None):
     """
-    Load the give YAML file
+    Load the given YAML string
     """
-    fp = open(filename)
-    loader = MarkedLoader(fp.read(), filename)
+    loader = MarkedLoader(yaml_string, fname)
     loader.add_multi_constructor('!', multi_constructor)
     template = loader.get_single_data()
     # Convert an empty file to an empty dict
@@ -190,3 +189,11 @@
         template = {}
 
     return template
+
+
+def load(filename):
+    """
+    Load the given YAML file
+    """
+    fp = open(filename)
+    return loads(fp.read(), filename)

{"golden_diff": "diff --git a/src/cfnlint/decode/cfn_yaml.py b/src/cfnlint/decode/cfn_yaml.py\n--- a/src/cfnlint/decode/cfn_yaml.py\n+++ b/src/cfnlint/decode/cfn_yaml.py\n@@ -177,12 +177,11 @@\n     raise ValueError('Unexpected node type: {}'.format(type(node.value)))\n \n \n-def load(filename):\n+def loads(yaml_string, fname=None):\n     \"\"\"\n-    Load the give YAML file\n+    Load the given YAML string\n     \"\"\"\n-    fp = open(filename)\n-    loader = MarkedLoader(fp.read(), filename)\n+    loader = MarkedLoader(yaml_string, fname)\n     loader.add_multi_constructor('!', multi_constructor)\n     template = loader.get_single_data()\n     # Convert an empty file to an empty dict\n@@ -190,3 +189,11 @@\n         template = {}\n \n     return template\n+\n+\n+def load(filename):\n+    \"\"\"\n+    Load the given YAML file\n+    \"\"\"\n+    fp = open(filename)\n+    return loads(fp.read(), filename)\n", "issue": "method to load yaml from a string\n*cfn-lint version: cfn-lint 0.4.2*\r\n\r\n*Description of issue.*\r\n\r\nWhen consuming cfn-lint as a module there is currently only an interface to load yaml from a file(`cfnlint.core.decode.cfn_yaml.load`). In our project([taskcat](https://github.com/aws-quickstart/taskcat/)) we have a need to load templates from strings as well. \r\n\r\nWhile we could just do something like below, think it would be useful to others to have a simpler interface for yaml strings:\r\n\r\n```python\r\nfrom cfnlint.decode.cfn_yaml import MarkedLoader, multi_constructor\r\nloader = MarkedLoader(my_yaml_string, None)\r\nloader.add_multi_constructor('!', multi_constructor)\r\ntemplate = loader.get_single_data()\r\n```\r\n\n", "before_files": [{"content": "\"\"\"\n  Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.\n\n  Permission is hereby granted, free of charge, to any person obtaining a copy of this\n  software and associated documentation files (the \"Software\"), to deal in the Software\n  without restriction, including without limitation the rights to use, copy, modify,\n  merge, publish, distribute, sublicense, and/or sell copies of the Software, and to\n  permit persons to whom the Software is furnished to do so.\n\n  THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,\n  INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\n  PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT\n  HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n  OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n  SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n\"\"\"\nimport logging\nimport six\nfrom yaml.composer import Composer\nfrom yaml.reader import Reader\nfrom yaml.scanner import Scanner\nfrom yaml.resolver import Resolver\nfrom yaml import ScalarNode\nfrom yaml import SequenceNode\nfrom yaml import MappingNode\nfrom yaml.constructor import SafeConstructor\nfrom yaml.constructor import ConstructorError\nimport cfnlint\nfrom cfnlint.decode.str_node import str_node\n\ntry:\n    from yaml.cyaml import CParser as Parser  # pylint: disable=ungrouped-imports\n    cyaml = True\nexcept ImportError:\n    from yaml.parser import Parser  # pylint: disable=ungrouped-imports\n    cyaml = False\n\nUNCONVERTED_SUFFIXES = ['Ref', 'Condition']\nFN_PREFIX = 'Fn::'\n\nLOGGER = logging.getLogger(__name__)\n\n\nclass CfnParseError(ConstructorError):\n    \"\"\"\n    Error thrown when the template contains Cfn Error\n    \"\"\"\n    def __init__(self, filename, message, line_number, column_number, key=' '):\n\n        # Call the base class constructor with the parameters it needs\n        super(CfnParseError, self).__init__(message)\n\n        # Now for your custom code...\n        self.filename = filename\n        self.line_number = line_number\n        self.column_number = column_number\n        self.message = message\n        self.match = cfnlint.Match(\n            line_number + 1, column_number + 1, line_number + 1,\n            column_number + 1 + len(key), filename, cfnlint.ParseError(), message=message)\n\n\nclass NodeConstructor(SafeConstructor):\n    \"\"\"\n    Node Constructors for loading different types in Yaml\n    \"\"\"\n\n    def __init__(self, filename):\n        # Call the base class constructor\n        super(NodeConstructor, self).__init__()\n\n        self.filename = filename\n\n    # To support lazy loading, the original constructors first yield\n    # an empty object, then fill them in when iterated. Due to\n    # laziness we omit this behaviour (and will only do \"deep\n    # construction\") by first exhausting iterators, then yielding\n    # copies.\n    def construct_yaml_map(self, node):\n\n        # Check for duplicate keys on the current level, this is not desirable\n        # because a dict does not support this. It overwrites it with the last\n        # occurance, which can give unexpected results\n        mapping = {}\n        for key_node, value_node in node.value:\n            key = self.construct_object(key_node, False)\n            value = self.construct_object(value_node, False)\n\n            if key in mapping:\n                raise CfnParseError(\n                    self.filename,\n                    'Duplicate resource found \"{}\" (line {})'.format(key, key_node.start_mark.line + 1),\n                    key_node.start_mark.line, key_node.start_mark.column, key)\n            mapping[key] = value\n\n        obj, = SafeConstructor.construct_yaml_map(self, node)\n        return obj\n\n    def construct_yaml_str(self, node):\n        obj = SafeConstructor.construct_yaml_str(self, node)\n        assert isinstance(obj, (six.string_types))\n        return str_node(obj, node.start_mark, node.end_mark)\n\n    def construct_yaml_null_error(self, node):\n        \"\"\"Throw a null error\"\"\"\n        raise CfnParseError(\n            self.filename,\n            'Null value at line {0} column {1}'.format(node.start_mark.line, node.start_mark.column),\n            node.start_mark.line, node.start_mark.column, ' ')\n\n\nNodeConstructor.add_constructor(\n    u'tag:yaml.org,2002:map',\n    NodeConstructor.construct_yaml_map)\n\nNodeConstructor.add_constructor(\n    u'tag:yaml.org,2002:str',\n    NodeConstructor.construct_yaml_str)\n\nNodeConstructor.add_constructor(\n    u'tag:yaml.org,2002:null',\n    NodeConstructor.construct_yaml_null_error)\n\n\nclass MarkedLoader(Reader, Scanner, Parser, Composer, NodeConstructor, Resolver):\n    \"\"\"\n    Class for marked loading YAML\n    \"\"\"\n    # pylint: disable=non-parent-init-called,super-init-not-called\n    def __init__(self, stream, filename):\n        Reader.__init__(self, stream)\n        Scanner.__init__(self)\n        if cyaml:\n            Parser.__init__(self, stream)\n        else:\n            Parser.__init__(self)\n        Composer.__init__(self)\n        SafeConstructor.__init__(self)\n        Resolver.__init__(self)\n        NodeConstructor.__init__(self, filename)\n\n\ndef multi_constructor(loader, tag_suffix, node):\n    \"\"\"\n    Deal with !Ref style function format\n    \"\"\"\n\n    if tag_suffix not in UNCONVERTED_SUFFIXES:\n        tag_suffix = '{}{}'.format(FN_PREFIX, tag_suffix)\n\n    constructor = None\n\n    if tag_suffix == 'Fn::GetAtt':\n        constructor = construct_getatt\n    elif isinstance(node, ScalarNode):\n        constructor = loader.construct_scalar\n    elif isinstance(node, SequenceNode):\n        constructor = loader.construct_sequence\n    elif isinstance(node, MappingNode):\n        constructor = loader.construct_mapping\n    else:\n        raise 'Bad tag: !{}'.format(tag_suffix)\n\n    return {tag_suffix: constructor(node)}\n\n\ndef construct_getatt(node):\n    \"\"\"\n    Reconstruct !GetAtt into a list\n    \"\"\"\n\n    if isinstance(node.value, (six.text_type, six.string_types)):\n        return node.value.split('.')\n    if isinstance(node.value, list):\n        return [s.value for s in node.value]\n\n    raise ValueError('Unexpected node type: {}'.format(type(node.value)))\n\n\ndef load(filename):\n    \"\"\"\n    Load the give YAML file\n    \"\"\"\n    fp = open(filename)\n    loader = MarkedLoader(fp.read(), filename)\n    loader.add_multi_constructor('!', multi_constructor)\n    template = loader.get_single_data()\n    # Convert an empty file to an empty dict\n    if template is None:\n        template = {}\n\n    return template\n", "path": "src/cfnlint/decode/cfn_yaml.py"}], "after_files": [{"content": "\"\"\"\n  Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.\n\n  Permission is hereby granted, free of charge, to any person obtaining a copy of this\n  software and associated documentation files (the \"Software\"), to deal in the Software\n  without restriction, including without limitation the rights to use, copy, modify,\n  merge, publish, distribute, sublicense, and/or sell copies of the Software, and to\n  permit persons to whom the Software is furnished to do so.\n\n  THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,\n  INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\n  PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT\n  HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n  OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n  SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n\"\"\"\nimport logging\nimport six\nfrom yaml.composer import Composer\nfrom yaml.reader import Reader\nfrom yaml.scanner import Scanner\nfrom yaml.resolver import Resolver\nfrom yaml import ScalarNode\nfrom yaml import SequenceNode\nfrom yaml import MappingNode\nfrom yaml.constructor import SafeConstructor\nfrom yaml.constructor import ConstructorError\nimport cfnlint\nfrom cfnlint.decode.str_node import str_node\n\ntry:\n    from yaml.cyaml import CParser as Parser  # pylint: disable=ungrouped-imports\n    cyaml = True\nexcept ImportError:\n    from yaml.parser import Parser  # pylint: disable=ungrouped-imports\n    cyaml = False\n\nUNCONVERTED_SUFFIXES = ['Ref', 'Condition']\nFN_PREFIX = 'Fn::'\n\nLOGGER = logging.getLogger(__name__)\n\n\nclass CfnParseError(ConstructorError):\n    \"\"\"\n    Error thrown when the template contains Cfn Error\n    \"\"\"\n    def __init__(self, filename, message, line_number, column_number, key=' '):\n\n        # Call the base class constructor with the parameters it needs\n        super(CfnParseError, self).__init__(message)\n\n        # Now for your custom code...\n        self.filename = filename\n        self.line_number = line_number\n        self.column_number = column_number\n        self.message = message\n        self.match = cfnlint.Match(\n            line_number + 1, column_number + 1, line_number + 1,\n            column_number + 1 + len(key), filename, cfnlint.ParseError(), message=message)\n\n\nclass NodeConstructor(SafeConstructor):\n    \"\"\"\n    Node Constructors for loading different types in Yaml\n    \"\"\"\n\n    def __init__(self, filename):\n        # Call the base class constructor\n        super(NodeConstructor, self).__init__()\n\n        self.filename = filename\n\n    # To support lazy loading, the original constructors first yield\n    # an empty object, then fill them in when iterated. Due to\n    # laziness we omit this behaviour (and will only do \"deep\n    # construction\") by first exhausting iterators, then yielding\n    # copies.\n    def construct_yaml_map(self, node):\n\n        # Check for duplicate keys on the current level, this is not desirable\n        # because a dict does not support this. It overwrites it with the last\n        # occurance, which can give unexpected results\n        mapping = {}\n        for key_node, value_node in node.value:\n            key = self.construct_object(key_node, False)\n            value = self.construct_object(value_node, False)\n\n            if key in mapping:\n                raise CfnParseError(\n                    self.filename,\n                    'Duplicate resource found \"{}\" (line {})'.format(key, key_node.start_mark.line + 1),\n                    key_node.start_mark.line, key_node.start_mark.column, key)\n            mapping[key] = value\n\n        obj, = SafeConstructor.construct_yaml_map(self, node)\n        return obj\n\n    def construct_yaml_str(self, node):\n        obj = SafeConstructor.construct_yaml_str(self, node)\n        assert isinstance(obj, (six.string_types))\n        return str_node(obj, node.start_mark, node.end_mark)\n\n    def construct_yaml_null_error(self, node):\n        \"\"\"Throw a null error\"\"\"\n        raise CfnParseError(\n            self.filename,\n            'Null value at line {0} column {1}'.format(node.start_mark.line, node.start_mark.column),\n            node.start_mark.line, node.start_mark.column, ' ')\n\n\nNodeConstructor.add_constructor(\n    u'tag:yaml.org,2002:map',\n    NodeConstructor.construct_yaml_map)\n\nNodeConstructor.add_constructor(\n    u'tag:yaml.org,2002:str',\n    NodeConstructor.construct_yaml_str)\n\nNodeConstructor.add_constructor(\n    u'tag:yaml.org,2002:null',\n    NodeConstructor.construct_yaml_null_error)\n\n\nclass MarkedLoader(Reader, Scanner, Parser, Composer, NodeConstructor, Resolver):\n    \"\"\"\n    Class for marked loading YAML\n    \"\"\"\n    # pylint: disable=non-parent-init-called,super-init-not-called\n    def __init__(self, stream, filename):\n        Reader.__init__(self, stream)\n        Scanner.__init__(self)\n        if cyaml:\n            Parser.__init__(self, stream)\n        else:\n            Parser.__init__(self)\n        Composer.__init__(self)\n        SafeConstructor.__init__(self)\n        Resolver.__init__(self)\n        NodeConstructor.__init__(self, filename)\n\n\ndef multi_constructor(loader, tag_suffix, node):\n    \"\"\"\n    Deal with !Ref style function format\n    \"\"\"\n\n    if tag_suffix not in UNCONVERTED_SUFFIXES:\n        tag_suffix = '{}{}'.format(FN_PREFIX, tag_suffix)\n\n    constructor = None\n\n    if tag_suffix == 'Fn::GetAtt':\n        constructor = construct_getatt\n    elif isinstance(node, ScalarNode):\n        constructor = loader.construct_scalar\n    elif isinstance(node, SequenceNode):\n        constructor = loader.construct_sequence\n    elif isinstance(node, MappingNode):\n        constructor = loader.construct_mapping\n    else:\n        raise 'Bad tag: !{}'.format(tag_suffix)\n\n    return {tag_suffix: constructor(node)}\n\n\ndef construct_getatt(node):\n    \"\"\"\n    Reconstruct !GetAtt into a list\n    \"\"\"\n\n    if isinstance(node.value, (six.text_type, six.string_types)):\n        return node.value.split('.')\n    if isinstance(node.value, list):\n        return [s.value for s in node.value]\n\n    raise ValueError('Unexpected node type: {}'.format(type(node.value)))\n\n\ndef loads(yaml_string, fname=None):\n    \"\"\"\n    Load the given YAML string\n    \"\"\"\n    loader = MarkedLoader(yaml_string, fname)\n    loader.add_multi_constructor('!', multi_constructor)\n    template = loader.get_single_data()\n    # Convert an empty file to an empty dict\n    if template is None:\n        template = {}\n\n    return template\n\n\ndef load(filename):\n    \"\"\"\n    Load the given YAML file\n    \"\"\"\n    fp = open(filename)\n    return loads(fp.read(), filename)\n", "path": "src/cfnlint/decode/cfn_yaml.py"}]}