localstack-core 4.4.1.dev61__py3-none-any.whl → 4.4.1.dev63__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 (19) 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 +102 -144
  4. localstack/services/cloudformation/engine/v2/change_set_model_describer.py +9 -7
  5. localstack/services/cloudformation/engine/v2/change_set_model_executor.py +7 -6
  6. localstack/services/cloudformation/engine/v2/change_set_model_preproc.py +118 -118
  7. localstack/services/s3/provider.py +3 -2
  8. localstack/version.py +2 -2
  9. {localstack_core-4.4.1.dev61.dist-info → localstack_core-4.4.1.dev63.dist-info}/METADATA +3 -3
  10. {localstack_core-4.4.1.dev61.dist-info → localstack_core-4.4.1.dev63.dist-info}/RECORD +18 -18
  11. localstack_core-4.4.1.dev63.dist-info/plux.json +1 -0
  12. localstack_core-4.4.1.dev61.dist-info/plux.json +0 -1
  13. {localstack_core-4.4.1.dev61.data → localstack_core-4.4.1.dev63.data}/scripts/localstack +0 -0
  14. {localstack_core-4.4.1.dev61.data → localstack_core-4.4.1.dev63.data}/scripts/localstack-supervisor +0 -0
  15. {localstack_core-4.4.1.dev61.data → localstack_core-4.4.1.dev63.data}/scripts/localstack.bat +0 -0
  16. {localstack_core-4.4.1.dev61.dist-info → localstack_core-4.4.1.dev63.dist-info}/WHEEL +0 -0
  17. {localstack_core-4.4.1.dev61.dist-info → localstack_core-4.4.1.dev63.dist-info}/entry_points.txt +0 -0
  18. {localstack_core-4.4.1.dev61.dist-info → localstack_core-4.4.1.dev63.dist-info}/licenses/LICENSE.txt +0 -0
  19. {localstack_core-4.4.1.dev61.dist-info → localstack_core-4.4.1.dev63.dist-info}/top_level.txt +0 -0
@@ -8,7 +8,9 @@ from localstack.services.cloudformation.engine.v2.change_set_model import (
8
8
  NodeIntrinsicFunction,
9
9
  NodeProperty,
10
10
  NodeResource,
11
+ Nothing,
11
12
  PropertiesKey,
13
+ is_nothing,
12
14
  )
