We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Error transforming template when using !GetAtt
### CloudFormation Lint Version

0.85.0

### What operating system are you using?

Ubuntu

### Describe the bug

When I try to validate the template i get following error:

> E0001 Error transforming template: __init__() missing 2 required positional arguments: 'start_mark' and 'end_mark'
aws/template.yml:1:1

When AuthorizeUri is hardcoded the problem disappears.


### Expected behavior

No error is thrown and template is validated.

### Reproduction template

```AWSTemplateFormatVersion: "2010-09-09"
Transform: AWS::Serverless-2016-10-31

Resources:
  Api:
    Type: "AWS::Serverless::GraphQLApi"
    Properties:
      Name: SomeApi
      SchemaUri: ./schema.graphql
      Auth:
        Type: AWS_LAMBDA
        LambdaAuthorizer:
          AuthorizerUri: !GetAtt AppSyncAuthorizerFunction.Arn

  AppSyncAuthorizerFunction:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: ./lambda/
      Handler: lambda_authorizer.handle
      Runtime: python3.8


--- 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/node.py`
Content:
```
1 """
2 Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 SPDX-License-Identifier: MIT-0
4 """
5 
6 import logging
7 from copy import deepcopy
8 
9 import regex as re
10 
11 LOGGER = logging.getLogger(__name__)
12 
13 
14 class TemplateAttributeError(AttributeError):
15     """Custom error to capture Attribute Errors in the Template"""
16 
17 
18 def create_str_node_class(cls):
19     """
20     Create string node class
21     """
22 
23     class node_class(cls):
24         """Node class created based on the input class"""
25 
26         def __init__(self, x, start_mark, end_mark):
27             try:
28                 cls.__init__(self, x)
29             except TypeError:
30                 cls.__init__(self)
31             self.start_mark = start_mark
32             self.end_mark = end_mark
33 
34         # pylint: disable=bad-classmethod-argument, unused-argument
35         def __new__(self, x, start_mark, end_mark):
36             return cls.__new__(self, x)
37 
38         def __getattr__(self, name):
39             raise TemplateAttributeError(f"{self.__class__.__name__}.{name} is invalid")
40 
41         def __deepcopy__(self, memo):
42             result = str_node(self, self.start_mark, self.end_mark)
43             memo[id(self)] = result
44             return result
45 
46         def __copy__(self):
47             return self
48 
49     node_class.__name__ = f"{cls.__name__}_node"
50     return node_class
51 
52 
53 def create_dict_node_class(cls):
54     """
55     Create dynamic node class
56     """
57 
58     class node_class(cls):
59         """Node class created based on the input class"""
60 
61         def __init__(self, x, start_mark, end_mark):
62             try:
63                 cls.__init__(self, x)
64             except TypeError:
65                 cls.__init__(self)
66             self.start_mark = start_mark
67             self.end_mark = end_mark
68             self.condition_functions = ["Fn::If"]
69 
70         def __deepcopy__(self, memo):
71             result = dict_node(self, self.start_mark, self.end_mark)
72             memo[id(self)] = result
73             for k, v in self.items():
74                 result[deepcopy(k)] = deepcopy(v, memo)
75 
76             return result
77 
78         def __copy__(self):
79             return self
80 
81         def is_function_returning_object(self, mappings=None):
82             """
83             Check if an object is using a function that could return an object
84             Return True when
85                 Fn::Select:
86                 - 0  # or any number
87                 - !FindInMap [mapname, key, value] # or any mapname, key, value
88             Otherwise False
89             """
90             mappings = mappings or {}
91             if len(self) == 1:
92                 for k, v in self.items():
93                     if k in ["Fn::Select"]:
94                         if isinstance(v, list):
95                             if len(v) == 2:
96                                 p_v = v[1]
97                                 if isinstance(p_v, dict):
98                                     if len(p_v) == 1:
99                                         for l_k in p_v.keys():
100                                             if l_k in ["Fn::FindInMap", "Fn::GetAtt"]:
101                                                 return True
102 
103             return False
104 
105         def get(self, key, default=None):
106             """Override the default get"""
107             if isinstance(default, dict):
108                 default = dict_node(default, self.start_mark, self.end_mark)
109             return super().get(key, default)
110 
111         def get_safe(self, key, default=None, path=None, type_t=()):
112             """
113             Get values in format
114             """
115             path = path or []
116 
117             if default == {}:
118                 default = dict_node({}, self.start_mark, self.end_mark)
119             value = self.get(key, default)
120             if value is None and default is None:
121                 # if default is None and value is None return empty list
122                 return []
123 
124             # if the value is the default make sure that the default value is of type_t when specified
125             if bool(type_t) and value == default and not isinstance(default, type_t):
126                 raise ValueError('"default" type should be of "type_t"')
127 
128             # when not a dict see if if the value is of the right type
129             results = []
130             if not isinstance(value, (dict)):
131                 if isinstance(value, type_t) or not type_t:
132                     return [(value, (path[:] + [key]))]
133             else:
134                 for sub_v, sub_path in value.items_safe(path + [key]):
135                     if isinstance(sub_v, type_t) or not type_t:
136                         results.append((sub_v, sub_path))
137 
138             return results
139 
140         def clean(self):
141             """Clean object to remove any Ref AWS::NoValue"""
142             result = dict_node({}, self.start_mark, self.end_mark)
143             for k, v in self.items():
144                 if isinstance(v, dict) and len(v) == 1:
145                     if v.get("Ref") == "AWS::NoValue":
146                         continue
147                 result[k] = v
148             return result
149 
150         def items_safe(self, path=None, type_t=()):
151             """Get items while handling IFs"""
152             path = path or []
153             if len(self) == 1:
154                 for k, v in self.items():
155                     if k == "Fn::If":
156                         if isinstance(v, list):
157                             if len(v) == 3:
158                                 for i, if_v in enumerate(v[1:]):
159                                     if isinstance(if_v, dict):
160                                         # yield from if_v.items_safe(path[:] + [k, i - 1])
161                                         # Python 2.7 support
162                                         for items, p in if_v.items_safe(
163                                             path[:] + [k, i + 1]
164                                         ):
165                                             if isinstance(items, type_t) or not type_t:
166                                                 yield items, p
167                                     elif isinstance(if_v, list):
168                                         if isinstance(if_v, type_t) or not type_t:
169                                             yield if_v, path[:] + [k, i + 1]
170                                     else:
171                                         if isinstance(if_v, type_t) or not type_t:
172                                             yield if_v, path[:] + [k, i + 1]
173                     elif not (k == "Ref" and v == "AWS::NoValue"):
174                         if isinstance(self, type_t) or not type_t:
175                             yield self.clean(), path[:]
176             else:
177                 if isinstance(self, type_t) or not type_t:
178                     yield self.clean(), path[:]
179 
180         def __getattr__(self, name):
181             raise TemplateAttributeError(f"{self.__class__.__name__}.{name} is invalid")
182 
183     node_class.__name__ = f"{cls.__name__}_node"
184     return node_class
185 
186 
187 def create_intrinsic_node_class(cls):
188     """
189     Create dynamic sub class
190     """
191 
192     class intrinsic_class(cls):
193         """Node class created based on the input class"""
194 
195         def is_valid(self):
196             raise TemplateAttributeError("intrisnic class shouldn't be directly used")
197 
198     intrinsic_class.__name__ = f"{cls.__name__}_intrinsic"
199     return intrinsic_class
200 
201 
202 def create_sub_node_class(cls):
203     """
204     Create dynamic sub class
205     """
206 
207     class sub_class(cls):
208         """Node class created based on the input class"""
209 
210         def __init__(self, x, start_mark, end_mark):
211             cls.__init__(self, x, start_mark, end_mark)
212             self.__cache_is_valid = False
213             self.__cache_sub_string = ""
214             self.__cache_sub_string_vars = set()
215             self.__cache_sub_vars = {}
216             self.__setup()
217 
218         def __setup_list_sub_string(self, s):
219             self.__cache_sub_string = s
220             regex = re.compile(r"\${[^!].*?}")
221             string_params = regex.findall(s)
222 
223             for string_param in string_params:
224                 self.__cache_sub_string_vars.add(string_param[2:-1].strip())
225 
226         def __setup_list(self, v):
227             if len(v) == 2:
228                 if not isinstance(v[0], str):
229                     return
230                 self.__setup_list_sub_string(v[0])
231                 if not isinstance(v[1], dict):
232                     return
233                 self.__cache_sub_vars = v[1]
234                 self.__cache_is_valid = True
235 
236         def __setup(self):
237             if len(self) == 1:
238                 for k, v in self.items():
239                     if k == "Fn::Sub":
240                         if isinstance(v, str):
241                             self.__setup_list_sub_string(v)
242                             self.__cache_is_valid = True
243                         elif isinstance(v, list):
244                             self.__setup_list(v)
245 
246         def get_defined_vars(self):
247             # Returns that are in the second part of a list Fn::Sub
248             # This function will not return implied variables from a String Ref and GetAtt
249             if self.is_valid():
250                 return self.__cache_sub_vars
251 
252             return {}
253 
254         def get_string_vars(self):
255             # Returns all variables in the Sub String
256             if self.is_valid():
257                 return self.__cache_sub_string_vars
258 
259             return set()
260 
261         def get_string(self):
262             # Returns the sub string as it was when it was decoded
263             if self.is_valid():
264                 return self.__cache_sub_string
265 
266             return ""
267 
268         def is_valid(self):
269             return self.__cache_is_valid
270 
271     sub_class.__name__ = f"{cls.__name__}_sub"
272     return sub_class
273 
274 
275 def create_dict_list_class(cls):
276     """
277     Create dynamic list class
278     """
279 
280     class node_class(cls):
281         """Node class created based on the input class"""
282 
283         def __init__(self, x, start_mark, end_mark):
284             try:
285                 cls.__init__(self, x)
286             except TypeError:
287                 cls.__init__(self)
288             self.start_mark = start_mark
289             self.end_mark = end_mark
290             self.condition_functions = ["Fn::If"]
291 
292         def __deepcopy__(self, memo):
293             result = list_node([], self.start_mark, self.end_mark)
294             memo[id(self)] = result
295             for _, v in enumerate(self):
296                 result.append(deepcopy(v, memo))
297 
298             return result
299 
300         def __copy__(self):
301             return self
302 
303         def items_safe(self, path=None, type_t=()):
304             """Get items while handling IFs"""
305             path = path or []
306             for i, v in enumerate(self):
307                 if isinstance(v, dict):
308                     for items, p in v.items_safe(path[:] + [i]):
309                         if isinstance(items, type_t) or not type_t:
310                             yield items, p
311                 else:
312                     if isinstance(v, type_t) or not type_t:
313                         yield v, path[:] + [i]
314 
315         def __getattr__(self, name):
316             raise TemplateAttributeError(f"{self.__class__.__name__}.{name} is invalid")
317 
318     node_class.__name__ = f"{cls.__name__}_node"
319     return node_class
320 
321 
322 str_node = create_str_node_class(str)
323 dict_node = create_dict_node_class(dict)
324 list_node = create_dict_list_class(list)
325 intrinsic_node = create_intrinsic_node_class(dict_node)
326 sub_node = create_sub_node_class(intrinsic_node)
327
```
--- 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/node.py b/src/cfnlint/decode/node.py
--- a/src/cfnlint/decode/node.py
+++ b/src/cfnlint/decode/node.py
@@ -3,8 +3,12 @@
 SPDX-License-Identifier: MIT-0
 """
 
+from __future__ import annotations
+
 import logging
+from collections import namedtuple
 from copy import deepcopy
+from typing import Any, Dict, Protocol, Set
 
 import regex as re
 
@@ -15,6 +19,14 @@
     """Custom error to capture Attribute Errors in the Template"""
 
 
+class Mark(Protocol):
+    line: int
+    column: int
+
+
+_mark = namedtuple("_mark", ["line", "column"])
+
+
 def create_str_node_class(cls):
     """
     Create string node class
