localstack-core 4.4.1.dev59__py3-none-any.whl → 4.4.1.dev65__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (22) hide show
  1. localstack/aws/api/ec2/__init__.py +70 -2
  2. localstack/aws/api/s3/__init__.py +2 -0
  3. localstack/services/cloudformation/engine/v2/change_set_model.py +107 -146
  4. localstack/services/cloudformation/engine/v2/change_set_model_describer.py +41 -22
  5. localstack/services/cloudformation/engine/v2/change_set_model_executor.py +51 -23
  6. localstack/services/cloudformation/engine/v2/change_set_model_preproc.py +244 -123
  7. localstack/services/cloudformation/engine/v2/change_set_model_visitor.py +16 -1
  8. localstack/services/s3/provider.py +3 -2
  9. localstack/services/sns/provider.py +27 -9
  10. localstack/testing/pytest/cloudformation/fixtures.py +13 -1
  11. localstack/version.py +2 -2
  12. {localstack_core-4.4.1.dev59.dist-info → localstack_core-4.4.1.dev65.dist-info}/METADATA +3 -3
  13. {localstack_core-4.4.1.dev59.dist-info → localstack_core-4.4.1.dev65.dist-info}/RECORD +21 -21
  14. localstack_core-4.4.1.dev65.dist-info/plux.json +1 -0
  15. localstack_core-4.4.1.dev59.dist-info/plux.json +0 -1
  16. {localstack_core-4.4.1.dev59.data → localstack_core-4.4.1.dev65.data}/scripts/localstack +0 -0
  17. {localstack_core-4.4.1.dev59.data → localstack_core-4.4.1.dev65.data}/scripts/localstack-supervisor +0 -0
  18. {localstack_core-4.4.1.dev59.data → localstack_core-4.4.1.dev65.data}/scripts/localstack.bat +0 -0
  19. {localstack_core-4.4.1.dev59.dist-info → localstack_core-4.4.1.dev65.dist-info}/WHEEL +0 -0
  20. {localstack_core-4.4.1.dev59.dist-info → localstack_core-4.4.1.dev65.dist-info}/entry_points.txt +0 -0
  21. {localstack_core-4.4.1.dev59.dist-info → localstack_core-4.4.1.dev65.dist-info}/licenses/LICENSE.txt +0 -0
  22. {localstack_core-4.4.1.dev59.dist-info → localstack_core-4.4.1.dev65.dist-info}/top_level.txt +0 -0
@@ -23,6 +23,9 @@ class NothingType:
23
23
  cls._singleton = super().__new__(cls)
24
24
  return cls._singleton
25
25
 
26
+ def __eq__(self, other):
27
+ return is_nothing(other)
28
+
26
29
  def __str__(self):
27
30
  return repr(self)
28
31
 
@@ -35,11 +38,46 @@ class NothingType:
35
38
  def __iter__(self):
36
39
  return iter(())
37
40
 
41
+ def __contains__(self, item):
42
+ return False
43
+
38
44
 
39
45
  Maybe = Union[T, NothingType]
40
46
  Nothing = NothingType()
41
47
 
42
48
 
49
+ def is_nothing(value: Any) -> bool:
50
+ return isinstance(value, NothingType)
51
+
52
+
53
+ def is_created(before: Maybe[Any], after: Maybe[Any]) -> bool:
54
+ return is_nothing(before) and not is_nothing(after)
55
+
56
+
57
+ def is_removed(before: Maybe[Any], after: Maybe[Any]) -> bool:
58
+ return not is_nothing(before) and is_nothing(after)
59
+
60
+
61
+ def parent_change_type_of(children: list[Maybe[ChangeSetEntity]]):
62
+ change_types = [c.change_type for c in children if not is_nothing(c)]
63
+ if not change_types:
64
+ return ChangeType.UNCHANGED
65
+ first_type = change_types[0]
66
+ if all(ct == first_type for ct in change_types):
67
+ return first_type
68
+ return ChangeType.MODIFIED
69
+
70
+
71
+ def change_type_of(before: Maybe[Any], after: Maybe[Any], children: list[Maybe[ChangeSetEntity]]):
72
+ if is_created(before, after):
73
+ change_type = ChangeType.CREATED
74
+ elif is_removed(before, after):
75
+ change_type = ChangeType.REMOVED
76
+ else:
77
+ change_type = parent_change_type_of(children)
78
+ return change_type
79
+
80
+
43
81
  class Scope(str):
44
82
  _ROOT_SCOPE: Final[str] = str()
45
83
  _SEPARATOR: Final[str] = "/"
@@ -66,14 +104,6 @@ class ChangeType(enum.Enum):
66
104
  def __str__(self):
67
105
  return self.value
68
106
 