13
15
  from localstack.services.cloudformation.engine.v2.change_set_model_preproc import (
14
16
  ChangeSetModelPreproc,
@@ -53,8 +55,8 @@ class ChangeSetModelDescriber(ChangeSetModelPreproc):
53
55
  if isinstance(after_argument, str):
54
56
  after_argument = after_argument.split(".")
55
57
 
56
- before = None
57
- if before_argument:
58
+ before = Nothing
59
+ if not is_nothing(before_argument):
58
60
  before_logical_name_of_resource = before_argument[0]
59
61
  before_attribute_name = before_argument[1]
60
62
  before_node_resource = self._get_node_resource_for(
@@ -72,8 +74,8 @@ class ChangeSetModelDescriber(ChangeSetModelPreproc):
72
74
  property_name=before_attribute_name,
73
75
  )
74
76
 
75
- after = None
76
- if after_argument:
77
+ after = Nothing
78
+ if not is_nothing(after_argument):
77
79
  after_logical_name_of_resource = after_argument[0]
78
80
  after_attribute_name = after_argument[1]
79
81
  after_node_resource = self._get_node_resource_for(
@@ -154,7 +156,7 @@ class ChangeSetModelDescriber(ChangeSetModelPreproc):
154
156
  if before == after:
155
157
  # unchanged: nothing to do.
156
158
  return
157
- if before is not None and after is not None:
159
+ if not is_nothing(before) and not is_nothing(after):
158
160
  # Case: change on same type.
159
161
  if before.resource_type == after.resource_type:
160
162
  # Register a Modified if changed.
@@ -184,7 +186,7 @@ class ChangeSetModelDescriber(ChangeSetModelPreproc):
184
186
  before_properties=None,
185
187
  after_properties=after.properties,
186
188
  )
187
- elif before is not None:
189
+ elif not is_nothing(before):
188
190
  # Case: removal
189
191
  self._register_resource_change(
190
192
  logical_id=name,
@@ -193,7 +195,7 @@ class ChangeSetModelDescriber(ChangeSetModelPreproc):
193
195
  before_properties=before.properties,
194
196
  after_properties=None,
195
197
  )
196
- elif after is not None:
198
+ elif not is_nothing(after):
197
199
  # Case: addition
198
200
  self._register_resource_change(
199
201
  logical_id=name,
@@ -11,6 +11,7 @@ from localstack.services.cloudformation.engine.v2.change_set_model import (
11
11
  NodeOutput,
12
12
  NodeParameter,
13
13
  NodeResource,
14
+ is_nothing,
14
15
  )
15
16
  from localstack.services.cloudformation.engine.v2.change_set_model_preproc import (
16
17
  ChangeSetModelPreproc,
@@ -113,13 +114,13 @@ class ChangeSetModelExecutor(ChangeSetModelPreproc):
113
114
  # There are no updates for this resource; iff the resource was previously
114
115
  # deployed, then the resolved details are copied in the current state for
115
116
  # references or other downstream operations.
116
- if before is not None:
117
+ if not is_nothing(before):
117
118
  before_logical_id = delta.before.logical_id
118
119
  before_resource = self._before_resolved_resources.get(before_logical_id, dict())
119
120
  self.resources[before_logical_id] = before_resource
120
121
 
121
122
  # Update the latest version of this resource for downstream references.
122
- if after is not None:
123
+ if not is_nothing(after):
123
124
  after_logical_id = after.logical_id
124
125
  after_physical_id: str = self._after_resource_physical_id(
125
126
  resource_logical_id=after_logical_id
@@ -132,7 +133,7 @@ class ChangeSetModelExecutor(ChangeSetModelPreproc):
132
133
  ) -> PreprocEntityDelta[PreprocOutput, PreprocOutput]:
133
134
  delta = super().visit_node_output(node_output=node_output)
134
135
  after = delta.after
135
- if after is None or (isinstance(after, PreprocOutput) and after.condition is False):
136
+ if is_nothing(after) or (isinstance(after, PreprocOutput) and after.condition is False):
136
137
  return delta
137
138
  self.outputs[delta.after.name] = delta.after.value
138
139
  return delta
@@ -142,7 +143,7 @@ class ChangeSetModelExecutor(ChangeSetModelPreproc):
142
143
  ) -> None:
143
144
  # Changes are to be made about this resource.
144
145
  # TODO: this logic is a POC and should be revised.
145
- if before is not None and after is not None:
146
+ if not is_nothing(before) and not is_nothing(after):
146
147
  # Case: change on same type.
147
148
  if before.resource_type == after.resource_type:
148
149
  # Register a Modified if changed.
@@ -177,7 +178,7 @@ class ChangeSetModelExecutor(ChangeSetModelPreproc):
177
178
  before_properties=None,
178
179
  after_properties=after.properties,
179
180
  )
180
- elif before is not None:
181
+ elif not is_nothing(before):
181
182
  # Case: removal
182
183
  # XXX hacky, stick the previous resources' properties into the payload
183
184
  # XXX hacky, stick the previous resources' properties into the payload
@@ -190,7 +191,7 @@ class ChangeSetModelExecutor(ChangeSetModelPreproc):
190
191
  before_properties=before_properties,
191
192
  after_properties=None,
192
193
  )
193
- elif after is not None:
194
+ elif not is_nothing(after):
194
195
  # Case: addition
