We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
E1010 Invalid GetAtt error message could be better
*cfn-lint version: (`cfn-lint --version`)* 0.7.1

Code:
```yaml
  ElasticSearchHostname:
    Value:
      Fn::GetAtt: ElasticSearchDomain.DomainEndpoint
```

(I mixed short and long form)

Current:
```
E1010 Invalid GetAtt E.l.a.s.t.i.c.S.e.a.r.c.h.D.o.m.a.i.n...D.o.m.a.i.n.E.n.d.p.o.i.n.t for resource ElasticSearchHostname
cfn/x.cfn.yaml:342:7
```

Better:
```
E1010 GetAtt expects an array of length 2, not String for resource ElasticSearchHostname
```

(nb, this is also an error in an *output*, not *resource*, but I didn't even notice that until filing the bug report. I guess my eyes skip over everything except the line number)

--- 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/rules/functions/GetAtt.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 from cfnlint import CloudFormationLintRule
18 from cfnlint import RuleMatch
19 import cfnlint.helpers
20 
21 
22 class GetAtt(CloudFormationLintRule):
23     """Check if GetAtt values are correct"""
24     id = 'E1010'
25     shortdesc = 'GetAtt validation of parameters'
26     description = 'Validates that GetAtt parameters are to valid resources and properties of those resources'
27     source_url = 'https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-getatt.html'
28     tags = ['functions', 'getatt']
29 
30     def __init__(self):
31         resourcespecs = cfnlint.helpers.RESOURCE_SPECS['us-east-1']
32         self.resourcetypes = resourcespecs['ResourceTypes']
33         self.propertytypes = resourcespecs['PropertyTypes']
34 
35     def match(self, cfn):
36         """Check CloudFormation GetAtt"""
37 
38         matches = []
39 
40         getatts = cfn.search_deep_keys('Fn::GetAtt')
41         valid_getatts = cfn.get_valid_getatts()
42         for getatt in getatts:
43             if len(getatt[-1]) < 2:
44                 message = 'Invalid GetAtt for {0}'
45                 matches.append(RuleMatch(getatt, message.format('/'.join(map(str, getatt[:-1])))))
46                 continue
47             resname = getatt[-1][0]
48             restype = '.'.join(getatt[-1][1:])
49             if resname in valid_getatts:
50                 if restype not in valid_getatts[resname] and '*' not in valid_getatts[resname]:
51                     message = 'Invalid GetAtt {0}.{1} for resource {2}'
52                     matches.append(RuleMatch(
53                         getatt[:-1], message.format(resname, restype, getatt[1])))
54             else:
55                 message = 'Invalid GetAtt {0}.{1} for resource {2}'
56                 matches.append(RuleMatch(getatt, message.format(resname, restype, getatt[1])))
57 
58         return matches
59
```

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.node import str_node, dict_node, list_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 dict_node(obj, node.start_mark, node.end_mark)
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_seq(self, node):
105         obj, = SafeConstructor.construct_yaml_seq(self, node)
106         assert isinstance(obj, list)
107         return list_node(obj, node.start_mark, node.end_mark)
108 
109     def construct_yaml_null_error(self, node):
110         """Throw a null error"""
111         raise CfnParseError(
112             self.filename,
113             'Null value at line {0} column {1}'.format(node.start_mark.line, node.start_mark.column),
114             node.start_mark.line, node.start_mark.column, ' ')
115 
116 
117 NodeConstructor.add_constructor(
118     u'tag:yaml.org,2002:map',
119     NodeConstructor.construct_yaml_map)
120 
121 NodeConstructor.add_constructor(
122     u'tag:yaml.org,2002:str',
123     NodeConstructor.construct_yaml_str)
124 
125 NodeConstructor.add_constructor(
126     u'tag:yaml.org,2002:seq',
127     NodeConstructor.construct_yaml_seq)
128 
129 NodeConstructor.add_constructor(
130     u'tag:yaml.org,2002:null',
131     NodeConstructor.construct_yaml_null_error)
132 
133 
134 class MarkedLoader(Reader, Scanner, Parser, Composer, NodeConstructor, Resolver):
135     """
136     Class for marked loading YAML
137     """
138     # pylint: disable=non-parent-init-called,super-init-not-called
139     def __init__(self, stream, filename):
140         Reader.__init__(self, stream)
141         Scanner.__init__(self)
142         if cyaml:
143             Parser.__init__(self, stream)
144         else:
145             Parser.__init__(self)
146         Composer.__init__(self)
147         SafeConstructor.__init__(self)
148         Resolver.__init__(self)
149         NodeConstructor.__init__(self, filename)
150 
151 
152 def multi_constructor(loader, tag_suffix, node):
153     """
154     Deal with !Ref style function format
155     """
156 
157     if tag_suffix not in UNCONVERTED_SUFFIXES:
158         tag_suffix = '{}{}'.format(FN_PREFIX, tag_suffix)
159 
160     constructor = None
161     if tag_suffix == 'Fn::GetAtt':
162         constructor = construct_getatt
163     elif isinstance(node, ScalarNode):
164         constructor = loader.construct_scalar
165     elif isinstance(node, SequenceNode):
166         constructor = loader.construct_sequence
167     elif isinstance(node, MappingNode):
168         constructor = loader.construct_mapping
169     else:
170         raise 'Bad tag: !{}'.format(tag_suffix)
171 
172     return dict_node({tag_suffix: constructor(node)}, node.start_mark, node.end_mark)
173 
174 
175 def construct_getatt(node):
176     """
177     Reconstruct !GetAtt into a list
178     """
179     if isinstance(node.value, (six.string_types)):
180         return list_node(node.value.split('.'), node.start_mark, node.end_mark)
181     if isinstance(node.value, list):
182         return list_node([s.value for s in node.value], node.start_mark, node.end_mark)
183 
184     raise ValueError('Unexpected node type: {}'.format(type(node.value)))
185 
186 
187 def loads(yaml_string, fname=None):
188     """
189     Load the given YAML string
190     """
191     loader = MarkedLoader(yaml_string, fname)
192     loader.add_multi_constructor('!', multi_constructor)
193     template = loader.get_single_data()
194     # Convert an empty file to an empty dict
195     if template is None:
196         template = {}
197 
198     return template
199 
200 
201 def load(filename):
202     """
203     Load the given YAML file
204     """
205     with open(filename) as fp:
206         return loads(fp.read(), filename)
207
```
--- 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
@@ -176,6 +176,7 @@
     """
     Reconstruct !GetAtt into a list
     """
+
     if isinstance(node.value, (six.string_types)):
         return list_node(node.value.split('.'), node.start_mark, node.end_mark)
     if isinstance(node.value, list):
diff --git a/src/cfnlint/rules/functions/GetAtt.py b/src/cfnlint/rules/functions/GetAtt.py
--- a/src/cfnlint/rules/functions/GetAtt.py
+++ b/src/cfnlint/rules/functions/GetAtt.py
@@ -14,6 +14,7 @@
   OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
   SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 """
+import six
 from cfnlint import CloudFormationLintRule
 from cfnlint import RuleMatch
 import cfnlint.helpers
@@ -44,8 +45,12 @@
                 message = 'Invalid GetAtt for {0}'
                 matches.append(RuleMatch(getatt, message.format('/'.join(map(str, getatt[:-1])))))
                 continue
-            resname = getatt[-1][0]
-            restype = '.'.join(getatt[-1][1:])
+            if isinstance(getatt[-1], six.string_types):
+                resname, restype = getatt[-1].split('.')
+            else:
+                resname = getatt[-1][0]
+                restype = '.'.join(getatt[-1][1:])
+
             if resname in valid_getatts:
                 if restype not in valid_getatts[resname] and '*' not in valid_getatts[resname]:
                     message = 'Invalid GetAtt {0}.{1} for resource {2}'

{"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@@ -176,6 +176,7 @@\n     \"\"\"\n     Reconstruct !GetAtt into a list\n     \"\"\"\n+\n     if isinstance(node.value, (six.string_types)):\n         return list_node(node.value.split('.'), node.start_mark, node.end_mark)\n     if isinstance(node.value, list):\ndiff --git a/src/cfnlint/rules/functions/GetAtt.py b/src/cfnlint/rules/functions/GetAtt.py\n--- a/src/cfnlint/rules/functions/GetAtt.py\n+++ b/src/cfnlint/rules/functions/GetAtt.py\n@@ -14,6 +14,7 @@\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 \"\"\"\n+import six\n from cfnlint import CloudFormationLintRule\n from cfnlint import RuleMatch\n import cfnlint.helpers\n@@ -44,8 +45,12 @@\n                 message = 'Invalid GetAtt for {0}'\n                 matches.append(RuleMatch(getatt, message.format('/'.join(map(str, getatt[:-1])))))\n                 continue\n-            resname = getatt[-1][0]\n-            restype = '.'.join(getatt[-1][1:])\n+            if isinstance(getatt[-1], six.string_types):\n+                resname, restype = getatt[-1].split('.')\n+            else:\n+                resname = getatt[-1][0]\n+                restype = '.'.join(getatt[-1][1:])\n+\n             if resname in valid_getatts:\n                 if restype not in valid_getatts[resname] and '*' not in valid_getatts[resname]:\n                     message = 'Invalid GetAtt {0}.{1} for resource {2}'\n", "issue": "E1010 Invalid GetAtt error message could be better\n*cfn-lint version: (`cfn-lint --version`)* 0.7.1\r\n\r\nCode:\r\n```yaml\r\n  ElasticSearchHostname:\r\n    Value:\r\n      Fn::GetAtt: ElasticSearchDomain.DomainEndpoint\r\n```\r\n\r\n(I mixed short and long form)\r\n\r\nCurrent:\r\n```\r\nE1010 Invalid GetAtt E.l.a.s.t.i.c.S.e.a.r.c.h.D.o.m.a.i.n...D.o.m.a.i.n.E.n.d.p.o.i.n.t for resource ElasticSearchHostname\r\ncfn/x.cfn.yaml:342:7\r\n```\r\n\r\nBetter:\r\n```\r\nE1010 GetAtt expects an array of length 2, not String for resource ElasticSearchHostname\r\n```\r\n\r\n(nb, this is also an error in an *output*, not *resource*, but I didn't even notice that until filing the bug report. I guess my eyes skip over everything except the line number)\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\"\"\"\nfrom cfnlint import CloudFormationLintRule\nfrom cfnlint import RuleMatch\nimport cfnlint.helpers\n\n\nclass GetAtt(CloudFormationLintRule):\n    \"\"\"Check if GetAtt values are correct\"\"\"\n    id = 'E1010'\n    shortdesc = 'GetAtt validation of parameters'\n    description = 'Validates that GetAtt parameters are to valid resources and properties of those resources'\n    source_url = 'https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-getatt.html'\n    tags = ['functions', 'getatt']\n\n    def __init__(self):\n        resourcespecs = cfnlint.helpers.RESOURCE_SPECS['us-east-1']\n        self.resourcetypes = resourcespecs['ResourceTypes']\n        self.propertytypes = resourcespecs['PropertyTypes']\n\n    def match(self, cfn):\n        \"\"\"Check CloudFormation GetAtt\"\"\"\n\n        matches = []\n\n        getatts = cfn.search_deep_keys('Fn::GetAtt')\n        valid_getatts = cfn.get_valid_getatts()\n        for getatt in getatts:\n            if len(getatt[-1]) < 2:\n                message = 'Invalid GetAtt for {0}'\n                matches.append(RuleMatch(getatt, message.format('/'.join(map(str, getatt[:-1])))))\n                continue\n            resname = getatt[-1][0]\n            restype = '.'.join(getatt[-1][1:])\n            if resname in valid_getatts:\n                if restype not in valid_getatts[resname] and '*' not in valid_getatts[resname]:\n                    message = 'Invalid GetAtt {0}.{1} for resource {2}'\n                    matches.append(RuleMatch(\n                        getatt[:-1], message.format(resname, restype, getatt[1])))\n            else:\n                message = 'Invalid GetAtt {0}.{1} for resource {2}'\n                matches.append(RuleMatch(getatt, message.format(resname, restype, getatt[1])))\n\n        return matches\n", "path": "src/cfnlint/rules/functions/GetAtt.py"}, {"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.node import str_node, dict_node, list_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 dict_node(obj, node.start_mark, node.end_mark)\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_seq(self, node):\n        obj, = SafeConstructor.construct_yaml_seq(self, node)\n        assert isinstance(obj, list)\n        return list_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:seq',\n    NodeConstructor.construct_yaml_seq)\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    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 dict_node({tag_suffix: constructor(node)}, node.start_mark, node.end_mark)\n\n\ndef construct_getatt(node):\n    \"\"\"\n    Reconstruct !GetAtt into a list\n    \"\"\"\n    if isinstance(node.value, (six.string_types)):\n        return list_node(node.value.split('.'), node.start_mark, node.end_mark)\n    if isinstance(node.value, list):\n        return list_node([s.value for s in node.value], node.start_mark, node.end_mark)\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    with open(filename) as fp:\n        return loads(fp.read(), filename)\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 six\nfrom cfnlint import CloudFormationLintRule\nfrom cfnlint import RuleMatch\nimport cfnlint.helpers\n\n\nclass GetAtt(CloudFormationLintRule):\n    \"\"\"Check if GetAtt values are correct\"\"\"\n    id = 'E1010'\n    shortdesc = 'GetAtt validation of parameters'\n    description = 'Validates that GetAtt parameters are to valid resources and properties of those resources'\n    source_url = 'https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-getatt.html'\n    tags = ['functions', 'getatt']\n\n    def __init__(self):\n        resourcespecs = cfnlint.helpers.RESOURCE_SPECS['us-east-1']\n        self.resourcetypes = resourcespecs['ResourceTypes']\n        self.propertytypes = resourcespecs['PropertyTypes']\n\n    def match(self, cfn):\n        \"\"\"Check CloudFormation GetAtt\"\"\"\n\n        matches = []\n\n        getatts = cfn.search_deep_keys('Fn::GetAtt')\n        valid_getatts = cfn.get_valid_getatts()\n        for getatt in getatts:\n            if len(getatt[-1]) < 2:\n                message = 'Invalid GetAtt for {0}'\n                matches.append(RuleMatch(getatt, message.format('/'.join(map(str, getatt[:-1])))))\n                continue\n            if isinstance(getatt[-1], six.string_types):\n                resname, restype = getatt[-1].split('.')\n            else:\n                resname = getatt[-1][0]\n                restype = '.'.join(getatt[-1][1:])\n\n            if resname in valid_getatts:\n                if restype not in valid_getatts[resname] and '*' not in valid_getatts[resname]:\n                    message = 'Invalid GetAtt {0}.{1} for resource {2}'\n                    matches.append(RuleMatch(\n                        getatt[:-1], message.format(resname, restype, getatt[1])))\n            else:\n                message = 'Invalid GetAtt {0}.{1} for resource {2}'\n                matches.append(RuleMatch(getatt, message.format(resname, restype, getatt[1])))\n\n        return matches\n", "path": "src/cfnlint/rules/functions/GetAtt.py"}, {"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.node import str_node, dict_node, list_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 dict_node(obj, node.start_mark, node.end_mark)\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_seq(self, node):\n        obj, = SafeConstructor.construct_yaml_seq(self, node)\n        assert isinstance(obj, list)\n        return list_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:seq',\n    NodeConstructor.construct_yaml_seq)\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    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 dict_node({tag_suffix: constructor(node)}, node.start_mark, node.end_mark)\n\n\ndef construct_getatt(node):\n    \"\"\"\n    Reconstruct !GetAtt into a list\n    \"\"\"\n\n    if isinstance(node.value, (six.string_types)):\n        return list_node(node.value.split('.'), node.start_mark, node.end_mark)\n    if isinstance(node.value, list):\n        return list_node([s.value for s in node.value], node.start_mark, node.end_mark)\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    with open(filename) as fp:\n        return loads(fp.read(), filename)\n", "path": "src/cfnlint/decode/cfn_yaml.py"}]}