69
- def for_child(self, child_change_type: ChangeType) -> ChangeType:
70
- if child_change_type == self:
71
- return self
72
- elif self == ChangeType.UNCHANGED:
73
- return child_change_type
74
- else:
75
- return ChangeType.MODIFIED
76
-
77
107
 
78
108
  class ChangeSetEntity(abc.ABC):
79
109
  scope: Final[Scope]
@@ -122,13 +152,13 @@ class NodeTemplate(ChangeSetNode):
122
152
  def __init__(
123
153
  self,
124
154
  scope: Scope,
125
- change_type: ChangeType,
126
155
  mappings: NodeMappings,
127
156
  parameters: NodeParameters,
128
157
  conditions: NodeConditions,
129
158
  resources: NodeResources,
130
159
  outputs: NodeOutputs,
131
160
  ):
161
+ change_type = parent_change_type_of([resources, outputs])
132
162
  super().__init__(scope=scope, change_type=change_type)
133
163
  self.mappings = mappings
134
164
  self.parameters = parameters
@@ -151,17 +181,17 @@ class NodeParameter(ChangeSetNode):
151
181
  name: Final[str]
152
182
  type_: Final[ChangeSetEntity]
153
183
  dynamic_value: Final[ChangeSetEntity]
154
- default_value: Final[Optional[ChangeSetEntity]]
184
+ default_value: Final[Maybe[ChangeSetEntity]]
155
185
 
156
186
  def __init__(
157
187
  self,
158
188
  scope: Scope,
159
- change_type: ChangeType,
160
189
  name: str,
161
190
  type_: ChangeSetEntity,
162
191
  dynamic_value: ChangeSetEntity,
163
- default_value: Optional[ChangeSetEntity],
192
+ default_value: Maybe[ChangeSetEntity],
164
193
  ):
194
+ change_type = parent_change_type_of([type_, default_value, dynamic_value])
165
195
  super().__init__(scope=scope, change_type=change_type)
166
196
  self.name = name
167
197
  self.type_ = type_
@@ -172,7 +202,8 @@ class NodeParameter(ChangeSetNode):
172
202
  class NodeParameters(ChangeSetNode):
173
203
  parameters: Final[list[NodeParameter]]
174
204
 
175
- def __init__(self, scope: Scope, change_type: ChangeType, parameters: list[NodeParameter]):
205
+ def __init__(self, scope: Scope, parameters: list[NodeParameter]):
206
+ change_type = parent_change_type_of(parameters)
176
207
  super().__init__(scope=scope, change_type=change_type)
177
208
  self.parameters = parameters
178
209
 
@@ -181,8 +212,8 @@ class NodeMapping(ChangeSetNode):
181
212
  name: Final[str]
182
213
  bindings: Final[NodeObject]
183
214
 
184
- def __init__(self, scope: Scope, change_type: ChangeType, name: str, bindings: NodeObject):
185
- super().__init__(scope=scope, change_type=change_type)
215
+ def __init__(self, scope: Scope, name: str, bindings: NodeObject):
216
+ super().__init__(scope=scope, change_type=bindings.change_type)
186
217
  self.name = name
187
218
  self.bindings = bindings
188
219
 
@@ -190,7 +221,8 @@ class NodeMapping(ChangeSetNode):
190
221
  class NodeMappings(ChangeSetNode):
191
222
  mappings: Final[list[NodeMapping]]
192
223
 
193
- def __init__(self, scope: Scope, change_type: ChangeType, mappings: list[NodeMapping]):
224
+ def __init__(self, scope: Scope, mappings: list[NodeMapping]):
225
+ change_type = parent_change_type_of(mappings)
194
226
  super().__init__(scope=scope, change_type=change_type)
195
227
  self.mappings = mappings
196
228
 
@@ -198,18 +230,18 @@ class NodeMappings(ChangeSetNode):
198
230
  class NodeOutput(ChangeSetNode):
199
231
  name: Final[str]
200
232
  value: Final[ChangeSetEntity]
201
- export: Final[Optional[ChangeSetEntity]]
202
- condition_reference: Final[Optional[TerminalValue]]
233
+ export: Final[Maybe[ChangeSetEntity]]
234
+ condition_reference: Final[Maybe[TerminalValue]]
203
235
 
204
236
  def __init__(
205
237
  self,
206
238
  scope: Scope,
207
- change_type: ChangeType,
208
239
  name: str,
209
240
  value: ChangeSetEntity,
210
- export: Optional[ChangeSetEntity],
211
- conditional_reference: Optional[TerminalValue],
241
+ export: Maybe[ChangeSetEntity],
242
+ conditional_reference: Maybe[TerminalValue],
212
243
  ):
244
+ change_type = parent_change_type_of([value, export, conditional_reference])
213
245
  super().__init__(scope=scope, change_type=change_type)