@@ -23,13 +35,16 @@
     class node_class(cls):
         """Node class created based on the input class"""
 
-        def __init__(self, x, start_mark, end_mark):
+        def __init__(
+            self, x, start_mark: Mark | None = None, end_mark: Mark | None = None
+        ):
             try:
                 cls.__init__(self, x)
             except TypeError:
                 cls.__init__(self)
-            self.start_mark = start_mark
-            self.end_mark = end_mark
+
+            self.start_mark = start_mark or _mark(0, 0)
+            self.end_mark = end_mark or _mark(0, 0)
 
         # pylint: disable=bad-classmethod-argument, unused-argument
         def __new__(self, x, start_mark, end_mark):
@@ -58,13 +73,16 @@
     class node_class(cls):
         """Node class created based on the input class"""
 
-        def __init__(self, x, start_mark, end_mark):
+        def __init__(
+            self, x, start_mark: Mark | None = None, end_mark: Mark | None = None
+        ):
+            LOGGER.debug(type(start_mark))
             try:
                 cls.__init__(self, x)
             except TypeError:
                 cls.__init__(self)
-            self.start_mark = start_mark
-            self.end_mark = end_mark
+            self.start_mark = start_mark or _mark(0, 0)
+            self.end_mark = end_mark or _mark(0, 0)
             self.condition_functions = ["Fn::If"]
 
         def __deepcopy__(self, memo):