195
196
  self._execute_resource_action(
196
197
  action=ChangeAction.Add,
@@ -11,6 +11,7 @@ from localstack.services.cloudformation.engine.transformers import (
11
11
  from localstack.services.cloudformation.engine.v2.change_set_model import (
12
12
  ChangeSetEntity,
13
13
  ChangeType,
14
+ Maybe,
14
15
  NodeArray,
15
16
  NodeCondition,
16
17
  NodeDependsOn,
@@ -25,12 +26,14 @@ from localstack.services.cloudformation.engine.v2.change_set_model import (
25
26
  NodeProperty,
26
27
  NodeResource,
27
28
  NodeTemplate,
29
+ Nothing,
28
30
  Scope,
29
31
  TerminalValue,
30
32
  TerminalValueCreated,
31
33
  TerminalValueModified,
32
34
  TerminalValueRemoved,
33
35
  TerminalValueUnchanged,
36
+ is_nothing,
34
37
  )
35
38
  from localstack.services.cloudformation.engine.v2.change_set_model_visitor import (
36
39
  ChangeSetModelVisitor,
@@ -58,10 +61,10 @@ TAfter = TypeVar("TAfter")
58
61
 
59
62
 
60
63
  class PreprocEntityDelta(Generic[TBefore, TAfter]):
61
- before: Optional[TBefore]
62
- after: Optional[TAfter]
64
+ before: Maybe[TBefore]
65
+ after: Maybe[TAfter]
63
66
 
64
- def __init__(self, before: Optional[TBefore] = None, after: Optional[TAfter] = None):
67
+ def __init__(self, before: Maybe[TBefore] = Nothing, after: Maybe[TAfter] = Nothing):
65
68
  self.before = before
66
69
  self.after = after
67
70
 
@@ -200,7 +203,6 @@ class ChangeSetModelPreproc(ChangeSetModelVisitor):
200
203
  _ = self._get_node_resource_for(
201
204
  resource_name=resource_logical_id, node_template=self._node_template
202
205
  )
203
-
204
206
  resolved_resource = resolved_resources.get(resource_logical_id)
205
207
  if resolved_resource is None:
206
208
  raise RuntimeError(
@@ -237,26 +239,25 @@ class ChangeSetModelPreproc(ChangeSetModelVisitor):
237
239
  if mapping.name == map_name:
238
240
  self.visit(mapping)
239
241
  return mapping
240
- # TODO
241
- raise RuntimeError()
242
+ raise RuntimeError(f"Undefined '{map_name}' mapping")
242
243
 
243
- def _get_node_parameter_if_exists(self, parameter_name: str) -> Optional[NodeParameter]:
244
+ def _get_node_parameter_if_exists(self, parameter_name: str) -> Maybe[NodeParameter]:
244
245
  parameters: list[NodeParameter] = self._node_template.parameters.parameters
245
246
  # TODO: another scenarios suggesting property lookups might be preferable.
246
247
  for parameter in parameters:
247
248
  if parameter.name == parameter_name:
248
249
  self.visit(parameter)
249
250
  return parameter
250
- return None
251
+ return Nothing
251
252
 
252
- def _get_node_condition_if_exists(self, condition_name: str) -> Optional[NodeCondition]:
253
+ def _get_node_condition_if_exists(self, condition_name: str) -> Maybe[NodeCondition]:
253
254
  conditions: list[NodeCondition] = self._node_template.conditions.conditions
254
255
  # TODO: another scenarios suggesting property lookups might be preferable.
255
256
  for condition in conditions:
256
257
  if condition.name == condition_name:
257
258
  self.visit(condition)
258
259
  return condition
259
- return None
260
+ return Nothing
260
261
 
261
262
  def _resolve_condition(self, logical_id: str) -> PreprocEntityDelta:
262
263
  node_condition = self._get_node_condition_if_exists(condition_name=logical_id)
@@ -280,14 +281,9 @@ class ChangeSetModelPreproc(ChangeSetModelVisitor):
280
281
  case "AWS::URLSuffix":
281
282
  return _AWS_URL_SUFFIX
282
283
  case "AWS::NoValue":
283
- # TODO: add support for NoValue, None cannot be used to communicate a Null value in preproc classes.
284
- raise NotImplementedError("The use of AWS:NoValue is currently unsupported")
285
- case "AWS::NotificationARNs":
286
- raise NotImplementedError(
287
- "The use of AWS::NotificationARNs is currently unsupported"
288
- )
284
+ return None
289
285
  case _:
290
- raise RuntimeError(f"Unknown pseudo parameter value '{pseudo_parameter_name}'")
286
+ raise RuntimeError(f"The use of '{pseudo_parameter_name}' is currently unsupported")
291
287
 
292
288
  def _resolve_reference(self, logical_id: str) -> PreprocEntityDelta:
293
289
  if logical_id in _PSEUDO_PARAMETERS:
@@ -323,11 +319,12 @@ class ChangeSetModelPreproc(ChangeSetModelVisitor):
323
319
  return mapping_value_delta
324
320
 
325
321
  def visit(self, change_set_entity: ChangeSetEntity) -> PreprocEntityDelta:
326
- delta = self._processed.get(change_set_entity.scope)
327
- if delta is not None:
322
+ scope = change_set_entity.scope
323
+ if scope in self._processed:
324
+ delta = self._processed[scope]
328
325
  return delta
329
326
  delta = super().visit(change_set_entity=change_set_entity)
330
- self._processed[change_set_entity.scope] = delta
327
+ self._processed[scope] = delta
331
328
  return delta
332
329
 
333
330
  def visit_terminal_value_modified(
@@ -362,21 +359,17 @@ class ChangeSetModelPreproc(ChangeSetModelVisitor):
362
359
  return PreprocEntityDelta(before=before_delta.before, after=after_delta.after)
363
360
 
364
361
  def visit_node_object(self, node_object: NodeObject) -> PreprocEntityDelta:
365
- before = dict()
366
- after = dict()
362
+ node_change_type = node_object.change_type
363
+ before = dict() if node_change_type != ChangeType.CREATED else Nothing
364
+ after = dict() if node_change_type != ChangeType.REMOVED else Nothing
367
365
  for name, change_set_entity in node_object.bindings.items():
368
366
  delta: PreprocEntityDelta = self.visit(change_set_entity=change_set_entity)
369
- match change_set_entity.change_type:
370
- case ChangeType.MODIFIED:
371
- before[name] = delta.before
372
- after[name] = delta.after
373
- case ChangeType.CREATED:
374
- after[name] = delta.after
375
- case ChangeType.REMOVED:
376
- before[name] = delta.before
377
- case ChangeType.UNCHANGED:
378
- before[name] = delta.before
379
- after[name] = delta.before
367
+ delta_before = delta.before
368
+ delta_after = delta.after
369
+ if not is_nothing(before) and not is_nothing(delta_before) and delta_before is not None:
370
+ before[name] = delta_before
371
+ if not is_nothing(after) and not is_nothing(delta_after) and delta_after is not None:
372
+ after[name] = delta_after
380
373
  return PreprocEntityDelta(before=before, after=after)
381
374
 
382
375
  def visit_node_intrinsic_function_fn_get_att(
@@ -384,14 +377,14 @@ class ChangeSetModelPreproc(ChangeSetModelVisitor):
384
377
  ) -> PreprocEntityDelta:
385
378
  # TODO: validate the return value according to the spec.
386
379
  arguments_delta = self.visit(node_intrinsic_function.arguments)
387
- before_argument: Optional[list[str]] = arguments_delta.before
380
+ before_argument: Maybe[list[str]] = arguments_delta.before
388
381
  if isinstance(before_argument, str):
389
382
  before_argument = before_argument.split(".")
390
- after_argument: Optional[list[str]] = arguments_delta.after
383
+ after_argument: Maybe[list[str]] = arguments_delta.after
391
384
  if isinstance(after_argument, str):
392
385
  after_argument = after_argument.split(".")
393
386
 
394
- before = None
387
+ before = Nothing
395
388
  if before_argument:
396
389
  before_logical_name_of_resource = before_argument[0]
397
390
  before_attribute_name = before_argument[1]
@@ -414,7 +407,7 @@ class ChangeSetModelPreproc(ChangeSetModelVisitor):
414
407
  property_name=before_attribute_name,
415
408
  )
416
409
 
417
- after = None
410
+ after = Nothing
418
411
  if after_argument:
419
412
  after_logical_name_of_resource = after_argument[0]
420
413
  after_attribute_name = after_argument[1]
@@ -444,10 +437,10 @@ class ChangeSetModelPreproc(ChangeSetModelVisitor):
444
437
  arguments_delta = self.visit(node_intrinsic_function.arguments)
445
438
  before_values = arguments_delta.before
446
439
  after_values = arguments_delta.after
447
- before = None
440
+ before = Nothing
448
441
  if before_values:
449
442
  before = before_values[0] == before_values[1]
450
- after = None
443
+ after = Nothing
451
444
  if after_values:
452
445
  after = after_values[0] == after_values[1]
453
446
  return PreprocEntityDelta(before=before, after=after)
@@ -456,6 +449,8 @@ class ChangeSetModelPreproc(ChangeSetModelVisitor):
456
449
  self, node_intrinsic_function: NodeIntrinsicFunction
457
450
  ) -> PreprocEntityDelta:
458
451
  arguments_delta = self.visit(node_intrinsic_function.arguments)
452
+ arguments_before = arguments_delta.before
453
+ arguments_after = arguments_delta.after
459
454
 
460
455
  def _compute_delta_for_if_statement(args: list[Any]) -> PreprocEntityDelta:
461
456
  condition_name = args[0]
@@ -466,13 +461,13 @@ class ChangeSetModelPreproc(ChangeSetModelVisitor):
466
461
  )
467
462
 
468
463
  # TODO: add support for this being created or removed.
469
- before = None
470
- if arguments_delta.before:
471
- before_outcome_delta = _compute_delta_for_if_statement(arguments_delta.before)
464
+ before = Nothing
465
+ if not is_nothing(arguments_before):
466
+ before_outcome_delta = _compute_delta_for_if_statement(arguments_before)
472
467
  before = before_outcome_delta.before
473
- after = None
474
- if arguments_delta.after:
475
- after_outcome_delta = _compute_delta_for_if_statement(arguments_delta.after)
468
+ after = Nothing
469
+ if not is_nothing(arguments_after):
470
+ after_outcome_delta = _compute_delta_for_if_statement(arguments_after)
476
471
  after = after_outcome_delta.after
477
472
  return PreprocEntityDelta(before=before, after=after)
478
473
 
@@ -482,17 +477,14 @@ class ChangeSetModelPreproc(ChangeSetModelVisitor):
482
477
  arguments_delta = self.visit(node_intrinsic_function.arguments)
483
478
  before_condition = arguments_delta.before
484
479
  after_condition = arguments_delta.after
485
- if before_condition:
480
+ before = Nothing
481
+ if not is_nothing(before_condition):
486
482
  before_condition_outcome = before_condition[0]
487
483
  before = not before_condition_outcome
488
- else:
489
- before = None
490
-
491
- if after_condition:
484
+ after = Nothing
485
+ if not is_nothing(after_condition):
492
486
  after_condition_outcome = after_condition[0]
493
487
  after = not after_condition_outcome
494
- else:
495
- after = None
496
488
  # Implicit change type computation.
497
489
  return PreprocEntityDelta(before=before, after=after)
498
490
 
@@ -560,11 +552,11 @@ class ChangeSetModelPreproc(ChangeSetModelVisitor):
560
552
  # TODO: add tests to review the behaviour of CFN with changes to transformation
561
553
  # function code and no changes to the template.
562
554
 
563
- before = None
564
- if arguments_before:
555
+ before = Nothing
556
+ if not is_nothing(arguments_before):
565
557
  before = self._compute_fn_transform(args=arguments_before)
566
- after = None
567
- if arguments_after:
558
+ after = Nothing
559
+ if not is_nothing(arguments_after):
568
560
  after = self._compute_fn_transform(args=arguments_after)
569
561
  return PreprocEntityDelta(before=before, after=after)
570
562
 
@@ -606,9 +598,9 @@ class ChangeSetModelPreproc(ChangeSetModelVisitor):
606
598
  template_variable_value = sub_parameters[template_variable_name]
607
599
  else:
608
600
  try:
609
- reference_delta = self._resolve_reference(logical_id=template_variable_name)
601
+ resource_delta = self._resolve_reference(logical_id=template_variable_name)
610
602
  template_variable_value = (
611
- reference_delta.before if select_before else reference_delta.after
603
+ resource_delta.before if select_before else resource_delta.after
612
604
  )
613
605
  if isinstance(template_variable_value, PreprocResource):
614
606
  template_variable_value = template_variable_value.logical_id
@@ -621,19 +613,11 @@ class ChangeSetModelPreproc(ChangeSetModelVisitor):
621
613
  )
622
614
  return sub_string
623
615
 
624
- before = None
625
- if (
626
- isinstance(arguments_before, str)
627
- or isinstance(arguments_before, list)
628
- and len(arguments_before) == 2
629
- ):
616
+ before = Nothing
617
+ if not is_nothing(arguments_before):
630
618
  before = _compute_sub(args=arguments_before, select_before=True)
631
- after = None
632
- if (
633
- isinstance(arguments_after, str)
634
- or isinstance(arguments_after, list)
635
- and len(arguments_after) == 2
636
- ):
619
+ after = Nothing
620
+ if not is_nothing(arguments_after):
637
621
  after = _compute_sub(args=arguments_after)
638
622
  return PreprocEntityDelta(before=before, after=after)
639
623
 
@@ -654,10 +638,10 @@ class ChangeSetModelPreproc(ChangeSetModelVisitor):
654
638
  join_result = delimiter.join(map(str, values))
655
639
  return join_result
656
640
 
657
- before = None
641
+ before = Nothing
658
642
  if isinstance(arguments_before, list) and len(arguments_before) == 2:
659
643
  before = _compute_join(arguments_before)
660
- after = None
644
+ after = Nothing
661
645
  if isinstance(arguments_after, list) and len(arguments_after) == 2:
662
646
  after = _compute_join(arguments_after)
663
647
  return PreprocEntityDelta(before=before, after=after)
@@ -669,16 +653,14 @@ class ChangeSetModelPreproc(ChangeSetModelVisitor):
669
653
  arguments_delta = self.visit(node_intrinsic_function.arguments)
670
654
  before_arguments = arguments_delta.before
671
655
  after_arguments = arguments_delta.after
656
+ before = Nothing
672
657
  if before_arguments:
673
658
  before_value_delta = self._resolve_mapping(*before_arguments)
674
659
  before = before_value_delta.before
675
- else:
676
- before = None
660
+ after = Nothing
677
661
  if after_arguments:
678
662
  after_value_delta = self._resolve_mapping(*after_arguments)
679
663
  after = after_value_delta.after
680
- else:
681
- after = None
682
664
  return PreprocEntityDelta(before=before, after=after)
683
665
 
684
666
  def visit_node_mapping(self, node_mapping: NodeMapping) -> PreprocEntityDelta:
@@ -733,15 +715,15 @@ class ChangeSetModelPreproc(ChangeSetModelVisitor):
733
715
  after_logical_id = arguments_delta.after
734
716
 
735
717
  # TODO: extend this to support references to other types.
736
- before = None
737
- if before_logical_id is not None:
718
+ before = Nothing
719
+ if not is_nothing(before_logical_id):
738
720
  before_delta = self._resolve_reference(logical_id=before_logical_id)
739
721
  before = before_delta.before
740
722
  if isinstance(before, PreprocResource):
741
723
  before = before.physical_resource_id
742
724
 
743
- after = None
744
- if after_logical_id is not None:
725
+ after = Nothing
726
+ if not is_nothing(after_logical_id):
745
727
  after_delta = self._resolve_reference(logical_id=after_logical_id)
746
728
  after = after_delta.after
747
729
  if isinstance(after, PreprocResource):
@@ -750,14 +732,17 @@ class ChangeSetModelPreproc(ChangeSetModelVisitor):
750
732
  return PreprocEntityDelta(before=before, after=after)
751
733
 
752
734
  def visit_node_array(self, node_array: NodeArray) -> PreprocEntityDelta:
753
- before = list()
754
- after = list()
735
+ node_change_type = node_array.change_type
736
+ before = list() if node_change_type != ChangeType.CREATED else Nothing
737
+ after = list() if node_change_type != ChangeType.REMOVED else Nothing
755
738
  for change_set_entity in node_array.array:
756
739
  delta: PreprocEntityDelta = self.visit(change_set_entity=change_set_entity)
757
- if delta.before is not None:
758
- before.append(delta.before)
759
- if delta.after is not None:
760
- after.append(delta.after)
740
+ delta_before = delta.before
741
+ delta_after = delta.after
742
+ if not is_nothing(before) and not is_nothing(delta_before):
743
+ before.append(delta_before)
744
+ if not is_nothing(after) and not is_nothing(delta_after):
745
+ after.append(delta_after)
761
746
  return PreprocEntityDelta(before=before, after=after)
762
747
 
763
748
  def visit_node_property(self, node_property: NodeProperty) -> PreprocEntityDelta:
@@ -766,29 +751,44 @@ class ChangeSetModelPreproc(ChangeSetModelVisitor):
766
751
  def visit_node_properties(
767
752
  self, node_properties: NodeProperties
768
753
  ) -> PreprocEntityDelta[PreprocProperties, PreprocProperties]:
769
- before_bindings: dict[str, Any] = dict()
770
- after_bindings: dict[str, Any] = dict()
754
+ node_change_type = node_properties.change_type
755
+ before_bindings = dict() if node_change_type != ChangeType.CREATED else Nothing
756
+ after_bindings = dict() if node_change_type != ChangeType.REMOVED else Nothing
771
757
  for node_property in node_properties.properties:
772
- delta = self.visit(node_property)
773
758
  property_name = node_property.name
774
- if node_property.change_type != ChangeType.CREATED:
775
- before_bindings[property_name] = delta.before
776
- if node_property.change_type != ChangeType.REMOVED:
777
- after_bindings[property_name] = delta.after
778
- before = PreprocProperties(properties=before_bindings)
779
- after = PreprocProperties(properties=after_bindings)
759
+ delta = self.visit(node_property)
760
+ delta_before = delta.before
761
+ delta_after = delta.after
762
+ if (
763
+ not is_nothing(before_bindings)
764
+ and not is_nothing(delta_before)
765
+ and delta_before is not None
766
+ ):
767
+ before_bindings[property_name] = delta_before
768
+ if (
769
+ not is_nothing(after_bindings)
770
+ and not is_nothing(delta_after)
771
+ and delta_after is not None
772
+ ):
773
+ after_bindings[property_name] = delta_after
774
+ before = Nothing
775
+ if not is_nothing(before_bindings):
776
+ before = PreprocProperties(properties=before_bindings)
777
+ after = Nothing
778
+ if not is_nothing(after_bindings):
779
+ after = PreprocProperties(properties=after_bindings)
780
780
  return PreprocEntityDelta(before=before, after=after)
781
781
 
782
782
  def _resolve_resource_condition_reference(self, reference: TerminalValue) -> PreprocEntityDelta:
783
783
  reference_delta = self.visit(reference)
784
784
  before_reference = reference_delta.before
785
- before = None
786
- if before_reference is not None:
785
+ before = Nothing
786
+ if isinstance(before_reference, str):
787
787
  before_delta = self._resolve_condition(logical_id=before_reference)
788
788
  before = before_delta.before
789
- after = None
789
+ after = Nothing
790
790
  after_reference = reference_delta.after
791
- if after_reference is not None:
791
+ if isinstance(after_reference, str):
792
792
  after_delta = self._resolve_condition(logical_id=after_reference)
793
793
  after = after_delta.after
794
794
  return PreprocEntityDelta(before=before, after=after)
@@ -797,19 +797,19 @@ class ChangeSetModelPreproc(ChangeSetModelVisitor):
797
797
  self, node_resource: NodeResource
798
798
  ) -> PreprocEntityDelta[PreprocResource, PreprocResource]:
799
799
  change_type = node_resource.change_type
800
- condition_before = None
801
- condition_after = None
802
- if node_resource.condition_reference is not None:
800
+ condition_before = Nothing
801
+ condition_after = Nothing
802
+ if not is_nothing(node_resource.condition_reference):
803
803
  condition_delta = self._resolve_resource_condition_reference(
804
804
  node_resource.condition_reference
805
805
  )
806
806
  condition_before = condition_delta.before
807
807
  condition_after = condition_delta.after
808
808
 
809
- depends_on_before = None
810
- depends_on_after = None
811
- if node_resource.depends_on is not None:
812
- depends_on_delta = self.visit_node_depends_on(node_resource.depends_on)
809
+ depends_on_before = Nothing
810
+ depends_on_after = Nothing
811
+ if not is_nothing(node_resource.depends_on):
812
+ depends_on_delta = self.visit(node_resource.depends_on)
813
813
  depends_on_before = depends_on_delta.before
814
814
  depends_on_after = depends_on_delta.after
815
815
 
@@ -818,9 +818,9 @@ class ChangeSetModelPreproc(ChangeSetModelVisitor):
818
818
  node_resource.properties
819
819
  )