214
246
  self.name = name
215
247
  self.value = value
@@ -220,7 +252,8 @@ class NodeOutput(ChangeSetNode):
220
252
  class NodeOutputs(ChangeSetNode):
221
253
  outputs: Final[list[NodeOutput]]
222
254
 
223
- def __init__(self, scope: Scope, change_type: ChangeType, outputs: list[NodeOutput]):
255
+ def __init__(self, scope: Scope, outputs: list[NodeOutput]):
256
+ change_type = parent_change_type_of(outputs)
224
257
  super().__init__(scope=scope, change_type=change_type)
225
258
  self.outputs = outputs
226
259
 
@@ -229,8 +262,8 @@ class NodeCondition(ChangeSetNode):
229
262
  name: Final[str]
230
263
  body: Final[ChangeSetEntity]
231
264
 
232
- def __init__(self, scope: Scope, change_type: ChangeType, name: str, body: ChangeSetEntity):
233
- super().__init__(scope=scope, change_type=change_type)
265
+ def __init__(self, scope: Scope, name: str, body: ChangeSetEntity):
266
+ super().__init__(scope=scope, change_type=body.change_type)
234
267
  self.name = name
235
268
  self.body = body
236
269
 
@@ -238,7 +271,8 @@ class NodeCondition(ChangeSetNode):
238
271
  class NodeConditions(ChangeSetNode):
239
272
  conditions: Final[list[NodeCondition]]
240
273
 
241
- def __init__(self, scope: Scope, change_type: ChangeType, conditions: list[NodeCondition]):
274
+ def __init__(self, scope: Scope, conditions: list[NodeCondition]):
275
+ change_type = parent_change_type_of(conditions)
242
276
  super().__init__(scope=scope, change_type=change_type)
243
277
  self.conditions = conditions
244
278
 
@@ -246,7 +280,8 @@ class NodeConditions(ChangeSetNode):
246
280
  class NodeResources(ChangeSetNode):
247
281
  resources: Final[list[NodeResource]]
248
282
 
249
- def __init__(self, scope: Scope, change_type: ChangeType, resources: list[NodeResource]):
283
+ def __init__(self, scope: Scope, resources: list[NodeResource]):
284
+ change_type = parent_change_type_of(resources)
250
285
  super().__init__(scope=scope, change_type=change_type)
251
286
  self.resources = resources
252
287
 
@@ -254,9 +289,9 @@ class NodeResources(ChangeSetNode):
254
289
  class NodeResource(ChangeSetNode):
255
290
  name: Final[str]
256
291
  type_: Final[ChangeSetTerminal]
257
- condition_reference: Final[Optional[TerminalValue]]
258
292
  properties: Final[NodeProperties]
259
- depends_on: Final[Optional[NodeDependsOn]]
293
+ condition_reference: Final[Maybe[TerminalValue]]
294
+ depends_on: Final[Maybe[NodeDependsOn]]
260
295
 
261
296
  def __init__(
262
297
  self,
@@ -265,8 +300,8 @@ class NodeResource(ChangeSetNode):
265
300
  name: str,
266
301
  type_: ChangeSetTerminal,
267
302
  properties: NodeProperties,
268
- condition_reference: Optional[TerminalValue],
269
- depends_on: Optional[NodeDependsOn],
303
+ condition_reference: Maybe[TerminalValue],
304
+ depends_on: Maybe[NodeDependsOn],
270
305
  ):
271
306
  super().__init__(scope=scope, change_type=change_type)
272
307
  self.name = name
@@ -279,7 +314,8 @@ class NodeResource(ChangeSetNode):
279
314
  class NodeProperties(ChangeSetNode):
280
315
  properties: Final[list[NodeProperty]]
281
316
 
282
- def __init__(self, scope: Scope, change_type: ChangeType, properties: list[NodeProperty]):
317
+ def __init__(self, scope: Scope, properties: list[NodeProperty]):
318
+ change_type = parent_change_type_of(properties)
283
319
  super().__init__(scope=scope, change_type=change_type)
284
320
  self.properties = properties
285
321
 
@@ -287,8 +323,8 @@ class NodeProperties(ChangeSetNode):
287
323
  class NodeDependsOn(ChangeSetNode):
288
324
  depends_on: Final[NodeArray]
289
325
 
290
- def __init__(self, scope: Scope, change_type: ChangeType, depends_on: NodeArray):
291
- super().__init__(scope=scope, change_type=change_type)
326
+ def __init__(self, scope: Scope, depends_on: NodeArray):
327
+ super().__init__(scope=scope, change_type=depends_on.change_type)
292
328
  self.depends_on = depends_on
293
329
 
294
330
 
@@ -296,8 +332,8 @@ class NodeProperty(ChangeSetNode):
296
332
  name: Final[str]