@@ -207,12 +225,14 @@
     class sub_class(cls):
         """Node class created based on the input class"""
 
-        def __init__(self, x, start_mark, end_mark):
+        def __init__(
+            self, x, start_mark: Mark | None = None, end_mark: Mark | None = None
+        ):
             cls.__init__(self, x, start_mark, end_mark)
             self.__cache_is_valid = False
             self.__cache_sub_string = ""
-            self.__cache_sub_string_vars = set()
-            self.__cache_sub_vars = {}
+            self.__cache_sub_string_vars: Set[str] = set()
+            self.__cache_sub_vars: Dict[str, Any] = {}
             self.__setup()
 
         def __setup_list_sub_string(self, s):
@@ -280,13 +300,15 @@
     class node_class(cls):
         """Node class created based on the input class"""
 
-        def __init__(self, x, start_mark, end_mark):
+        def __init__(
+            self, x, start_mark: Mark | None = None, end_mark: Mark | None = None
+        ):
             try:
                 cls.__init__(self, x)
             except TypeError:
                 cls.__init__(self)
-            self.start_mark = start_mark
-            self.end_mark = end_mark
+            self.start_mark = start_mark or _mark(0, 0)
+            self.end_mark = end_mark or _mark(0, 0)
             self.condition_functions = ["Fn::If"]
 
         def __deepcopy__(self, memo):

