We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
construct_yaml_map leading to TypeError: unhashable type: 'dict_node'
*cfn-lint version: 0.58.1*

*Unhandled `TypeError: unhashable type: 'dict_node'` thrown from construct_yaml_map.*

Please provide as much information as possible:
Utilising some slightly edited form of the example eks-nodegroup cfn, [see here for the failing cfn](https://github.com/SaltKhan/_cfn-breaking-cfn-lint/blob/main/eks-nodegroup.yaml) or [here, for the stacktrace](https://github.com/SaltKhan/_cfn-breaking-cfn-lint/blob/main/README.md). The problem occurrs on line 259 `VPCZoneIdentifier: !Join [ ',', [ !ImportValue Fn::Sub: "${NetworkStackName}-SubnetsPrivate01", !ImportValue Fn::Sub: "${NetworkStackName}-SubnetsPrivate02" ] ]`. (Was trying to provide a list by string concat as that's what's listed as the example input when subnets is a parameter as in the example cfn, but I've changed that for the example I provide here.) I changed the line to an expanded `!Join` ~
```
      VPCZoneIdentifier: !Join
                           - ','
                           - - Fn::ImportValue: !Sub "${NetworkStackName}-SubnetsPrivate01"
                             - Fn::ImportValue: !Sub "${NetworkStackName}-SubnetsPrivate02"
```
And it now correctly yields a normal error, rather than hitting the exception.
I found this similar previous issue ~ https://github.com/aws-cloudformation/cfn-lint/issues/348

--- 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 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 SPDX-License-Identifier: MIT-0
4 """
5 import fileinput
6 import logging
7 import sys
8 import six
9 from yaml.composer import Composer
10 from yaml.reader import Reader
11 from yaml.scanner import Scanner
12 from yaml.resolver import Resolver
13 from yaml import ScalarNode
14 from yaml import SequenceNode
15 from yaml import MappingNode
16 from yaml.constructor import SafeConstructor
17 from yaml.constructor import ConstructorError
18 import cfnlint
19 from cfnlint.decode.node import str_node, dict_node, list_node, sub_node
20 
21 try:
22     from yaml.cyaml import CParser as Parser  # pylint: disable=ungrouped-imports
23     cyaml = True
24 except ImportError:
25     from yaml.parser import Parser  # pylint: disable=ungrouped-imports
26     cyaml = False
27 
28 UNCONVERTED_SUFFIXES = ['Ref', 'Condition']
29 FN_PREFIX = 'Fn::'
30 
31 LOGGER = logging.getLogger(__name__)
32 
33 
34 class CfnParseError(ConstructorError):
35     """
36     Error thrown when the template contains Cfn Error
37     """
38 
39     def __init__(self, filename, errors):
40 
41         if isinstance(errors, cfnlint.rules.Match):
42             errors = [errors]
43 
44         # Call the base class constructor with the parameters it needs
45         super(CfnParseError, self).__init__(errors[0].message)
46 
47         # Now for your custom code...
48         self.filename = filename
49         self.matches = errors
50 
51 def build_match(filename, message, line_number, column_number, key):
52     return cfnlint.rules.Match(
53         line_number + 1, column_number + 1, line_number + 1,
54         column_number + 1 + len(key), filename, cfnlint.rules.ParseError(), message=message)
55 
56 class NodeConstructor(SafeConstructor):
57     """
58     Node Constructors for loading different types in Yaml
59     """
60 
61     def __init__(self, filename):
62         # Call the base class constructor
63         super(NodeConstructor, self).__init__()
64 
65         self.filename = filename
66 
67     # To support lazy loading, the original constructors first yield
68     # an empty object, then fill them in when iterated. Due to
69     # laziness we omit this behaviour (and will only do "deep
70     # construction") by first exhausting iterators, then yielding
71     # copies.
72     def construct_yaml_map(self, node):
73 
74         # Check for duplicate keys on the current level, this is not desirable
75         # because a dict does not support this. It overwrites it with the last
76         # occurance, which can give unexpected results
77         mapping = {}
78         self.flatten_mapping(node)
79         for key_node, value_node in node.value:
80             key = self.construct_object(key_node, False)
81             value = self.construct_object(value_node, False)
82 
83             for key_dup in mapping:
84                 if key_dup == key:
85                     raise CfnParseError(
86                         self.filename,
87                         [
88                             build_match(
89                                 filename=self.filename,
90                                 message='Duplicate resource found "{}" (line {})'.format(
91                                     key, key_dup.start_mark.line + 1),
92                                 line_number=key_dup.start_mark.line,
93                                 column_number=key_dup.start_mark.column,
94                                 key=key
95                             ),
96                             build_match(
97                                 filename=self.filename,
98                                 message='Duplicate resource found "{}" (line {})'.format(
99                                     key, key_node.start_mark.line + 1),
100                                 line_number=key_node.start_mark.line,
101                                 column_number=key_node.start_mark.column,
102                                 key=key
103                             ),
104                         ]
105                     )
106             mapping[key] = value
107 
108         obj, = SafeConstructor.construct_yaml_map(self, node)
109 
110         if len(mapping) == 1:
111             if 'Fn::Sub' in mapping:
112                 return sub_node(obj, node.start_mark, node.end_mark)
113 
114         return dict_node(obj, node.start_mark, node.end_mark)
115 
116     def construct_yaml_str(self, node):
117         obj = SafeConstructor.construct_yaml_str(self, node)
118         assert isinstance(obj, (six.string_types))
119         return str_node(obj, node.start_mark, node.end_mark)
120 
121     def construct_yaml_seq(self, node):
122         obj, = SafeConstructor.construct_yaml_seq(self, node)
123         assert isinstance(obj, list)
124         return list_node(obj, node.start_mark, node.end_mark)
125 
126     def construct_yaml_null_error(self, node):
127         """Throw a null error"""
128         raise CfnParseError(
129             self.filename,
130             [
131                 build_match(
132                     filename=self.filename,
133                     message='Null value at line {0} column {1}'.format(
134                         node.start_mark.line + 1, node.start_mark.column + 1),
135                     line_number=node.start_mark.line,
136                     column_number=node.start_mark.column,
137                     key=' ',
138                 )
139             ]
140         )
141 
142 
143 NodeConstructor.add_constructor(
144     u'tag:yaml.org,2002:map',
145     NodeConstructor.construct_yaml_map)
146 
147 NodeConstructor.add_constructor(
148     u'tag:yaml.org,2002:str',
149     NodeConstructor.construct_yaml_str)
150 
151 NodeConstructor.add_constructor(
152     u'tag:yaml.org,2002:seq',
153     NodeConstructor.construct_yaml_seq)
154 
155 NodeConstructor.add_constructor(
156     u'tag:yaml.org,2002:null',
157     NodeConstructor.construct_yaml_null_error)
158 
159 
160 class MarkedLoader(Reader, Scanner, Parser, Composer, NodeConstructor, Resolver):
161     """
162     Class for marked loading YAML
163     """
164     # pylint: disable=non-parent-init-called,super-init-not-called
165 
166     def __init__(self, stream, filename):
167         Reader.__init__(self, stream)
168         Scanner.__init__(self)
169         if cyaml:
170             Parser.__init__(self, stream)
171         else:
172             Parser.__init__(self)
173         Composer.__init__(self)
174         SafeConstructor.__init__(self)
175         Resolver.__init__(self)
176         NodeConstructor.__init__(self, filename)
177 
178 
179 def multi_constructor(loader, tag_suffix, node):
180     """
181     Deal with !Ref style function format
182     """
183 
184     if tag_suffix not in UNCONVERTED_SUFFIXES:
185         tag_suffix = '{}{}'.format(FN_PREFIX, tag_suffix)
186 
187     constructor = None
188     if tag_suffix == 'Fn::GetAtt':
189         constructor = construct_getatt
190     elif isinstance(node, ScalarNode):
191         constructor = loader.construct_scalar
192     elif isinstance(node, SequenceNode):
193         constructor = loader.construct_sequence
194     elif isinstance(node, MappingNode):
195         constructor = loader.construct_mapping
196     else:
197         raise 'Bad tag: !{}'.format(tag_suffix)
198 
199     if tag_suffix == 'Fn::Sub':
200         return sub_node({tag_suffix: constructor(node)}, node.start_mark, node.end_mark)
201 
202     return dict_node({tag_suffix: constructor(node)}, node.start_mark, node.end_mark)
203 
204 
205 def construct_getatt(node):
206     """
207     Reconstruct !GetAtt into a list
208     """
209 
210     if isinstance(node.value, (six.string_types)):
211         return list_node(node.value.split('.', 1), node.start_mark, node.end_mark)
212     if isinstance(node.value, list):
213         return list_node([s.value for s in node.value], node.start_mark, node.end_mark)
214 
215     raise ValueError('Unexpected node type: {}'.format(type(node.value)))
216 
217 
218 def loads(yaml_string, fname=None):
219     """
220     Load the given YAML string
221     """
222     loader = MarkedLoader(yaml_string, fname)
223     loader.add_multi_constructor('!', multi_constructor)
224     template = loader.get_single_data()
225     # Convert an empty file to an empty dict
226     if template is None:
227         template = {}
228 
229     return template
230 
231 
232 def load(filename):
233     """
234     Load the given YAML file
235     """
236 
237     content = ''
238 
239     if not sys.stdin.isatty():
240         for line in fileinput.input(files=filename):
241             content = content + line
242     else:
243         with open(filename) as fp:
244             content = fp.read()
245 
246     return loads(content, filename)
247
```
--- 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
@@ -103,7 +103,21 @@
                             ),
                         ]
                     )
-            mapping[key] = value
+            try:
+                mapping[key] = value
+            except:
+                raise CfnParseError(
+                        self.filename,
+                        [
+                            build_match(
+                                filename=self.filename,
+                                message=f'Unhashable type "{key}" (line {key.start_mark.line + 1})',
+                                line_number=key.start_mark.line,
+                                column_number=key.start_mark.column,
+                                key=key
+                            ),
+                        ]
+                    )
 
         obj, = SafeConstructor.construct_yaml_map(self, node)

{"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@@ -103,7 +103,21 @@\n                             ),\n                         ]\n                     )\n-            mapping[key] = value\n+            try:\n+                mapping[key] = value\n+            except:\n+                raise CfnParseError(\n+                        self.filename,\n+                        [\n+                            build_match(\n+                                filename=self.filename,\n+                                message=f'Unhashable type \"{key}\" (line {key.start_mark.line + 1})',\n+                                line_number=key.start_mark.line,\n+                                column_number=key.start_mark.column,\n+                                key=key\n+                            ),\n+                        ]\n+                    )\n \n         obj, = SafeConstructor.construct_yaml_map(self, node)\n", "issue": "construct_yaml_map leading to TypeError: unhashable type: 'dict_node'\n*cfn-lint version: 0.58.1*\r\n\r\n*Unhandled `TypeError: unhashable type: 'dict_node'` thrown from construct_yaml_map.*\r\n\r\nPlease provide as much information as possible:\r\nUtilising some slightly edited form of the example eks-nodegroup cfn, [see here for the failing cfn](https://github.com/SaltKhan/_cfn-breaking-cfn-lint/blob/main/eks-nodegroup.yaml) or [here, for the stacktrace](https://github.com/SaltKhan/_cfn-breaking-cfn-lint/blob/main/README.md). The problem occurrs on line 259 `VPCZoneIdentifier: !Join [ ',', [ !ImportValue Fn::Sub: \"${NetworkStackName}-SubnetsPrivate01\", !ImportValue Fn::Sub: \"${NetworkStackName}-SubnetsPrivate02\" ] ]`. (Was trying to provide a list by string concat as that's what's listed as the example input when subnets is a parameter as in the example cfn, but I've changed that for the example I provide here.) I changed the line to an expanded `!Join` ~\r\n```\r\n      VPCZoneIdentifier: !Join\r\n                           - ','\r\n                           - - Fn::ImportValue: !Sub \"${NetworkStackName}-SubnetsPrivate01\"\r\n                             - Fn::ImportValue: !Sub \"${NetworkStackName}-SubnetsPrivate02\"\r\n```\r\nAnd it now correctly yields a normal error, rather than hitting the exception.\r\nI found this similar previous issue ~ https://github.com/aws-cloudformation/cfn-lint/issues/348\n", "before_files": [{"content": "\"\"\"\nCopyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\nSPDX-License-Identifier: MIT-0\n\"\"\"\nimport fileinput\nimport logging\nimport sys\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, sub_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\n    def __init__(self, filename, errors):\n\n        if isinstance(errors, cfnlint.rules.Match):\n            errors = [errors]\n\n        # Call the base class constructor with the parameters it needs\n        super(CfnParseError, self).__init__(errors[0].message)\n\n        # Now for your custom code...\n        self.filename = filename\n        self.matches = errors\n\ndef build_match(filename, message, line_number, column_number, key):\n    return cfnlint.rules.Match(\n        line_number + 1, column_number + 1, line_number + 1,\n        column_number + 1 + len(key), filename, cfnlint.rules.ParseError(), message=message)\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        self.flatten_mapping(node)\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            for key_dup in mapping:\n                if key_dup == key:\n                    raise CfnParseError(\n                        self.filename,\n                        [\n                            build_match(\n                                filename=self.filename,\n                                message='Duplicate resource found \"{}\" (line {})'.format(\n                                    key, key_dup.start_mark.line + 1),\n                                line_number=key_dup.start_mark.line,\n                                column_number=key_dup.start_mark.column,\n                                key=key\n                            ),\n                            build_match(\n                                filename=self.filename,\n                                message='Duplicate resource found \"{}\" (line {})'.format(\n                                    key, key_node.start_mark.line + 1),\n                                line_number=key_node.start_mark.line,\n                                column_number=key_node.start_mark.column,\n                                key=key\n                            ),\n                        ]\n                    )\n            mapping[key] = value\n\n        obj, = SafeConstructor.construct_yaml_map(self, node)\n\n        if len(mapping) == 1:\n            if 'Fn::Sub' in mapping:\n                return sub_node(obj, node.start_mark, node.end_mark)\n\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            [\n                build_match(\n                    filename=self.filename,\n                    message='Null value at line {0} column {1}'.format(\n                        node.start_mark.line + 1, node.start_mark.column + 1),\n                    line_number=node.start_mark.line,\n                    column_number=node.start_mark.column,\n                    key=' ',\n                )\n            ]\n        )\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\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    if tag_suffix == 'Fn::Sub':\n        return sub_node({tag_suffix: constructor(node)}, node.start_mark, node.end_mark)\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('.', 1), 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\n    content = ''\n\n    if not sys.stdin.isatty():\n        for line in fileinput.input(files=filename):\n            content = content + line\n    else:\n        with open(filename) as fp:\n            content = fp.read()\n\n    return loads(content, filename)\n", "path": "src/cfnlint/decode/cfn_yaml.py"}], "after_files": [{"content": "\"\"\"\nCopyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\nSPDX-License-Identifier: MIT-0\n\"\"\"\nimport fileinput\nimport logging\nimport sys\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, sub_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\n    def __init__(self, filename, errors):\n\n        if isinstance(errors, cfnlint.rules.Match):\n            errors = [errors]\n\n        # Call the base class constructor with the parameters it needs\n        super(CfnParseError, self).__init__(errors[0].message)\n\n        # Now for your custom code...\n        self.filename = filename\n        self.matches = errors\n\ndef build_match(filename, message, line_number, column_number, key):\n    return cfnlint.rules.Match(\n        line_number + 1, column_number + 1, line_number + 1,\n        column_number + 1 + len(key), filename, cfnlint.rules.ParseError(), message=message)\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        self.flatten_mapping(node)\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            for key_dup in mapping:\n                if key_dup == key:\n                    raise CfnParseError(\n                        self.filename,\n                        [\n                            build_match(\n                                filename=self.filename,\n                                message='Duplicate resource found \"{}\" (line {})'.format(\n                                    key, key_dup.start_mark.line + 1),\n                                line_number=key_dup.start_mark.line,\n                                column_number=key_dup.start_mark.column,\n                                key=key\n                            ),\n                            build_match(\n                                filename=self.filename,\n                                message='Duplicate resource found \"{}\" (line {})'.format(\n                                    key, key_node.start_mark.line + 1),\n                                line_number=key_node.start_mark.line,\n                                column_number=key_node.start_mark.column,\n                                key=key\n                            ),\n                        ]\n                    )\n            try:\n                mapping[key] = value\n            except:\n                raise CfnParseError(\n                        self.filename,\n                        [\n                            build_match(\n                                filename=self.filename,\n                                message=f'Unhashable type \"{key}\" (line {key.start_mark.line + 1})',\n                                line_number=key.start_mark.line,\n                                column_number=key.start_mark.column,\n                                key=key\n                            ),\n                        ]\n                    )\n\n        obj, = SafeConstructor.construct_yaml_map(self, node)\n\n        if len(mapping) == 1:\n            if 'Fn::Sub' in mapping:\n                return sub_node(obj, node.start_mark, node.end_mark)\n\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            [\n                build_match(\n                    filename=self.filename,\n                    message='Null value at line {0} column {1}'.format(\n                        node.start_mark.line + 1, node.start_mark.column + 1),\n                    line_number=node.start_mark.line,\n                    column_number=node.start_mark.column,\n                    key=' ',\n                )\n            ]\n        )\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\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    if tag_suffix == 'Fn::Sub':\n        return sub_node({tag_suffix: constructor(node)}, node.start_mark, node.end_mark)\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('.', 1), 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\n    content = ''\n\n    if not sys.stdin.isatty():\n        for line in fileinput.input(files=filename):\n            content = content + line\n    else:\n        with open(filename) as fp:\n            content = fp.read()\n\n    return loads(content, filename)\n", "path": "src/cfnlint/decode/cfn_yaml.py"}]}