297
333
  value: Final[ChangeSetEntity]
298
334
 
299
- def __init__(self, scope: Scope, change_type: ChangeType, name: str, value: ChangeSetEntity):
300
- super().__init__(scope=scope, change_type=change_type)
335
+ def __init__(self, scope: Scope, name: str, value: ChangeSetEntity):
336
+ super().__init__(scope=scope, change_type=value.change_type)
301
337
  self.name = name
302
338
  self.value = value
303
339
 
@@ -386,6 +422,8 @@ FnGetAttKey: Final[str] = "Fn::GetAtt"
386
422
  FnEqualsKey: Final[str] = "Fn::Equals"
387
423
  FnFindInMapKey: Final[str] = "Fn::FindInMap"
388
424
  FnSubKey: Final[str] = "Fn::Sub"
425
+ FnTransform: Final[str] = "Fn::Transform"
426
+ FnSelect: Final[str] = "Fn::Select"
389
427
  INTRINSIC_FUNCTIONS: Final[set[str]] = {
390
428
  RefKey,
391
429
  FnIfKey,
@@ -395,6 +433,8 @@ INTRINSIC_FUNCTIONS: Final[set[str]] = {
395
433
  FnGetAttKey,
396
434
  FnFindInMapKey,
397
435
  FnSubKey,
436
+ FnTransform,
437
+ FnSelect,
398
438
  }
399
439
 
400
440
 
@@ -440,9 +480,9 @@ class ChangeSetModel:
440
480
  terminal_value = self._visited_scopes.get(scope)
441
481
  if isinstance(terminal_value, TerminalValue):
442
482
  return terminal_value
443
- if self._is_created(before=before_value, after=after_value):
483
+ if is_created(before=before_value, after=after_value):
444
484
  terminal_value = TerminalValueCreated(scope=scope, value=after_value)
445
- elif self._is_removed(before=before_value, after=after_value):
485
+ elif is_removed(before=before_value, after=after_value):
446
486
  terminal_value = TerminalValueRemoved(scope=scope, value=before_value)
447
487
  elif before_value == after_value:
448
488
  terminal_value = TerminalValueUnchanged(scope=scope, value=before_value)
@@ -466,9 +506,9 @@ class ChangeSetModel:
466
506
  arguments = self._visit_value(
467
507
  scope=scope, before_value=before_arguments, after_value=after_arguments
468
508
  )
469
- if self._is_created(before=before_arguments, after=after_arguments):
509
+ if is_created(before=before_arguments, after=after_arguments):
470
510
  change_type = ChangeType.CREATED
471
- elif self._is_removed(before=before_arguments, after=after_arguments):
511
+ elif is_removed(before=before_arguments, after=after_arguments):
472
512
  change_type = ChangeType.REMOVED
473
513
  else:
474
514
  function_name = intrinsic_function.replace("::", "_")
@@ -523,7 +563,6 @@ class ChangeSetModel:
523
563
  def _resolve_intrinsic_function_ref(self, arguments: ChangeSetEntity) -> ChangeType:
524
564
  if arguments.change_type != ChangeType.UNCHANGED:
525
565
  return arguments.change_type
526
- # TODO: add support for nested functions, here we assume the argument is a logicalID.
527
566
  if not isinstance(arguments, TerminalValue):
528
567
  return arguments.change_type
529
568
 
@@ -587,8 +626,7 @@ class ChangeSetModel:
587
626
  )
588
627
  if not isinstance(node_condition, NodeCondition):
589
628
  raise RuntimeError()
590
- change_types = [node_condition.change_type, *arguments.array[1:]]
591
- change_type = self._change_type_for_parent_of(change_types=change_types)
629
+ change_type = parent_change_type_of([node_condition, *arguments[1:]])
592
630
  return change_type
593
631
 
594
632
  def _visit_array(
@@ -603,12 +641,7 @@ class ChangeSetModel:
603
641
  scope=value_scope, before_value=before_value, after_value=after_value
604
642
  )
605
643
  array.append(value)
606
- if self._is_created(before=before_array, after=after_array):
607
- change_type = ChangeType.CREATED
608
- elif self._is_removed(before=before_array, after=after_array):
609
- change_type = ChangeType.REMOVED
610
- else:
611
- change_type = self._change_type_for_parent_of([value.change_type for value in array])
644
+ change_type = change_type_of(before_array, after_array, array)
612
645
  return NodeArray(scope=scope, change_type=change_type, array=array)
613
646
 