820
820
 
821
- before = None
822
- after = None
823
- if change_type != ChangeType.CREATED and condition_before is None or condition_before:
821
+ before = Nothing
822
+ after = Nothing
823
+ if change_type != ChangeType.CREATED and is_nothing(condition_before) or condition_before:
824
824
  logical_resource_id = node_resource.name
825
825
  before_physical_resource_id = self._before_resource_physical_id(
826
826
  resource_logical_id=logical_resource_id
@@ -833,7 +833,7 @@ class ChangeSetModelPreproc(ChangeSetModelVisitor):
833
833
  properties=properties_delta.before,
834
834
  depends_on=depends_on_before,
835
835
  )
836
- if change_type != ChangeType.REMOVED and condition_after is None or condition_after:
836
+ if change_type != ChangeType.REMOVED and is_nothing(condition_after) or condition_after:
837
837
  logical_resource_id = node_resource.name
838
838
  try:
839
839
  after_physical_resource_id = self._after_resource_physical_id(
@@ -857,8 +857,8 @@ class ChangeSetModelPreproc(ChangeSetModelVisitor):
857
857
  change_type = node_output.change_type
858
858
  value_delta = self.visit(node_output.value)
859
859
 
860
- condition_delta = None
861
- if node_output.condition_reference is not None:
860
+ condition_delta = Nothing
861
+ if not is_nothing(node_output.condition_reference):
862
862
  condition_delta = self._resolve_resource_condition_reference(
863
863
  node_output.condition_reference
864
864
  )
@@ -869,11 +869,11 @@ class ChangeSetModelPreproc(ChangeSetModelVisitor):
869
869
  elif condition_before and not condition_after:
870
870
  change_type = ChangeType.REMOVED
871
871
 
872
- export_delta = None
873
- if node_output.export is not None:
872
+ export_delta = Nothing
873
+ if not is_nothing(node_output.export):
874
874
  export_delta = self.visit(node_output.export)
875
875
 
876
- before: Optional[PreprocOutput] = None
876
+ before: Maybe[PreprocOutput] = Nothing
877
877
  if change_type != ChangeType.CREATED:
878
878
  before = PreprocOutput(
879
879
  name=node_output.name,
@@ -881,7 +881,7 @@ class ChangeSetModelPreproc(ChangeSetModelVisitor):
881
881
  export=export_delta.before if export_delta else None,
882
882
  condition=condition_delta.before if condition_delta else None,
883
883
  )
884
- after: Optional[PreprocOutput] = None
884
+ after: Maybe[PreprocOutput] = Nothing
885
885
  if change_type != ChangeType.REMOVED:
886
886
  after = PreprocOutput(
887
887
  name=node_output.name,
@@ -900,8 +900,8 @@ class ChangeSetModelPreproc(ChangeSetModelVisitor):
900
900
  output_delta: PreprocEntityDelta[PreprocOutput, PreprocOutput] = self.visit(node_output)
901
901
  output_before = output_delta.before
902
902
  output_after = output_delta.after
903
- if output_before:
903
+ if not is_nothing(output_before):
904
904
  before.append(output_before)
905
- if output_after:
905
+ if not is_nothing(output_after):
906
906
  after.append(output_after)
907
907
  return PreprocEntityDelta(before=before, after=after)
@@ -3815,8 +3815,9 @@ class S3Provider(S3Api, ServiceLifecycleHook):
3815
3815
  context: RequestContext,
3816
3816
  bucket: BucketName,
3817
3817
  ownership_controls: OwnershipControls,
3818
- content_md5: ContentMD5 = None,
3819
- expected_bucket_owner: AccountId = None,
3818
+ content_md5: ContentMD5 | None = None,
3819
+ expected_bucket_owner: AccountId | None = None,
3820
+ checksum_algorithm: ChecksumAlgorithm | None = None,
3820
3821
  **kwargs,
3821
3822
  ) -> None:
3822
3823
  # TODO: this currently only mock the operation, but its actual effect is not emulated
localstack/version.py CHANGED
@@ -17,5 +17,5 @@ __version__: str
17
17
  __version_tuple__: VERSION_TUPLE
18
18
  version_tuple: VERSION_TUPLE
19
19
 
20
- __version__ = version = '4.4.1.dev61'
21
- __version_tuple__ = version_tuple = (4, 4, 1, 'dev61')
20
+ __version__ = version = '4.4.1.dev63'
21
+ __version_tuple__ = version_tuple = (4, 4, 1, 'dev63')
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: localstack-core
3
- Version: 4.4.1.dev61
3
+ Version: 4.4.1.dev63
4
4
  Summary: The core library and runtime of LocalStack
5
5
  Author-email: LocalStack Contributors <info@localstack.cloud>
6
6
  License-Expression: Apache-2.0
@@ -31,8 +31,8 @@ Requires-Dist: requests>=2.20.0
31
31
  Requires-Dist: semver>=2.10
32
32
  Requires-Dist: tailer>=0.4.1
33
33
  Provides-Extra: base-runtime
34
- Requires-Dist: boto3==1.38.23; extra == "base-runtime"
35
- Requires-Dist: botocore==1.38.23; extra == "base-runtime"
34
+ Requires-Dist: boto3==1.38.27; extra == "base-runtime"
35
+ Requires-Dist: botocore==1.38.27; extra == "base-runtime"
36
36
  Requires-Dist: awscrt!=0.27.1,>=0.13.14; extra == "base-runtime"
37
37
  Requires-Dist: cbor2>=5.5.0; extra == "base-runtime"
38
38
  Requires-Dist: dnspython>=1.16.0; extra == "base-runtime"