{"golden_diff": "diff --git a/src/cfnlint/decode/node.py b/src/cfnlint/decode/node.py\n--- a/src/cfnlint/decode/node.py\n+++ b/src/cfnlint/decode/node.py\n@@ -3,8 +3,12 @@\n SPDX-License-Identifier: MIT-0\n \"\"\"\n \n+from __future__ import annotations\n+\n import logging\n+from collections import namedtuple\n from copy import deepcopy\n+from typing import Any, Dict, Protocol, Set\n \n import regex as re\n \n@@ -15,6 +19,14 @@\n     \"\"\"Custom error to capture Attribute Errors in the Template\"\"\"\n \n \n+class Mark(Protocol):\n+    line: int\n+    column: int\n+\n+\n+_mark = namedtuple(\"_mark\", [\"line\", \"column\"])\n+\n+\n def create_str_node_class(cls):\n     \"\"\"\n     Create string node class\n@@ -23,13 +35,16 @@\n     class node_class(cls):\n         \"\"\"Node class created based on the input class\"\"\"\n \n-        def __init__(self, x, start_mark, end_mark):\n+        def __init__(\n+            self, x, start_mark: Mark | None = None, end_mark: Mark | None = None\n+        ):\n             try:\n                 cls.__init__(self, x)\n             except TypeError:\n                 cls.__init__(self)\n-            self.start_mark = start_mark\n-            self.end_mark = end_mark\n+\n+            self.start_mark = start_mark or _mark(0, 0)\n+            self.end_mark = end_mark or _mark(0, 0)\n \n         # pylint: disable=bad-classmethod-argument, unused-argument\n         def __new__(self, x, start_mark, end_mark):\n@@ -58,13 +73,16 @@\n     class node_class(cls):\n         \"\"\"Node class created based on the input class\"\"\"\n \n-        def __init__(self, x, start_mark, end_mark):\n+        def __init__(\n+            self, x, start_mark: Mark | None = None, end_mark: Mark | None = None\n+        ):\n+            LOGGER.debug(type(start_mark))\n             try:\n                 cls.__init__(self, x)\n             except TypeError:\n                 cls.__init__(self)\n-            self.start_mark = start_mark\n-            self.end_mark = end_mark\n+            self.start_mark = start_mark or _mark(0, 0)\n+            self.end_mark = end_mark or _mark(0, 0)\n             self.condition_functions = [\"Fn::If\"]\n \n         def __deepcopy__(self, memo):\n@@ -207,12 +225,14 @@\n     class sub_class(cls):\n         \"\"\"Node class created based on the input class\"\"\"\n \n-        def __init__(self, x, start_mark, end_mark):\n+        def __init__(\n+            self, x, start_mark: Mark | None = None, end_mark: Mark | None = None\n+        ):\n             cls.__init__(self, x, start_mark, end_mark)\n             self.__cache_is_valid = False\n             self.__cache_sub_string = \"\"\n-            self.__cache_sub_string_vars = set()\n-            self.__cache_sub_vars = {}\n+            self.__cache_sub_string_vars: Set[str] = set()\n+            self.__cache_sub_vars: Dict[str, Any] = {}\n             self.__setup()\n \n         def __setup_list_sub_string(self, s):\n@@ -280,13 +300,15 @@\n     class node_class(cls):\n         \"\"\"Node class created based on the input class\"\"\"\n \n-        def __init__(self, x, start_mark, end_mark):\n+        def __init__(\n+            self, x, start_mark: Mark | None = None, end_mark: Mark | None = None\n+        ):\n             try:\n                 cls.__init__(self, x)\n             except TypeError:\n                 cls.__init__(self)\n-            self.start_mark = start_mark\n-            self.end_mark = end_mark\n+            self.start_mark = start_mark or _mark(0, 0)\n+            self.end_mark = end_mark or _mark(0, 0)\n             self.condition_functions = [\"Fn::If\"]\n \n         def __deepcopy__(self, memo):\n", "issue": "Error transforming template when using !GetAtt\n### CloudFormation Lint Version\n\n0.85.0\n\n### What operating system are you using?\n\nUbuntu\n\n### Describe the bug\n\nWhen I try to validate the template i get following error:\r\n\r\n> E0001 Error transforming template: __init__() missing 2 required positional arguments: 'start_mark' and 'end_mark'\r\naws/template.yml:1:1\r\n\r\nWhen AuthorizeUri is hardcoded the problem disappears.\r\n\n\n### Expected behavior\n\nNo error is thrown and template is validated.\n\n### Reproduction template\n\n```AWSTemplateFormatVersion: \"2010-09-09\"\r\nTransform: AWS::Serverless-2016-10-31\r\n\r\nResources:\r\n  Api:\r\n    Type: \"AWS::Serverless::GraphQLApi\"\r\n    Properties:\r\n      Name: SomeApi\r\n      SchemaUri: ./schema.graphql\r\n      Auth:\r\n        Type: AWS_LAMBDA\r\n        LambdaAuthorizer:\r\n          AuthorizerUri: !GetAtt AppSyncAuthorizerFunction.Arn\r\n\r\n  AppSyncAuthorizerFunction:\r\n    Type: AWS::Serverless::Function\r\n    Properties:\r\n      CodeUri: ./lambda/\r\n      Handler: lambda_authorizer.handle\r\n      Runtime: python3.8\r\n\n", "before_files": [{"content": "\"\"\"\nCopyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\nSPDX-License-Identifier: MIT-0\n\"\"\"\n\nimport logging\nfrom copy import deepcopy\n\nimport regex as re\n\nLOGGER = logging.getLogger(__name__)\n\n\nclass TemplateAttributeError(AttributeError):\n    \"\"\"Custom error to capture Attribute Errors in the Template\"\"\"\n\n\ndef create_str_node_class(cls):\n    \"\"\"\n    Create string node class\n    \"\"\"\n\n    class node_class(cls):\n        \"\"\"Node class created based on the input class\"\"\"\n\n        def __init__(self, x, start_mark, end_mark):\n            try:\n                cls.__init__(self, x)\n            except TypeError:\n                cls.__init__(self)\n            self.start_mark = start_mark\n            self.end_mark = end_mark\n\n        # pylint: disable=bad-classmethod-argument, unused-argument\n        def __new__(self, x, start_mark, end_mark):\n            return cls.__new__(self, x)\n\n        def __getattr__(self, name):\n            raise TemplateAttributeError(f\"{self.__class__.__name__}.{name} is invalid\")\n\n        def __deepcopy__(self, memo):\n            result = str_node(self, self.start_mark, self.end_mark)\n            memo[id(self)] = result\n            return result\n\n        def __copy__(self):\n            return self\n\n    node_class.__name__ = f\"{cls.__name__}_node\"\n    return node_class\n\n\ndef create_dict_node_class(cls):\n    \"\"\"\n    Create dynamic node class\n    \"\"\"\n\n    class node_class(cls):\n        \"\"\"Node class created based on the input class\"\"\"\n\n        def __init__(self, x, start_mark, end_mark):\n            try:\n                cls.__init__(self, x)\n            except TypeError:\n                cls.__init__(self)\n            self.start_mark = start_mark\n            self.end_mark = end_mark\n            self.condition_functions = [\"Fn::If\"]\n\n        def __deepcopy__(self, memo):\n            result = dict_node(self, self.start_mark, self.end_mark)\n            memo[id(self)] = result\n            for k, v in self.items():\n                result[deepcopy(k)] = deepcopy(v, memo)\n\n            return result\n\n        def __copy__(self):\n            return self\n\n        def is_function_returning_object(self, mappings=None):\n            \"\"\"\n            Check if an object is using a function that could return an object\n            Return True when\n                Fn::Select:\n                - 0  # or any number\n                - !FindInMap [mapname, key, value] # or any mapname, key, value\n            Otherwise False\n            \"\"\"\n            mappings = mappings or {}\n            if len(self) == 1:\n                for k, v in self.items():\n                    if k in [\"Fn::Select\"]:\n                        if isinstance(v, list):\n                            if len(v) == 2:\n                                p_v = v[1]\n                                if isinstance(p_v, dict):\n                                    if len(p_v) == 1:\n                                        for l_k in p_v.keys():\n                                            if l_k in [\"Fn::FindInMap\", \"Fn::GetAtt\"]:\n                                                return True\n\n            return False\n\n        def get(self, key, default=None):\n            \"\"\"Override the default get\"\"\"\n            if isinstance(default, dict):\n                default = dict_node(default, self.start_mark, self.end_mark)\n            return super().get(key, default)\n\n        def get_safe(self, key, default=None, path=None, type_t=()):\n            \"\"\"\n            Get values in format\n            \"\"\"\n            path = path or []\n\n            if default == {}:\n                default = dict_node({}, self.start_mark, self.end_mark)\n            value = self.get(key, default)\n            if value is None and default is None:\n                # if default is None and value is None return empty list\n                return []\n\n            # if the value is the default make sure that the default value is of type_t when specified\n            if bool(type_t) and value == default and not isinstance(default, type_t):\n                raise ValueError('\"default\" type should be of \"type_t\"')\n\n            # when not a dict see if if the value is of the right type\n            results = []\n            if not isinstance(value, (dict)):\n                if isinstance(value, type_t) or not type_t:\n                    return [(value, (path[:] + [key]))]\n            else:\n                for sub_v, sub_path in value.items_safe(path + [key]):\n                    if isinstance(sub_v, type_t) or not type_t:\n                        results.append((sub_v, sub_path))\n\n            return results\n\n        def clean(self):\n            \"\"\"Clean object to remove any Ref AWS::NoValue\"\"\"\n            result = dict_node({}, self.start_mark, self.end_mark)\n            for k, v in self.items():\n                if isinstance(v, dict) and len(v) == 1:\n                    if v.get(\"Ref\") == \"AWS::NoValue\":\n                        continue\n                result[k] = v\n            return result\n\n        def items_safe(self, path=None, type_t=()):\n            \"\"\"Get items while handling IFs\"\"\"\n            path = path or []\n            if len(self) == 1:\n                for k, v in self.items():\n                    if k == \"Fn::If\":\n                        if isinstance(v, list):\n                            if len(v) == 3:\n                                for i, if_v in enumerate(v[1:]):\n                                    if isinstance(if_v, dict):\n                                        # yield from if_v.items_safe(path[:] + [k, i - 1])\n                                        # Python 2.7 support\n                                        for items, p in if_v.items_safe(\n                                            path[:] + [k, i + 1]\n                                        ):\n                                            if isinstance(items, type_t) or not type_t:\n                                                yield items, p\n                                    elif isinstance(if_v, list):\n                                        if isinstance(if_v, type_t) or not type_t:\n                                            yield if_v, path[:] + [k, i + 1]\n                                    else:\n                                        if isinstance(if_v, type_t) or not type_t:\n                                            yield if_v, path[:] + [k, i + 1]\n                    elif not (k == \"Ref\" and v == \"AWS::NoValue\"):\n                        if isinstance(self, type_t) or not type_t:\n                            yield self.clean(), path[:]\n            else:\n                if isinstance(self, type_t) or not type_t:\n                    yield self.clean(), path[:]\n\n        def __getattr__(self, name):\n            raise TemplateAttributeError(f\"{self.__class__.__name__}.{name} is invalid\")\n\n    node_class.__name__ = f\"{cls.__name__}_node\"\n    return node_class\n\n\ndef create_intrinsic_node_class(cls):\n    \"\"\"\n    Create dynamic sub class\n    \"\"\"\n\n    class intrinsic_class(cls):\n        \"\"\"Node class created based on the input class\"\"\"\n\n        def is_valid(self):\n            raise TemplateAttributeError(\"intrisnic class shouldn't be directly used\")\n\n    intrinsic_class.__name__ = f\"{cls.__name__}_intrinsic\"\n    return intrinsic_class\n\n\ndef create_sub_node_class(cls):\n    \"\"\"\n    Create dynamic sub class\n    \"\"\"\n\n    class sub_class(cls):\n        \"\"\"Node class created based on the input class\"\"\"\n\n        def __init__(self, x, start_mark, end_mark):\n            cls.__init__(self, x, start_mark, end_mark)\n            self.__cache_is_valid = False\n            self.__cache_sub_string = \"\"\n            self.__cache_sub_string_vars = set()\n            self.__cache_sub_vars = {}\n            self.__setup()\n\n        def __setup_list_sub_string(self, s):\n            self.__cache_sub_string = s\n            regex = re.compile(r\"\\${[^!].*?}\")\n            string_params = regex.findall(s)\n\n            for string_param in string_params:\n                self.__cache_sub_string_vars.add(string_param[2:-1].strip())\n\n        def __setup_list(self, v):\n            if len(v) == 2:\n                if not isinstance(v[0], str):\n                    return\n                self.__setup_list_sub_string(v[0])\n                if not isinstance(v[1], dict):\n                    return\n                self.__cache_sub_vars = v[1]\n                self.__cache_is_valid = True\n\n        def __setup(self):\n            if len(self) == 1:\n                for k, v in self.items():\n                    if k == \"Fn::Sub\":\n                        if isinstance(v, str):\n                            self.__setup_list_sub_string(v)\n                            self.__cache_is_valid = True\n                        elif isinstance(v, list):\n                            self.__setup_list(v)\n\n        def get_defined_vars(self):\n            # Returns that are in the second part of a list Fn::Sub\n            # This function will not return implied variables from a String Ref and GetAtt\n            if self.is_valid():\n                return self.__cache_sub_vars\n\n            return {}\n\n        def get_string_vars(self):\n            # Returns all variables in the Sub String\n            if self.is_valid():\n                return self.__cache_sub_string_vars\n\n            return set()\n\n        def get_string(self):\n            # Returns the sub string as it was when it was decoded\n            if self.is_valid():\n                return self.__cache_sub_string\n\n            return \"\"\n\n        def is_valid(self):\n            return self.__cache_is_valid\n\n    sub_class.__name__ = f\"{cls.__name__}_sub\"\n    return sub_class\n\n\ndef create_dict_list_class(cls):\n    \"\"\"\n    Create dynamic list class\n    \"\"\"\n\n    class node_class(cls):\n        \"\"\"Node class created based on the input class\"\"\"\n\n        def __init__(self, x, start_mark, end_mark):\n            try:\n                cls.__init__(self, x)\n            except TypeError:\n                cls.__init__(self)\n            self.start_mark = start_mark\n            self.end_mark = end_mark\n            self.condition_functions = [\"Fn::If\"]\n\n        def __deepcopy__(self, memo):\n            result = list_node([], self.start_mark, self.end_mark)\n            memo[id(self)] = result\n            for _, v in enumerate(self):\n                result.append(deepcopy(v, memo))\n\n            return result\n\n        def __copy__(self):\n            return self\n\n        def items_safe(self, path=None, type_t=()):\n            \"\"\"Get items while handling IFs\"\"\"\n            path = path or []\n            for i, v in enumerate(self):\n                if isinstance(v, dict):\n                    for items, p in v.items_safe(path[:] + [i]):\n                        if isinstance(items, type_t) or not type_t:\n                            yield items, p\n                else:\n                    if isinstance(v, type_t) or not type_t:\n                        yield v, path[:] + [i]\n\n        def __getattr__(self, name):\n            raise TemplateAttributeError(f\"{self.__class__.__name__}.{name} is invalid\")\n\n    node_class.__name__ = f\"{cls.__name__}_node\"\n    return node_class\n\n\nstr_node = create_str_node_class(str)\ndict_node = create_dict_node_class(dict)\nlist_node = create_dict_list_class(list)\nintrinsic_node = create_intrinsic_node_class(dict_node)\nsub_node = create_sub_node_class(intrinsic_node)\n", "path": "src/cfnlint/decode/node.py"}], "after_files": [{"content": "\"\"\"\nCopyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\nSPDX-License-Identifier: MIT-0\n\"\"\"\n\nfrom __future__ import annotations\n\nimport logging\nfrom collections import namedtuple\nfrom copy import deepcopy\nfrom typing import Any, Dict, Protocol, Set\n\nimport regex as re\n\nLOGGER = logging.getLogger(__name__)\n\n\nclass TemplateAttributeError(AttributeError):\n    \"\"\"Custom error to capture Attribute Errors in the Template\"\"\"\n\n\nclass Mark(Protocol):\n    line: int\n    column: int\n\n\n_mark = namedtuple(\"_mark\", [\"line\", \"column\"])\n\n\ndef create_str_node_class(cls):\n    \"\"\"\n    Create string node class\n    \"\"\"\n\n    class node_class(cls):\n        \"\"\"Node class created based on the input class\"\"\"\n\n        def __init__(\n            self, x, start_mark: Mark | None = None, end_mark: Mark | None = None\n        ):\n            try:\n                cls.__init__(self, x)\n            except TypeError:\n                cls.__init__(self)\n\n            self.start_mark = start_mark or _mark(0, 0)\n            self.end_mark = end_mark or _mark(0, 0)\n\n        # pylint: disable=bad-classmethod-argument, unused-argument\n        def __new__(self, x, start_mark, end_mark):\n            return cls.__new__(self, x)\n\n        def __getattr__(self, name):\n            raise TemplateAttributeError(f\"{self.__class__.__name__}.{name} is invalid\")\n\n        def __deepcopy__(self, memo):\n            result = str_node(self, self.start_mark, self.end_mark)\n            memo[id(self)] = result\n            return result\n\n        def __copy__(self):\n            return self\n\n    node_class.__name__ = f\"{cls.__name__}_node\"\n    return node_class\n\n\ndef create_dict_node_class(cls):\n    \"\"\"\n    Create dynamic node class\n    \"\"\"\n\n    class node_class(cls):\n        \"\"\"Node class created based on the input class\"\"\"\n\n        def __init__(\n            self, x, start_mark: Mark | None = None, end_mark: Mark | None = None\n        ):\n            LOGGER.debug(type(start_mark))\n            try:\n                cls.__init__(self, x)\n            except TypeError:\n                cls.__init__(self)\n            self.start_mark = start_mark or _mark(0, 0)\n            self.end_mark = end_mark or _mark(0, 0)\n            self.condition_functions = [\"Fn::If\"]\n\n        def __deepcopy__(self, memo):\n            result = dict_node(self, self.start_mark, self.end_mark)\n            memo[id(self)] = result\n            for k, v in self.items():\n                result[deepcopy(k)] = deepcopy(v, memo)\n\n            return result\n\n        def __copy__(self):\n            return self\n\n        def is_function_returning_object(self, mappings=None):\n            \"\"\"\n            Check if an object is using a function that could return an object\n            Return True when\n                Fn::Select:\n                - 0  # or any number\n                - !FindInMap [mapname, key, value] # or any mapname, key, value\n            Otherwise False\n            \"\"\"\n            mappings = mappings or {}\n            if len(self) == 1:\n                for k, v in self.items():\n                    if k in [\"Fn::Select\"]:\n                        if isinstance(v, list):\n                            if len(v) == 2:\n                                p_v = v[1]\n                                if isinstance(p_v, dict):\n                                    if len(p_v) == 1:\n                                        for l_k in p_v.keys():\n                                            if l_k in [\"Fn::FindInMap\", \"Fn::GetAtt\"]:\n                                                return True\n\n            return False\n\n        def get(self, key, default=None):\n            \"\"\"Override the default get\"\"\"\n            if isinstance(default, dict):\n                default = dict_node(default, self.start_mark, self.end_mark)\n            return super().get(key, default)\n\n        def get_safe(self, key, default=None, path=None, type_t=()):\n            \"\"\"\n            Get values in format\n            \"\"\"\n            path = path or []\n\n            if default == {}:\n                default = dict_node({}, self.start_mark, self.end_mark)\n            value = self.get(key, default)\n            if value is None and default is None:\n                # if default is None and value is None return empty list\n                return []\n\n            # if the value is the default make sure that the default value is of type_t when specified\n            if bool(type_t) and value == default and not isinstance(default, type_t):\n                raise ValueError('\"default\" type should be of \"type_t\"')\n\n            # when not a dict see if if the value is of the right type\n            results = []\n            if not isinstance(value, (dict)):\n                if isinstance(value, type_t) or not type_t:\n                    return [(value, (path[:] + [key]))]\n            else:\n                for sub_v, sub_path in value.items_safe(path + [key]):\n                    if isinstance(sub_v, type_t) or not type_t:\n                        results.append((sub_v, sub_path))\n\n            return results\n\n        def clean(self):\n            \"\"\"Clean object to remove any Ref AWS::NoValue\"\"\"\n            result = dict_node({}, self.start_mark, self.end_mark)\n            for k, v in self.items():\n                if isinstance(v, dict) and len(v) == 1:\n                    if v.get(\"Ref\") == \"AWS::NoValue\":\n                        continue\n                result[k] = v\n            return result\n\n        def items_safe(self, path=None, type_t=()):\n            \"\"\"Get items while handling IFs\"\"\"\n            path = path or []\n            if len(self) == 1:\n                for k, v in self.items():\n                    if k == \"Fn::If\":\n                        if isinstance(v, list):\n                            if len(v) == 3:\n                                for i, if_v in enumerate(v[1:]):\n                                    if isinstance(if_v, dict):\n                                        # yield from if_v.items_safe(path[:] + [k, i - 1])\n                                        # Python 2.7 support\n                                        for items, p in if_v.items_safe(\n                                            path[:] + [k, i + 1]\n                                        ):\n                                            if isinstance(items, type_t) or not type_t:\n                                                yield items, p\n                                    elif isinstance(if_v, list):\n                                        if isinstance(if_v, type_t) or not type_t:\n                                            yield if_v, path[:] + [k, i + 1]\n                                    else:\n                                        if isinstance(if_v, type_t) or not type_t:\n                                            yield if_v, path[:] + [k, i + 1]\n                    elif not (k == \"Ref\" and v == \"AWS::NoValue\"):\n                        if isinstance(self, type_t) or not type_t:\n                            yield self.clean(), path[:]\n            else:\n                if isinstance(self, type_t) or not type_t:\n                    yield self.clean(), path[:]\n\n        def __getattr__(self, name):\n            raise TemplateAttributeError(f\"{self.__class__.__name__}.{name} is invalid\")\n\n    node_class.__name__ = f\"{cls.__name__}_node\"\n    return node_class\n\n\ndef create_intrinsic_node_class(cls):\n    \"\"\"\n    Create dynamic sub class\n    \"\"\"\n\n    class intrinsic_class(cls):\n        \"\"\"Node class created based on the input class\"\"\"\n\n        def is_valid(self):\n            raise TemplateAttributeError(\"intrisnic class shouldn't be directly used\")\n\n    intrinsic_class.__name__ = f\"{cls.__name__}_intrinsic\"\n    return intrinsic_class\n\n\ndef create_sub_node_class(cls):\n    \"\"\"\n    Create dynamic sub class\n    \"\"\"\n\n    class sub_class(cls):\n        \"\"\"Node class created based on the input class\"\"\"\n\n        def __init__(\n            self, x, start_mark: Mark | None = None, end_mark: Mark | None = None\n        ):\n            cls.__init__(self, x, start_mark, end_mark)\n            self.__cache_is_valid = False\n            self.__cache_sub_string = \"\"\n            self.__cache_sub_string_vars: Set[str] = set()\n            self.__cache_sub_vars: Dict[str, Any] = {}\n            self.__setup()\n\n        def __setup_list_sub_string(self, s):\n            self.__cache_sub_string = s\n            regex = re.compile(r\"\\${[^!].*?}\")\n            string_params = regex.findall(s)\n\n            for string_param in string_params:\n                self.__cache_sub_string_vars.add(string_param[2:-1].strip())\n\n        def __setup_list(self, v):\n            if len(v) == 2:\n                if not isinstance(v[0], str):\n                    return\n                self.__setup_list_sub_string(v[0])\n                if not isinstance(v[1], dict):\n                    return\n                self.__cache_sub_vars = v[1]\n                self.__cache_is_valid = True\n\n        def __setup(self):\n            if len(self) == 1:\n                for k, v in self.items():\n                    if k == \"Fn::Sub\":\n                        if isinstance(v, str):\n                            self.__setup_list_sub_string(v)\n                            self.__cache_is_valid = True\n                        elif isinstance(v, list):\n                            self.__setup_list(v)\n\n        def get_defined_vars(self):\n            # Returns that are in the second part of a list Fn::Sub\n            # This function will not return implied variables from a String Ref and GetAtt\n            if self.is_valid():\n                return self.__cache_sub_vars\n\n            return {}\n\n        def get_string_vars(self):\n            # Returns all variables in the Sub String\n            if self.is_valid():\n                return self.__cache_sub_string_vars\n\n            return set()\n\n        def get_string(self):\n            # Returns the sub string as it was when it was decoded\n            if self.is_valid():\n                return self.__cache_sub_string\n\n            return \"\"\n\n        def is_valid(self):\n            return self.__cache_is_valid\n\n    sub_class.__name__ = f\"{cls.__name__}_sub\"\n    return sub_class\n\n\ndef create_dict_list_class(cls):\n    \"\"\"\n    Create dynamic list class\n    \"\"\"\n\n    class node_class(cls):\n        \"\"\"Node class created based on the input class\"\"\"\n\n        def __init__(\n            self, x, start_mark: Mark | None = None, end_mark: Mark | None = None\n        ):\n            try:\n                cls.__init__(self, x)\n            except TypeError:\n                cls.__init__(self)\n            self.start_mark = start_mark or _mark(0, 0)\n            self.end_mark = end_mark or _mark(0, 0)\n            self.condition_functions = [\"Fn::If\"]\n\n        def __deepcopy__(self, memo):\n            result = list_node([], self.start_mark, self.end_mark)\n            memo[id(self)] = result\n            for _, v in enumerate(self):\n                result.append(deepcopy(v, memo))\n\n            return result\n\n        def __copy__(self):\n            return self\n\n        def items_safe(self, path=None, type_t=()):\n            \"\"\"Get items while handling IFs\"\"\"\n            path = path or []\n            for i, v in enumerate(self):\n                if isinstance(v, dict):\n                    for items, p in v.items_safe(path[:] + [i]):\n                        if isinstance(items, type_t) or not type_t:\n                            yield items, p\n                else:\n                    if isinstance(v, type_t) or not type_t:\n                        yield v, path[:] + [i]\n\n        def __getattr__(self, name):\n            raise TemplateAttributeError(f\"{self.__class__.__name__}.{name} is invalid\")\n\n    node_class.__name__ = f\"{cls.__name__}_node\"\n    return node_class\n\n\nstr_node = create_str_node_class(str)\ndict_node = create_dict_node_class(dict)\nlist_node = create_dict_list_class(list)\nintrinsic_node = create_intrinsic_node_class(dict_node)\nsub_node = create_sub_node_class(intrinsic_node)\n", "path": "src/cfnlint/decode/node.py"}]}