614
647
  def _visit_object(
@@ -617,12 +650,6 @@ class ChangeSetModel:
617
650
  node_object = self._visited_scopes.get(scope)
618
651
  if isinstance(node_object, NodeObject):
619
652
  return node_object
620
- if self._is_created(before=before_object, after=after_object):
621
- change_type = ChangeType.CREATED
622
- elif self._is_removed(before=before_object, after=after_object):
623
- change_type = ChangeType.REMOVED
624
- else:
625
- change_type = ChangeType.UNCHANGED
626
653
  binding_names = self._safe_keys_of(before_object, after_object)
627
654
  bindings: dict[str, ChangeSetEntity] = dict()
628
655
  for binding_name in binding_names:
@@ -633,7 +660,7 @@ class ChangeSetModel:
633
660
  scope=binding_scope, before_value=before_value, after_value=after_value
634
661
  )
635
662
  bindings[binding_name] = value
636
- change_type = change_type.for_child(value.change_type)
663
+ change_type = change_type_of(before_object, after_object, list(bindings.values()))
637
664
  node_object = NodeObject(scope=scope, change_type=change_type, bindings=bindings)
638
665
  self._visited_scopes[scope] = node_object
639
666
  return node_object
@@ -661,9 +688,9 @@ class ChangeSetModel:
661
688
  unset = object()
662
689
  if before_type_name == after_type_name:
663
690
  dominant_value = before_value
664
- elif self._is_created(before=before_value, after=after_value):
691
+ elif is_created(before=before_value, after=after_value):
665
692
  dominant_value = after_value
666
- elif self._is_removed(before=before_value, after=after_value):
693
+ elif is_removed(before=before_value, after=after_value):
667
694
  dominant_value = before_value
668
695
  else:
669
696
  dominant_value = unset
@@ -714,9 +741,7 @@ class ChangeSetModel:
714
741
  value = self._visit_value(
715
742
  scope=scope, before_value=before_property, after_value=after_property
716
743
  )
717
- node_property = NodeProperty(
718
- scope=scope, change_type=value.change_type, name=property_name, value=value
719
- )
744
+ node_property = NodeProperty(scope=scope, name=property_name, value=value)
720
745
  self._visited_scopes[scope] = node_property
721
746
  return node_property
722
747
 
@@ -726,10 +751,8 @@ class ChangeSetModel:
726
751
  node_properties = self._visited_scopes.get(scope)
727
752
  if isinstance(node_properties, NodeProperties):
728
753
  return node_properties
729
- # TODO: double check we are sure not to have this be a NodeObject
730
754
  property_names: list[str] = self._safe_keys_of(before_properties, after_properties)
731
755
  properties: list[NodeProperty] = list()
732
- change_type = ChangeType.UNCHANGED
733
756
  for property_name in property_names:
734
757
  property_scope, (before_property, after_property) = self._safe_access_in(
735
758
  scope, property_name, before_properties, after_properties
@@ -741,10 +764,7 @@ class ChangeSetModel:
741
764
  after_property=after_property,
742
765
  )
743
766
  properties.append(property_)
744
- change_type = change_type.for_child(property_.change_type)
745
- node_properties = NodeProperties(
746
- scope=scope, change_type=change_type, properties=properties
747
- )
767
+ node_properties = NodeProperties(scope=scope, properties=properties)
748
768
  self._visited_scopes[scope] = node_properties
749
769
  return node_properties
750
770
 
@@ -766,13 +786,6 @@ class ChangeSetModel:
766
786
  if isinstance(node_resource, NodeResource):
767
787
  return node_resource
768
788
 
769
- if self._is_created(before=before_resource, after=after_resource):
770
- change_type = ChangeType.CREATED
771
- elif self._is_removed(before=before_resource, after=after_resource):
772
- change_type = ChangeType.REMOVED
773
- else:
774
- change_type = ChangeType.UNCHANGED
775
-
776
789
  scope_type, (before_type, after_type) = self._safe_access_in(
777
790
  scope, TypeKey, before_resource, after_resource
778
791
  )
@@ -780,7 +793,7 @@ class ChangeSetModel:
780
793
  scope=scope_type, before_type=before_type, after_type=after_type
781
794
  )
782
795
 
783
- condition_reference = None
796
+ condition_reference = Nothing
784
797
  scope_condition, (before_condition, after_condition) = self._safe_access_in(
785
798
  scope, ConditionKey, before_resource, after_resource
786
799
  )
@@ -789,7 +802,7 @@ class ChangeSetModel:
789
802
  scope_condition, before_condition, after_condition
790
803
  )
791
804
 
792
- depends_on = None
805
+ depends_on = Nothing
793
806
  scope_depends_on, (before_depends_on, after_depends_on) = self._safe_access_in(
794
807
  scope, DependsOnKey, before_resource, after_resource
795
808
  )
@@ -806,10 +819,10 @@ class ChangeSetModel:
806
819
  before_properties=before_properties,
807
820
  after_properties=after_properties,
808
821
  )
809
- if properties.properties:
810
- # Properties were defined in the before or after template, thus must play a role
811
- # in affecting the change type of this resource.
812
- change_type = change_type.for_child(properties.change_type)
822
+
823
+ change_type = change_type_of(
824
+ before_resource, after_resource, [properties, condition_reference, depends_on]
825
+ )
813
826
  node_resource = NodeResource(
814
827
  scope=scope,
815
828
  change_type=change_type,
@@ -826,7 +839,6 @@ class ChangeSetModel:
826
839
  self, scope: Scope, before_resources: Maybe[dict], after_resources: Maybe[dict]
827
840
  ) -> NodeResources:
828
841
  # TODO: investigate type changes behavior.
829
- change_type = ChangeType.UNCHANGED
830
842
  resources: list[NodeResource] = list()
831
843
  resource_names = self._safe_keys_of(before_resources, after_resources)
832
844
  for resource_name in resource_names:
@@ -840,8 +852,7 @@ class ChangeSetModel:
840
852
  after_resource=after_resource,
841
853
  )
842
854
  resources.append(resource)
843
- change_type = change_type.for_child(resource.change_type)
844
- return NodeResources(scope=scope, change_type=change_type, resources=resources)
855
+ return NodeResources(scope=scope, resources=resources)
845
856
 
846
857
  def _visit_mapping(
847
858
  self, scope: Scope, name: str, before_mapping: Maybe[dict], after_mapping: Maybe[dict]
@@ -849,14 +860,11 @@ class ChangeSetModel:
849
860
  bindings = self._visit_object(
850
861
  scope=scope, before_object=before_mapping, after_object=after_mapping
851
862
  )
852
- return NodeMapping(
853
- scope=scope, change_type=bindings.change_type, name=name, bindings=bindings
854
- )
863
+ return NodeMapping(scope=scope, name=name, bindings=bindings)
855
864
 
856
865
  def _visit_mappings(
857
866
  self, scope: Scope, before_mappings: Maybe[dict], after_mappings: Maybe[dict]
858
867
  ) -> NodeMappings:
859
- change_type = ChangeType.UNCHANGED
860
868
  mappings: list[NodeMapping] = list()
861
869
  mapping_names = self._safe_keys_of(before_mappings, after_mappings)
862
870
  for mapping_name in mapping_names:
@@ -870,8 +878,7 @@ class ChangeSetModel:
870
878
  after_mapping=after_mapping,
871
879
  )
872
880
  mappings.append(mapping)
873
- change_type = change_type.for_child(mapping.change_type)
874
- return NodeMappings(scope=scope, change_type=change_type, mappings=mappings)
881
+ return NodeMappings(scope=scope, mappings=mappings)
875
882
 
876
883
  def _visit_dynamic_parameter(self, parameter_name: str) -> ChangeSetEntity:
877
884
  scope = Scope("Dynamic").open_scope("Parameters")
@@ -906,13 +913,8 @@ class ChangeSetModel:
906
913
 
907
914
  dynamic_value = self._visit_dynamic_parameter(parameter_name=parameter_name)
908
915
 
909
- change_type = self._change_type_for_parent_of(
910
- change_types=[type_.change_type, default_value.change_type, dynamic_value.change_type]
911
- )
912
-
913
916
  node_parameter = NodeParameter(
914
917
  scope=scope,
915
- change_type=change_type,
916
918
  name=parameter_name,
917
919
  type_=type_,
918
920
  default_value=default_value,
@@ -929,7 +931,6 @@ class ChangeSetModel:
929
931
  return node_parameters
930
932
  parameter_names: list[str] = self._safe_keys_of(before_parameters, after_parameters)
931
933
  parameters: list[NodeParameter] = list()
932
- change_type = ChangeType.UNCHANGED
933
934
  for parameter_name in parameter_names:
934
935
  parameter_scope, (before_parameter, after_parameter) = self._safe_access_in(
935
936
  scope, parameter_name, before_parameters, after_parameters
@@ -941,10 +942,7 @@ class ChangeSetModel:
941
942
  after_parameter=after_parameter,
942
943
  )
943
944
  parameters.append(parameter)
944
- change_type = change_type.for_child(parameter.change_type)
945
- node_parameters = NodeParameters(
946
- scope=scope, change_type=change_type, parameters=parameters
947
- )
945
+ node_parameters = NodeParameters(scope=scope, parameters=parameters)
948
946
  self._visited_scopes[scope] = node_parameters
949
947
  return node_parameters
950
948
 
@@ -975,9 +973,7 @@ class ChangeSetModel:
975
973
  node_array = self._visit_array(
976
974
  scope=scope, before_array=before_depends_on, after_array=after_depends_on
977
975
  )
978
- node_depends_on = NodeDependsOn(
979
- scope=scope, change_type=node_array.change_type, depends_on=node_array
980
- )
976
+ node_depends_on = NodeDependsOn(scope=scope, depends_on=node_array)
981
977
  return node_depends_on
982
978
 
983
979
  def _visit_condition(
@@ -993,9 +989,7 @@ class ChangeSetModel:
993
989
  body = self._visit_value(
994
990
  scope=scope, before_value=before_condition, after_value=after_condition
995
991
  )
996
- node_condition = NodeCondition(
997
- scope=scope, change_type=body.change_type, name=condition_name, body=body
998
- )
992
+ node_condition = NodeCondition(scope=scope, name=condition_name, body=body)
999
993
  self._visited_scopes[scope] = node_condition
1000
994
  return node_condition
1001
995
 
@@ -1007,7 +1001,6 @@ class ChangeSetModel:
1007
1001
  return node_conditions
1008
1002
  condition_names: list[str] = self._safe_keys_of(before_conditions, after_conditions)
1009
1003
  conditions: list[NodeCondition] = list()
1010
- change_type = ChangeType.UNCHANGED
1011
1004
  for condition_name in condition_names:
1012
1005
  condition_scope, (before_condition, after_condition) = self._safe_access_in(
1013
1006
  scope, condition_name, before_conditions, after_conditions
@@ -1019,33 +1012,27 @@ class ChangeSetModel:
1019
1012
  after_condition=after_condition,
1020
1013
  )
1021
1014
  conditions.append(condition)
1022
- change_type = change_type.for_child(child_change_type=condition.change_type)
1023
- node_conditions = NodeConditions(
1024
- scope=scope, change_type=change_type, conditions=conditions
1025
- )
1015
+ node_conditions = NodeConditions(scope=scope, conditions=conditions)
1026
1016
  self._visited_scopes[scope] = node_conditions
1027
1017
  return node_conditions
1028
1018
 
1029
1019
  def _visit_output(
1030
1020
  self, scope: Scope, name: str, before_output: Maybe[dict], after_output: Maybe[dict]
1031
1021
  ) -> NodeOutput:
1032
- change_type = ChangeType.UNCHANGED
1033
1022
  scope_value, (before_value, after_value) = self._safe_access_in(
1034
1023
  scope, ValueKey, before_output, after_output
1035
1024
  )
1036
1025
  value = self._visit_value(scope_value, before_value, after_value)
1037
- change_type = change_type.for_child(value.change_type)
1038
1026
 
1039
- export: Optional[ChangeSetEntity] = None
1027
+ export: Maybe[ChangeSetEntity] = Nothing
1040
1028
  scope_export, (before_export, after_export) = self._safe_access_in(
1041
1029
  scope, ExportKey, before_output, after_output
1042
1030
  )
1043
1031
  if before_export or after_export:
1044
1032
  export = self._visit_value(scope_export, before_export, after_export)
1045
- change_type = change_type.for_child(export.change_type)
1046
1033
 
1047
1034
  # TODO: condition references should be resolved for the condition's change_type?
1048
- condition_reference: Optional[TerminalValue] = None
1035
+ condition_reference: Maybe[TerminalValue] = Nothing
1049
1036
  scope_condition, (before_condition, after_condition) = self._safe_access_in(
1050
1037
  scope, ConditionKey, before_output, after_output
1051
1038
  )
@@ -1053,11 +1040,9 @@ class ChangeSetModel:
1053
1040
  condition_reference = self._visit_terminal_value(
1054
1041
  scope_condition, before_condition, after_condition
1055
1042
  )
1056
- change_type = change_type.for_child(condition_reference.change_type)
1057
1043
 
1058
1044
  return NodeOutput(
1059
1045
  scope=scope,
1060
- change_type=change_type,
1061
1046
  name=name,
1062
1047
  value=value,
1063
1048
  export=export,
@@ -1067,7 +1052,6 @@ class ChangeSetModel:
1067
1052
  def _visit_outputs(
1068
1053
  self, scope: Scope, before_outputs: Maybe[dict], after_outputs: Maybe[dict]
1069
1054
  ) -> NodeOutputs:
1070
- change_type = ChangeType.UNCHANGED
1071
1055
  outputs: list[NodeOutput] = list()
1072
1056
  output_names: list[str] = self._safe_keys_of(before_outputs, after_outputs)
1073
1057
  for output_name in output_names:
@@ -1081,8 +1065,7 @@ class ChangeSetModel:
1081
1065
  after_output=after_output,
1082
1066
  )
1083
1067
  outputs.append(output)
1084
- change_type = change_type.for_child(output.change_type)
1085
- return NodeOutputs(scope=scope, change_type=change_type, outputs=outputs)
1068
+ return NodeOutputs(scope=scope, outputs=outputs)
1086
1069
 
1087
1070
  def _model(self, before_template: Maybe[dict], after_template: Maybe[dict]) -> NodeTemplate:
1088
1071
  root_scope = Scope()
@@ -1132,7 +1115,6 @@ class ChangeSetModel:
1132
1115
  # TODO: compute the change_type of the template properly.
1133
1116
  return NodeTemplate(
1134
1117
  scope=root_scope,
1135
- change_type=resources.change_type,
1136
1118
  mappings=mappings,
1137
1119
  parameters=parameters,
1138
1120
  conditions=conditions,
@@ -1140,7 +1122,7 @@ class ChangeSetModel:
1140
1122
  outputs=outputs,
1141
1123
  )
1142
1124
 
1143
- def _retrieve_condition_if_exists(self, condition_name: str) -> Optional[NodeCondition]:
1125
+ def _retrieve_condition_if_exists(self, condition_name: str) -> Maybe[NodeCondition]:
1144
1126
  conditions_scope, (before_conditions, after_conditions) = self._safe_access_in(
1145
1127
  Scope(), ConditionsKey, self._before_template, self._after_template
1146
1128
  )
@@ -1157,34 +1139,30 @@ class ChangeSetModel:
1157
1139
  after_condition=after_condition,
1158
1140
  )
1159
1141
  return node_condition
1160
- return None
1142
+ return Nothing
1161
1143
 
1162
- def _retrieve_parameter_if_exists(self, parameter_name: str) -> Optional[NodeParameter]:
1144
+ def _retrieve_parameter_if_exists(self, parameter_name: str) -> Maybe[NodeParameter]:
1163
1145
  parameters_scope, (before_parameters, after_parameters) = self._safe_access_in(
1164
1146
  Scope(), ParametersKey, self._before_template, self._after_template
1165
1147
  )
1166
- before_parameters = before_parameters or dict()
1167
- after_parameters = after_parameters or dict()
1168
1148
  if parameter_name in before_parameters or parameter_name in after_parameters:
1169
1149
  parameter_scope, (before_parameter, after_parameter) = self._safe_access_in(
1170
1150
  parameters_scope, parameter_name, before_parameters, after_parameters
1171
1151
  )
1172
1152
  node_parameter = self._visit_parameter(
1173
- parameters_scope,
1153
+ parameter_scope,
1174
1154
  parameter_name,
1175
1155
  before_parameter=before_parameter,
1176
1156
  after_parameter=after_parameter,
1177
1157
  )
1178
1158
  return node_parameter
1179
- return None
1159
+ return Nothing
1180
1160
 
1181
1161
  def _retrieve_mapping(self, mapping_name) -> NodeMapping:
1182
1162
  # TODO: add caching mechanism, and raise appropriate error if missing.
1183
1163
  scope_mappings, (before_mappings, after_mappings) = self._safe_access_in(
1184
1164
  Scope(), MappingsKey, self._before_template, self._after_template
1185
1165
  )
1186
- before_mappings = before_mappings or dict()
1187
- after_mappings = after_mappings or dict()
1188
1166
  if mapping_name in before_mappings or mapping_name in after_mappings:
1189
1167
  scope_mapping, (before_mapping, after_mapping) = self._safe_access_in(
1190
1168
  scope_mappings, mapping_name, before_mappings, after_mappings
@@ -1241,15 +1219,6 @@ class ChangeSetModel:
1241
1219
  keys = sorted(key_set)
1242
1220
  return keys
1243
1221
 
1244
- @staticmethod
1245
- def _change_type_for_parent_of(change_types: list[ChangeType]) -> ChangeType:
1246
- parent_change_type = ChangeType.UNCHANGED
1247
- for child_change_type in change_types:
1248
- parent_change_type = parent_change_type.for_child(child_change_type)
1249
- if parent_change_type == ChangeType.MODIFIED:
1250
- break
1251
- return parent_change_type
1252
-
1253
1222
  @staticmethod
1254
1223
  def _name_if_intrinsic_function(value: Maybe[Any]) -> Optional[str]:
1255
1224
  if isinstance(value, dict):
@@ -1278,11 +1247,3 @@ class ChangeSetModel:
1278
1247
  @staticmethod
1279
1248
  def _is_array(value: Any) -> bool:
1280
1249
  return isinstance(value, list)
1281
-
1282
- @staticmethod
1283
- def _is_created(before: Maybe[Any], after: Maybe[Any]) -> bool:
1284
- return isinstance(before, NothingType) and not isinstance(after, NothingType)
1285
-
1286
- @staticmethod
1287
- def _is_removed(before: Maybe[Any], after: Maybe[Any]) -> bool:
1288
- return not isinstance(before, NothingType) and isinstance(after, NothingType)