localstack-core 4.7.1.dev49__py3-none-any.whl → 4.10.1.dev12__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 (253) hide show
  1. localstack/aws/api/cloudformation/__init__.py +18 -4
  2. localstack/aws/api/cloudwatch/__init__.py +41 -1
  3. localstack/aws/api/config/__init__.py +4 -0
  4. localstack/aws/api/core.py +6 -2
  5. localstack/aws/api/dynamodb/__init__.py +30 -0
  6. localstack/aws/api/ec2/__init__.py +1522 -65
  7. localstack/aws/api/iam/__init__.py +7 -0
  8. localstack/aws/api/kinesis/__init__.py +19 -0
  9. localstack/aws/api/kms/__init__.py +6 -0
  10. localstack/aws/api/lambda_/__init__.py +13 -0
  11. localstack/aws/api/logs/__init__.py +15 -0
  12. localstack/aws/api/redshift/__init__.py +9 -3
  13. localstack/aws/api/route53/__init__.py +5 -0
  14. localstack/aws/api/s3/__init__.py +12 -0
  15. localstack/aws/api/s3control/__init__.py +54 -0
  16. localstack/aws/api/ssm/__init__.py +2 -0
  17. localstack/aws/api/transcribe/__init__.py +17 -0
  18. localstack/aws/client.py +7 -2
  19. localstack/aws/forwarder.py +52 -5
  20. localstack/aws/handlers/analytics.py +1 -1
  21. localstack/aws/handlers/internal_requests.py +6 -1
  22. localstack/aws/handlers/logging.py +12 -2
  23. localstack/aws/handlers/metric_handler.py +41 -1
  24. localstack/aws/handlers/service.py +40 -20
  25. localstack/aws/mocking.py +2 -2
  26. localstack/aws/patches.py +2 -2
  27. localstack/aws/protocol/parser.py +459 -32
  28. localstack/aws/protocol/serializer.py +689 -69
  29. localstack/aws/protocol/service_router.py +120 -20
  30. localstack/aws/protocol/validate.py +1 -1
  31. localstack/aws/scaffold.py +1 -1
  32. localstack/aws/skeleton.py +4 -2
  33. localstack/aws/spec-patches.json +58 -0
  34. localstack/aws/spec.py +37 -16
  35. localstack/cli/exceptions.py +1 -1
  36. localstack/cli/localstack.py +6 -6
  37. localstack/cli/lpm.py +3 -4
  38. localstack/cli/plugins.py +1 -1
  39. localstack/cli/profiles.py +1 -2
  40. localstack/config.py +25 -18
  41. localstack/constants.py +4 -29
  42. localstack/dev/kubernetes/__main__.py +130 -7
  43. localstack/dev/run/configurators.py +1 -4
  44. localstack/dev/run/paths.py +1 -1
  45. localstack/dns/plugins.py +5 -1
  46. localstack/dns/server.py +13 -4
  47. localstack/logging/format.py +3 -3
  48. localstack/packages/api.py +9 -8
  49. localstack/packages/core.py +2 -2
  50. localstack/packages/plugins.py +0 -8
  51. localstack/runtime/analytics.py +3 -0
  52. localstack/runtime/hooks.py +1 -1
  53. localstack/runtime/init.py +2 -2
  54. localstack/runtime/main.py +5 -5
  55. localstack/runtime/patches.py +2 -2
  56. localstack/services/apigateway/helpers.py +1 -4
  57. localstack/services/apigateway/legacy/helpers.py +7 -8
  58. localstack/services/apigateway/legacy/integration.py +4 -3
  59. localstack/services/apigateway/legacy/invocations.py +6 -5
  60. localstack/services/apigateway/legacy/provider.py +148 -68
  61. localstack/services/apigateway/legacy/templates.py +1 -1
  62. localstack/services/apigateway/next_gen/execute_api/handlers/method_request.py +7 -2
  63. localstack/services/apigateway/next_gen/execute_api/handlers/resource_router.py +1 -2
  64. localstack/services/apigateway/next_gen/execute_api/integrations/aws.py +3 -0
  65. localstack/services/apigateway/next_gen/execute_api/integrations/http.py +3 -3
  66. localstack/services/apigateway/next_gen/execute_api/template_mapping.py +2 -2
  67. localstack/services/apigateway/next_gen/execute_api/test_invoke.py +114 -9
  68. localstack/services/apigateway/next_gen/provider.py +5 -0
  69. localstack/services/apigateway/resource_providers/aws_apigateway_resource.py +1 -1
  70. localstack/services/cloudformation/api_utils.py +4 -8
  71. localstack/services/cloudformation/cfn_utils.py +1 -1
  72. localstack/services/cloudformation/engine/entities.py +14 -4
  73. localstack/services/cloudformation/engine/template_deployer.py +6 -4
  74. localstack/services/cloudformation/engine/transformers.py +6 -4
  75. localstack/services/cloudformation/engine/v2/change_set_model.py +201 -13
  76. localstack/services/cloudformation/engine/v2/change_set_model_describer.py +52 -3
  77. localstack/services/cloudformation/engine/v2/change_set_model_executor.py +117 -76
  78. localstack/services/cloudformation/engine/v2/change_set_model_preproc.py +205 -52
  79. localstack/services/cloudformation/engine/v2/change_set_model_transform.py +350 -116
  80. localstack/services/cloudformation/engine/v2/change_set_model_validator.py +56 -14
  81. localstack/services/cloudformation/engine/v2/change_set_model_visitor.py +1 -0
  82. localstack/services/cloudformation/engine/v2/resolving.py +7 -5
  83. localstack/services/cloudformation/engine/yaml_parser.py +9 -2
  84. localstack/services/cloudformation/provider.py +7 -5
  85. localstack/services/cloudformation/resource_provider.py +7 -1
  86. localstack/services/cloudformation/resources.py +24149 -0
  87. localstack/services/cloudformation/service_models.py +2 -2
  88. localstack/services/cloudformation/v2/entities.py +19 -9
  89. localstack/services/cloudformation/v2/provider.py +336 -106
  90. localstack/services/cloudformation/v2/types.py +13 -7
  91. localstack/services/cloudformation/v2/utils.py +4 -1
  92. localstack/services/cloudwatch/alarm_scheduler.py +4 -1
  93. localstack/services/cloudwatch/provider.py +18 -13
  94. localstack/services/cloudwatch/provider_v2.py +25 -28
  95. localstack/services/dynamodb/packages.py +2 -1
  96. localstack/services/dynamodb/provider.py +42 -0
  97. localstack/services/dynamodb/server.py +2 -2
  98. localstack/services/dynamodb/v2/provider.py +42 -0
  99. localstack/services/ecr/resource_providers/aws_ecr_repository.py +5 -2
  100. localstack/services/edge.py +1 -1
  101. localstack/services/es/provider.py +2 -2
  102. localstack/services/events/event_rule_engine.py +31 -13
  103. localstack/services/events/models.py +4 -5
  104. localstack/services/events/provider.py +17 -14
  105. localstack/services/events/target.py +17 -9
  106. localstack/services/events/v1/provider.py +5 -5
  107. localstack/services/firehose/provider.py +14 -4
  108. localstack/services/iam/provider.py +11 -116
  109. localstack/services/iam/resources/policy_simulator.py +133 -0
  110. localstack/services/kinesis/models.py +15 -2
  111. localstack/services/kinesis/provider.py +86 -3
  112. localstack/services/kms/provider.py +14 -5
  113. localstack/services/lambda_/api_utils.py +6 -3
  114. localstack/services/lambda_/invocation/docker_runtime_executor.py +1 -1
  115. localstack/services/lambda_/invocation/event_manager.py +1 -1
  116. localstack/services/lambda_/invocation/internal_sqs_queue.py +5 -9
  117. localstack/services/lambda_/invocation/lambda_models.py +10 -7
  118. localstack/services/lambda_/invocation/lambda_service.py +5 -1
  119. localstack/services/lambda_/packages.py +1 -1
  120. localstack/services/lambda_/provider.py +4 -3
  121. localstack/services/lambda_/provider_utils.py +1 -1
  122. localstack/services/logs/provider.py +36 -19
  123. localstack/services/moto.py +2 -1
  124. localstack/services/opensearch/cluster.py +15 -7
  125. localstack/services/opensearch/packages.py +26 -7
  126. localstack/services/opensearch/provider.py +8 -2
  127. localstack/services/opensearch/versions.py +56 -7
  128. localstack/services/plugins.py +11 -7
  129. localstack/services/providers.py +10 -2
  130. localstack/services/redshift/provider.py +0 -21
  131. localstack/services/s3/constants.py +5 -2
  132. localstack/services/s3/cors.py +4 -4
  133. localstack/services/s3/models.py +1 -1
  134. localstack/services/s3/notifications.py +55 -39
  135. localstack/services/s3/presigned_url.py +35 -54
  136. localstack/services/s3/provider.py +73 -15
  137. localstack/services/s3/utils.py +42 -22
  138. localstack/services/s3/validation.py +46 -32
  139. localstack/services/s3/website_hosting.py +4 -2
  140. localstack/services/ses/provider.py +18 -8
  141. localstack/services/sns/constants.py +7 -1
  142. localstack/services/sns/executor.py +9 -2
  143. localstack/services/sns/provider.py +8 -5
  144. localstack/services/sns/publisher.py +31 -16
  145. localstack/services/sns/v2/models.py +167 -0
  146. localstack/services/sns/v2/provider.py +867 -0
  147. localstack/services/sns/v2/utils.py +130 -0
  148. localstack/services/sqs/constants.py +1 -1
  149. localstack/services/sqs/developer_api.py +205 -0
  150. localstack/services/sqs/models.py +48 -5
  151. localstack/services/sqs/provider.py +38 -311
  152. localstack/services/sqs/query_api.py +6 -2
  153. localstack/services/sqs/utils.py +121 -2
  154. localstack/services/ssm/provider.py +1 -1
  155. localstack/services/stepfunctions/asl/component/intrinsic/member.py +1 -1
  156. localstack/services/stepfunctions/asl/component/state/state_choice/comparison/comparison.py +5 -11
  157. localstack/services/stepfunctions/asl/component/state/state_choice/state_choice.py +2 -2
  158. localstack/services/stepfunctions/asl/component/state/state_execution/state_map/state_map.py +2 -2
  159. localstack/services/stepfunctions/asl/component/state/state_execution/state_parallel/state_parallel.py +1 -1
  160. localstack/services/stepfunctions/asl/component/state/state_execution/state_task/state_task.py +2 -2
  161. localstack/services/stepfunctions/asl/component/state/state_fail/state_fail.py +1 -1
  162. localstack/services/stepfunctions/asl/component/state/state_pass/state_pass.py +2 -2
  163. localstack/services/stepfunctions/asl/component/state/state_succeed/state_succeed.py +1 -1
  164. localstack/services/stepfunctions/asl/component/state/state_wait/state_wait.py +1 -1
  165. localstack/services/stepfunctions/asl/eval/environment.py +1 -1
  166. localstack/services/stepfunctions/asl/jsonata/jsonata.py +1 -1
  167. localstack/services/stepfunctions/backend/execution.py +2 -1
  168. localstack/services/stores.py +1 -1
  169. localstack/services/transcribe/provider.py +6 -1
  170. localstack/state/codecs.py +61 -0
  171. localstack/state/core.py +11 -5
  172. localstack/state/pickle.py +10 -49
  173. localstack/testing/aws/cloudformation_utils.py +1 -1
  174. localstack/testing/pytest/cloudformation/fixtures.py +3 -3
  175. localstack/testing/pytest/cloudformation/transformers.py +0 -0
  176. localstack/testing/pytest/container.py +4 -5
  177. localstack/testing/pytest/fixtures.py +33 -31
  178. localstack/testing/pytest/in_memory_localstack.py +0 -4
  179. localstack/testing/pytest/marking.py +38 -11
  180. localstack/testing/pytest/stepfunctions/utils.py +4 -3
  181. localstack/testing/pytest/util.py +1 -1
  182. localstack/testing/pytest/validation_tracking.py +1 -2
  183. localstack/testing/snapshots/transformer_utility.py +6 -1
  184. localstack/utils/analytics/events.py +2 -2
  185. localstack/utils/analytics/metadata.py +6 -4
  186. localstack/utils/analytics/metrics/counter.py +8 -15
  187. localstack/utils/analytics/publisher.py +1 -2
  188. localstack/utils/analytics/service_providers.py +19 -0
  189. localstack/utils/analytics/service_request_aggregator.py +2 -2
  190. localstack/utils/archives.py +11 -11
  191. localstack/utils/asyncio.py +2 -2
  192. localstack/utils/aws/arns.py +24 -29
  193. localstack/utils/aws/aws_responses.py +8 -8
  194. localstack/utils/aws/aws_stack.py +2 -3
  195. localstack/utils/aws/dead_letter_queue.py +1 -5
  196. localstack/utils/aws/message_forwarding.py +1 -2
  197. localstack/utils/aws/request_context.py +4 -5
  198. localstack/utils/aws/resources.py +1 -1
  199. localstack/utils/aws/templating.py +1 -1
  200. localstack/utils/batch_policy.py +3 -3
  201. localstack/utils/bootstrap.py +21 -13
  202. localstack/utils/catalog/catalog.py +139 -0
  203. localstack/utils/catalog/catalog_loader.py +119 -0
  204. localstack/utils/catalog/common.py +58 -0
  205. localstack/utils/catalog/plugins.py +28 -0
  206. localstack/utils/cloudwatch/cloudwatch_util.py +5 -5
  207. localstack/utils/collections.py +7 -8
  208. localstack/utils/config_listener.py +1 -1
  209. localstack/utils/container_networking.py +2 -3
  210. localstack/utils/container_utils/container_client.py +135 -136
  211. localstack/utils/container_utils/docker_cmd_client.py +85 -69
  212. localstack/utils/container_utils/docker_sdk_client.py +69 -66
  213. localstack/utils/crypto.py +10 -10
  214. localstack/utils/diagnose.py +3 -4
  215. localstack/utils/docker_utils.py +9 -5
  216. localstack/utils/files.py +33 -13
  217. localstack/utils/functions.py +4 -3
  218. localstack/utils/http.py +11 -11
  219. localstack/utils/json.py +20 -6
  220. localstack/utils/kinesis/kinesis_connector.py +2 -1
  221. localstack/utils/net.py +15 -9
  222. localstack/utils/no_exit_argument_parser.py +2 -2
  223. localstack/utils/numbers.py +9 -2
  224. localstack/utils/objects.py +7 -6
  225. localstack/utils/patch.py +10 -3
  226. localstack/utils/run.py +12 -11
  227. localstack/utils/scheduler.py +11 -11
  228. localstack/utils/server/tcp_proxy.py +2 -2
  229. localstack/utils/serving.py +3 -4
  230. localstack/utils/strings.py +15 -16
  231. localstack/utils/sync.py +126 -1
  232. localstack/utils/tagging.py +8 -6
  233. localstack/utils/testutil.py +8 -8
  234. localstack/utils/threads.py +2 -2
  235. localstack/utils/time.py +12 -4
  236. localstack/utils/urls.py +1 -3
  237. localstack/utils/xray/traceid.py +1 -1
  238. localstack/version.py +16 -3
  239. {localstack_core-4.7.1.dev49.dist-info → localstack_core-4.10.1.dev12.dist-info}/METADATA +18 -14
  240. {localstack_core-4.7.1.dev49.dist-info → localstack_core-4.10.1.dev12.dist-info}/RECORD +248 -239
  241. {localstack_core-4.7.1.dev49.dist-info → localstack_core-4.10.1.dev12.dist-info}/entry_points.txt +8 -4
  242. localstack_core-4.10.1.dev12.dist-info/plux.json +1 -0
  243. localstack/packages/terraform.py +0 -46
  244. localstack/services/cloudformation/deploy.html +0 -144
  245. localstack/services/cloudformation/deploy_ui.py +0 -47
  246. localstack/services/cloudformation/plugins.py +0 -12
  247. localstack_core-4.7.1.dev49.dist-info/plux.json +0 -1
  248. {localstack_core-4.7.1.dev49.data → localstack_core-4.10.1.dev12.data}/scripts/localstack +0 -0
  249. {localstack_core-4.7.1.dev49.data → localstack_core-4.10.1.dev12.data}/scripts/localstack-supervisor +0 -0
  250. {localstack_core-4.7.1.dev49.data → localstack_core-4.10.1.dev12.data}/scripts/localstack.bat +0 -0
  251. {localstack_core-4.7.1.dev49.dist-info → localstack_core-4.10.1.dev12.dist-info}/WHEEL +0 -0
  252. {localstack_core-4.7.1.dev49.dist-info → localstack_core-4.10.1.dev12.dist-info}/licenses/LICENSE.txt +0 -0
  253. {localstack_core-4.7.1.dev49.dist-info → localstack_core-4.10.1.dev12.dist-info}/top_level.txt +0 -0
@@ -72,7 +72,7 @@ class ComparisonCompositeSingle(ComparisonComposite, abc.ABC):
72
72
  rule: Final[ChoiceRule]
73
73
 
74
74
  def __init__(self, operator: ComparisonComposite.ChoiceOp, rule: ChoiceRule):
75
- super(ComparisonCompositeSingle, self).__init__(operator=operator)
75
+ super().__init__(operator=operator)
76
76
  self.rule = rule
77
77
 
78
78
 
@@ -80,15 +80,13 @@ class ComparisonCompositeMulti(ComparisonComposite, abc.ABC):
80
80
  rules: Final[list[ChoiceRule]]
81
81
 
82
82
  def __init__(self, operator: ComparisonComposite.ChoiceOp, rules: list[ChoiceRule]):
83
- super(ComparisonCompositeMulti, self).__init__(operator=operator)
83
+ super().__init__(operator=operator)
84
84
  self.rules = rules
85
85
 
86
86
 
87
87
  class ComparisonCompositeNot(ComparisonCompositeSingle):
88
88
  def __init__(self, rule: ChoiceRule):
89
- super(ComparisonCompositeNot, self).__init__(
90
- operator=ComparisonComposite.ChoiceOp.Not, rule=rule
91
- )
89
+ super().__init__(operator=ComparisonComposite.ChoiceOp.Not, rule=rule)
92
90
 
93
91
  def _eval_body(self, env: Environment) -> None:
94
92
  self.rule.eval(env)
@@ -99,9 +97,7 @@ class ComparisonCompositeNot(ComparisonCompositeSingle):
99
97
 
100
98
  class ComparisonCompositeAnd(ComparisonCompositeMulti):
101
99
  def __init__(self, rules: list[ChoiceRule]):
102
- super(ComparisonCompositeAnd, self).__init__(
103
- operator=ComparisonComposite.ChoiceOp.And, rules=rules
104
- )
100
+ super().__init__(operator=ComparisonComposite.ChoiceOp.And, rules=rules)
105
101
 
106
102
  def _eval_body(self, env: Environment) -> None:
107
103
  res = True
@@ -116,9 +112,7 @@ class ComparisonCompositeAnd(ComparisonCompositeMulti):
116
112
 
117
113
  class ComparisonCompositeOr(ComparisonCompositeMulti):
118
114
  def __init__(self, rules: list[ChoiceRule]):
119
- super(ComparisonCompositeOr, self).__init__(
120
- operator=ComparisonComposite.ChoiceOp.Or, rules=rules
121
- )
115
+ super().__init__(operator=ComparisonComposite.ChoiceOp.Or, rules=rules)
122
116
 
123
117
  def _eval_body(self, env: Environment) -> None:
124
118
  res = False
@@ -17,7 +17,7 @@ class StateChoice(CommonStateField):
17
17
  default_state: DefaultDecl | None
18
18
 
19
19
  def __init__(self):
20
- super(StateChoice, self).__init__(
20
+ super().__init__(
21
21
  state_entered_event_type=HistoryEventType.ChoiceStateEntered,
22
22
  state_exited_event_type=HistoryEventType.ChoiceStateExited,
23
23
  )
@@ -25,7 +25,7 @@ class StateChoice(CommonStateField):
25
25
  self._next_state_name = None
26
26
 
27
27
  def from_state_props(self, state_props: StateProps) -> None:
28
- super(StateChoice, self).from_state_props(state_props)
28
+ super().from_state_props(state_props)
29
29
  self.choices_decl = state_props.get(ChoicesDecl)
30
30
  self.default_state = state_props.get(DefaultDecl)
31
31
 
@@ -115,13 +115,13 @@ class StateMap(ExecutionState):
115
115
  result_writer: ResultWriter | None
116
116
 
117
117
  def __init__(self):
118
- super(StateMap, self).__init__(
118
+ super().__init__(
119
119
  state_entered_event_type=HistoryEventType.MapStateEntered,
120
120
  state_exited_event_type=HistoryEventType.MapStateExited,
121
121
  )
122
122
 
123
123
  def from_state_props(self, state_props: StateProps) -> None:
124
- super(StateMap, self).from_state_props(state_props)
124
+ super().from_state_props(state_props)
125
125
  if self._is_language_query_jsonpath():
126
126
  self.items = None
127
127
  self.items_path = state_props.get(ItemsPath) or ItemsPath(
@@ -33,7 +33,7 @@ class StateParallel(ExecutionState):
33
33
  )
34
34
 
35
35
  def from_state_props(self, state_props: StateProps) -> None:
36
- super(StateParallel, self).from_state_props(state_props)
36
+ super().from_state_props(state_props)
37
37
  self.branches = state_props.get(
38
38
  typ=BranchesDecl,
39
39
  raise_on_missing=ValueError(f"Missing Branches definition in props '{state_props}'."),
@@ -35,13 +35,13 @@ class StateTask(ExecutionState, abc.ABC):
35
35
  credentials: Credentials | None
36
36
 
37
37
  def __init__(self):
38
- super(StateTask, self).__init__(
38
+ super().__init__(
39
39
  state_entered_event_type=HistoryEventType.TaskStateEntered,
40
40
  state_exited_event_type=HistoryEventType.TaskStateExited,
41
41
  )
42
42
 
43
43
  def from_state_props(self, state_props: StateProps) -> None:
44
- super(StateTask, self).from_state_props(state_props)
44
+ super().from_state_props(state_props)
45
45
  self.resource = state_props.get(Resource)
46
46
  self.parargs = state_props.get(Parargs)
47
47
  self.credentials = state_props.get(Credentials)
@@ -24,7 +24,7 @@ class StateFail(CommonStateField):
24
24
  self.error: ErrorDecl | None = None
25
25
 
26
26
  def from_state_props(self, state_props: StateProps) -> None:
27
- super(StateFail, self).from_state_props(state_props)
27
+ super().from_state_props(state_props)
28
28
  self.cause = state_props.get(CauseDecl)
29
29
  self.error = state_props.get(ErrorDecl)
30
30
 
@@ -11,7 +11,7 @@ from localstack.services.stepfunctions.asl.eval.environment import Environment
11
11
 
12
12
  class StatePass(CommonStateField):
13
13
  def __init__(self):
14
- super(StatePass, self).__init__(
14
+ super().__init__(
15
15
  state_entered_event_type=HistoryEventType.PassStateEntered,
16
16
  state_exited_event_type=HistoryEventType.PassStateExited,
17
17
  )
@@ -32,7 +32,7 @@ class StatePass(CommonStateField):
32
32
  self.parameters: Parameters | None = None
33
33
 
34
34
  def from_state_props(self, state_props: StateProps) -> None:
35
- super(StatePass, self).from_state_props(state_props)
35
+ super().from_state_props(state_props)
36
36
  self.result = state_props.get(Result)
37
37
  self.result_path = state_props.get(ResultPath) or ResultPath(
38
38
  result_path_src=ResultPath.DEFAULT_PATH
@@ -17,7 +17,7 @@ class StateSucceed(CommonStateField):
17
17
  )
18
18
 
19
19
  def from_state_props(self, state_props: StateProps) -> None:
20
- super(StateSucceed, self).from_state_props(state_props)
20
+ super().from_state_props(state_props)
21
21
  # TODO: assert all other fields are undefined?
22
22
 
23
23
  # No Next or End field: Succeed states are terminal states.
@@ -19,7 +19,7 @@ class StateWait(CommonStateField):
19
19
  )
20
20
 
21
21
  def from_state_props(self, state_props: StateProps) -> None:
22
- super(StateWait, self).from_state_props(state_props)
22
+ super().from_state_props(state_props)
23
23
  self.wait_function = state_props.get(
24
24
  typ=WaitFunction,
25
25
  raise_on_missing=ValueError(f"Undefined WaitFunction for StateWait: '{self}'."),
@@ -73,7 +73,7 @@ class Environment:
73
73
  variable_store: VariableStore | None = None,
74
74
  mock_test_case: MockTestCase | None = None,
75
75
  ):
76
- super(Environment, self).__init__()
76
+ super().__init__()
77
77
  self._state_mutex = threading.RLock()
78
78
  self._program_state = None
79
79
  self.program_state_event = threading.Event()
@@ -7,7 +7,7 @@ from pathlib import Path
7
7
  from typing import Any, Final
8
8
 
9
9
  import jpype
10
- import jpype.imports
10
+ import jpype.imports # noqa # Required for JVM Java class imports
11
11
 
12
12
  from localstack.services.stepfunctions.asl.utils.encoding import to_json_str
13
13
  from localstack.services.stepfunctions.packages import jpype_jsonata_package
@@ -356,10 +356,11 @@ class Execution:
356
356
  try:
357
357
  self._get_events_client().put_events(Entries=[entry])
358
358
  except Exception:
359
- LOG.exception(
359
+ LOG.error(
360
360
  "Unable to send notification of Entry='%s' for Step Function execution with Arn='%s' to EventBridge.",
361
361
  entry,
362
362
  self.exec_arn,
363
+ exc_info=LOG.isEnabledFor(logging.DEBUG),
363
364
  )
364
365
 
365
366
 
@@ -238,7 +238,7 @@ class RegionBundle(dict, Generic[BaseStoreType]):
238
238
 
239
239
  store_obj._global = self._global
240
240
  store_obj._universal = self._universal
241
- store_obj.service_name = self.service_name
241
+ store_obj._service_name = self.service_name
242
242
  store_obj._account_id = self.account_id
243
243
  store_obj._region_name = region_name
244
244
 
@@ -412,4 +412,9 @@ class TranscribeProvider(TranscribeApi):
412
412
  job["FailureReason"] = failure_reason or str(exc)
413
413
  job["TranscriptionJobStatus"] = TranscriptionJobStatus.FAILED
414
414
 
415
- LOG.exception("Transcription job %s failed: %s", job_name, job["FailureReason"])
415
+ LOG.error(
416
+ "Transcription job %s failed: %s",
417
+ job_name,
418
+ job["FailureReason"],
419
+ exc_info=LOG.isEnabledFor(logging.DEBUG),
420
+ )
@@ -0,0 +1,61 @@
1
+ """Factory for encoders and decoders"""
2
+
3
+ from localstack import config
4
+ from localstack.state import Decoder, Encoder
5
+ from localstack.state.pickle import PickleDecoder, PickleEncoder
6
+
7
+ ENCODERS = {
8
+ "dill": PickleEncoder,
9
+ }
10
+ """Encoders that map to the name of ``STATE_SERIALIZATION_BACKEND``."""
11
+
12
+ DECODERS = {
13
+ "dill": PickleDecoder,
14
+ }
15
+ """Decoders that map to the name of ``STATE_SERIALIZATION_BACKEND``."""
16
+
17
+
18
+ def create_encoder(encoder_type: str) -> Encoder:
19
+ cls = ENCODERS.get(encoder_type)
20
+ if cls is None:
21
+ raise ValueError(f"Unknown encoder type: {encoder_type}")
22
+ return cls()
23
+
24
+
25
+ def create_decoder(decoder_type: str) -> Decoder:
26
+ cls = DECODERS.get(decoder_type, PickleDecoder)
27
+ if cls is None:
28
+ raise ValueError(f"Unknown decoder type: {decoder_type}")
29
+ return cls()
30
+
31
+
32
+ def get_default_encoder() -> Encoder:
33
+ """
34
+ Gets the default encoder based on the state serialization backend defined in the configuration
35
+ ``STATE_SERIALIZATION_BACKEND``.
36
+
37
+ If the serialization backend specified in the configuration leads to an error
38
+ (such as an invalid backend), a ``PickleEncoder`` is returned as a fallback.
39
+
40
+ :return: The default encoder for state serialization.
41
+ """
42
+ try:
43
+ return create_encoder(config.STATE_SERIALIZATION_BACKEND)
44
+ except ValueError:
45
+ return PickleEncoder()
46
+
47
+
48
+ def get_default_decoder() -> Decoder:
49
+ """
50
+ Gets the default decoder based on the state serialization backend defined in the configuration
51
+ ``STATE_SERIALIZATION_BACKEND``.
52
+
53
+ If the serialization backend specified in the configuration leads to an error
54
+ (such as an invalid backend), a ``PickleDecoder`` is returned as a fallback.
55
+
56
+ :return: The default decoder for state serialization.
57
+ """
58
+ try:
59
+ return create_decoder(config.STATE_SERIALIZATION_BACKEND)
60
+ except ValueError:
61
+ return PickleDecoder()
localstack/state/core.py CHANGED
@@ -97,42 +97,48 @@ class AssetDirectory:
97
97
 
98
98
 
99
99
  class Encoder:
100
- def encodes(self, obj: Any) -> bytes:
100
+ def encodes(self, obj: Any, py_type: type = None) -> bytes:
101
101
  """
102
102
  Encode an object into bytes.
103
103
 
104
104
  :param obj: the object to encode
105
+ :param py_type: the type of the object. needed by some encoders that don't have implicit type knowledge.
105
106
  :return: the encoded object
106
107
  """
107
108
  b = io.BytesIO()
108
109
  self.encode(obj, b)
109
110
  return b.getvalue()
110
111
 
111
- def encode(self, obj: Any, file: IO[bytes]):
112
+ def encode(self, obj: Any, file: IO[bytes], py_type: type = None):
112
113
  """
113
114
  Encode an object into bytes.
114
115
 
115
116
  :param obj: the object to encode
117
+ :param py_type: the type of the object. needed by some encoders that don't have implicit type knowledge.
116
118
  :param file: the file to write the encoded data into
117
119
  """
118
120
  raise NotImplementedError
119
121
 
120
122
 
121
123
  class Decoder:
122
- def decodes(self, data: bytes) -> Any:
124
+ def decodes(self, data: bytes, py_type: type = None) -> Any:
123
125
  """
124
126
  Decode a previously encoded object.
125
127
 
126
128
  :param data: the encoded object to decode
129
+ :param py_type: the type that is expected as return type. Needed by some decoders that don't have implicit
130
+ type knowledge.
127
131
  :return: the decoded object
128
132
  """
129
- return self.decode(io.BytesIO(data))
133
+ return self.decode(io.BytesIO(data), py_type)
130
134
 
131
- def decode(self, file: IO[bytes]) -> Any:
135
+ def decode(self, file: IO[bytes], py_type: type = None) -> Any:
132
136
  """
133
137
  Decode a previously encoded object.
134
138
 
135
139
  :param file: the io object containing the object to decode
140
+ :param py_type: the type that is expected as return type. Needed by some decoders that don't have implicit
141
+ type knowledge.
136
142
  :return: the decoded object
137
143
  """
138
144
  raise NotImplementedError
@@ -30,14 +30,11 @@ https://dill.readthedocs.io/en/latest/index.html?highlight=register#dill.Pickler
30
30
 
31
31
  import inspect
32
32
  from collections.abc import Callable
33
- from typing import IO, Any, BinaryIO, Generic, TypeVar
33
+ from typing import Any, BinaryIO, Generic, TypeVar
34
34
 
35
35
  import dill
36
- import jsonpickle
37
36
  from dill._dill import MetaCatchingDict
38
37
 
39
- from localstack import config
40
-
41
38
  from .core import Decoder, Encoder
42
39
 
43
40
  _T = TypeVar("_T")
@@ -64,7 +61,7 @@ def register(cls: type = None, subclasses: bool = False):
64
61
  elif callable(fn):
65
62
  add_dispatch_entry(cls, fn, subclasses=subclasses)
66
63
  else:
67
- raise ValueError("cannot register %s" % fn)
64
+ raise ValueError(f"cannot register {fn}")
68
65
 
69
66
  return fn
70
67
 
@@ -240,7 +237,7 @@ class PickleEncoder(Encoder):
240
237
  def __init__(self, pickler_class: type[dill.Pickler] = None):
241
238
  self.pickler_class = pickler_class or Pickler
242
239
 
243
- def encode(self, obj: Any, file: BinaryIO):
240
+ def encode(self, obj: Any, file: BinaryIO, py_type: type = None) -> Any:
244
241
  return self.pickler_class(file).dump(obj)
245
242
 
246
243
 
@@ -255,56 +252,20 @@ class PickleDecoder(Decoder):
255
252
  def __init__(self, unpickler_class: type[dill.Unpickler] = None):
256
253
  self.unpickler_class = unpickler_class or dill.Unpickler
257
254
 
258
- def decode(self, file: BinaryIO) -> Any:
255
+ def decode(self, file: BinaryIO, py_type=None) -> Any:
259
256
  return self.unpickler_class(file).load()
260
257
 
261
258
 
262
- class JsonEncoder(Encoder):
263
- """
264
- An Encoder that uses ``jsonpickle`` under the hood.
265
- """
266
-
267
- def __init__(self, pickler_class: type[jsonpickle.Pickler] = None):
268
- self.pickler_class = pickler_class or jsonpickle.Pickler()
269
-
270
- def encode(self, obj: Any, file: IO[bytes]):
271
- json_str = jsonpickle.encode(obj, context=self.pickler_class)
272
- file.write(json_str.encode("utf-8"))
273
-
274
-
275
- class JsonDecoder(Decoder):
276
- """
277
- A Decoder that uses ``jsonpickle`` under the hood.
278
- """
279
-
280
- unpickler_class: type[jsonpickle.Unpickler]
281
-
282
- def __init__(self, unpickler_class: type[jsonpickle.Unpickler] = None):
283
- self.unpickler_class = unpickler_class or jsonpickle.Unpickler()
284
-
285
- def decode(self, file: IO[bytes]) -> Any:
286
- json_str = file.read().decode("utf-8")
287
- return jsonpickle.decode(json_str, context=self.unpickler_class)
288
-
289
-
290
259
  def get_default_encoder() -> Encoder:
291
- match config.STATE_SERIALIZATION_BACKEND:
292
- case "jsonpickle":
293
- return JsonEncoder()
294
- case "dill":
295
- return PickleEncoder()
296
- case _:
297
- return PickleEncoder()
260
+ from .codecs import get_default_encoder
261
+
262
+ return get_default_encoder()
298
263
 
299
264
 
300
265
  def get_default_decoder() -> Decoder:
301
- match config.STATE_SERIALIZATION_BACKEND:
302
- case "jsonpickle":
303
- return JsonDecoder()
304
- case "dill":
305
- return PickleDecoder()
306
- case _:
307
- return PickleDecoder()
266
+ from .codecs import get_default_decoder
267
+
268
+ return get_default_decoder()
308
269
 
309
270
 
310
271
  class ObjectStateReducer(Generic[_T]):
@@ -29,7 +29,7 @@ def load_template_file(file_path: str | os.PathLike, *, path_ctx: str | os.PathL
29
29
  elif not file_path_obj.is_absolute():
30
30
  raise ValueError("Provided path must be absolute if no path_ctx is provided")
31
31
 
32
- return load_file(file_path_obj.absolute())
32
+ return load_file(file_path_obj.absolute(), strict=True)
33
33
 
34
34
 
35
35
  # TODO: TBH this utility really doesn't add anything, probably better to just remove it
@@ -1,7 +1,7 @@
1
1
  import json
2
2
  from collections import defaultdict
3
- from collections.abc import Generator
4
- from typing import Callable, Optional, TypedDict
3
+ from collections.abc import Callable, Generator
4
+ from typing import TypedDict
5
5
 
6
6
  import pytest
7
7
  from botocore.exceptions import WaiterError
@@ -13,7 +13,7 @@ from localstack.utils.strings import short_uid
13
13
 
14
14
 
15
15
  class NormalizedEvent(TypedDict):
16
- PhysicalResourceId: Optional[str]
16
+ PhysicalResourceId: str | None
17
17
  LogicalResourceId: str
18
18
  ResourceType: str
19
19
  ResourceStatus: str
@@ -2,8 +2,7 @@ import logging
2
2
  import os
3
3
  import shlex
4
4
  import threading
5
- from collections.abc import Generator
6
- from typing import Callable, Optional
5
+ from collections.abc import Callable, Generator
7
6
 
8
7
  import pytest
9
8
 
@@ -38,8 +37,8 @@ class ContainerFactory:
38
37
  self,
39
38
  # convenience properties
40
39
  pro: bool = False,
41
- publish: Optional[list[int]] = None,
42
- configurators: Optional[list[ContainerConfigurator]] = None,
40
+ publish: list[int] | None = None,
41
+ configurators: list[ContainerConfigurator] | None = None,
43
42
  # ContainerConfig properties
44
43
  **kwargs,
45
44
  ) -> Container:
@@ -172,7 +171,7 @@ def container_factory() -> Generator[ContainerFactory, None, None]:
172
171
 
173
172
  @pytest.fixture(scope="session")
174
173
  def wait_for_localstack_ready():
175
- def _wait_for(container: RunningContainer, timeout: Optional[float] = None):
174
+ def _wait_for(container: RunningContainer, timeout: float | None = None):
176
175
  container.wait_until_ready(timeout)
177
176
 
178
177
  poll_condition(
@@ -6,7 +6,8 @@ import os
6
6
  import re
7
7
  import textwrap
8
8
  import time
9
- from typing import TYPE_CHECKING, Any, Callable, Optional, Unpack
9
+ from collections.abc import Callable
10
+ from typing import TYPE_CHECKING, Any, Unpack
10
11
 
11
12
  import botocore.auth
12
13
  import botocore.config
@@ -232,7 +233,7 @@ def s3_create_bucket(s3_empty_bucket, aws_client):
232
233
 
233
234
  def factory(**kwargs) -> str:
234
235
  if "Bucket" not in kwargs:
235
- kwargs["Bucket"] = "test-bucket-%s" % short_uid()
236
+ kwargs["Bucket"] = f"test-bucket-{short_uid()}"
236
237
 
237
238
  if (
238
239
  "CreateBucketConfiguration" not in kwargs
@@ -335,7 +336,7 @@ def sqs_create_queue(aws_client):
335
336
 
336
337
  def factory(**kwargs):
337
338
  if "QueueName" not in kwargs:
338
- kwargs["QueueName"] = "test-queue-%s" % short_uid()
339
+ kwargs["QueueName"] = f"test-queue-{short_uid()}"
339
340
 
340
341
  response = aws_client.sqs.create_queue(**kwargs)
341
342
  url = response["QueueUrl"]
@@ -357,8 +358,8 @@ def sqs_create_queue(aws_client):
357
358
  def sqs_receive_messages_delete(aws_client):
358
359
  def factory(
359
360
  queue_url: str,
360
- expected_messages: Optional[int] = None,
361
- wait_time: Optional[int] = 5,
361
+ expected_messages: int | None = None,
362
+ wait_time: int | None = 5,
362
363
  ):
363
364
  response = aws_client.sqs.receive_message(
364
365
  QueueUrl=queue_url,
@@ -503,7 +504,7 @@ def sns_create_topic(aws_client):
503
504
 
504
505
  def _create_topic(**kwargs):
505
506
  if "Name" not in kwargs:
506
- kwargs["Name"] = "test-topic-%s" % short_uid()
507
+ kwargs["Name"] = f"test-topic-{short_uid()}"
507
508
  response = aws_client.sns.create_topic(**kwargs)
508
509
  topic_arns.append(response["TopicArn"])
509
510
  return response
@@ -706,7 +707,7 @@ def route53_hosted_zone(aws_client):
706
707
  def transcribe_create_job(s3_bucket, aws_client):
707
708
  job_names = []
708
709
 
709
- def _create_job(audio_file: str, params: Optional[dict[str, Any]] = None) -> str:
710
+ def _create_job(audio_file: str, params: dict[str, Any] | None = None) -> str:
710
711
  s3_key = "test-clip.wav"
711
712
 
712
713
  if not params:
@@ -1085,18 +1086,18 @@ def deploy_cfn_template(
1085
1086
 
1086
1087
  def _deploy(
1087
1088
  *,
1088
- is_update: Optional[bool] = False,
1089
- stack_name: Optional[str] = None,
1090
- change_set_name: Optional[str] = None,
1091
- template: Optional[str] = None,
1092
- template_path: Optional[str | os.PathLike] = None,
1093
- template_mapping: Optional[dict[str, Any]] = None,
1094
- parameters: Optional[dict[str, str]] = None,
1095
- role_arn: Optional[str] = None,
1096
- max_wait: Optional[int] = None,
1097
- delay_between_polls: Optional[int] = 2,
1098
- custom_aws_client: Optional[ServiceLevelClientFactory] = None,
1099
- raw_parameters: Optional[list[Parameter]] = None,
1089
+ is_update: bool | None = False,
1090
+ stack_name: str | None = None,
1091
+ change_set_name: str | None = None,
1092
+ template: str | None = None,
1093
+ template_path: str | os.PathLike | None = None,
1094
+ template_mapping: dict[str, Any] | None = None,
1095
+ parameters: dict[str, str] | None = None,
1096
+ role_arn: str | None = None,
1097
+ max_wait: int | None = None,
1098
+ delay_between_polls: int | None = 2,
1099
+ custom_aws_client: ServiceLevelClientFactory | None = None,
1100
+ raw_parameters: list[Parameter] | None = None,
1100
1101
  ) -> DeployResult:
1101
1102
  if is_update:
1102
1103
  assert stack_name
@@ -1137,9 +1138,16 @@ def deploy_cfn_template(
1137
1138
  change_set_id = response["Id"]
1138
1139
  stack_id = response["StackId"]
1139
1140
 
1140
- cfn_aws_client.cloudformation.get_waiter(WAITER_CHANGE_SET_CREATE_COMPLETE).wait(
1141
- ChangeSetName=change_set_id
1142
- )
1141
+ try:
1142
+ cfn_aws_client.cloudformation.get_waiter(WAITER_CHANGE_SET_CREATE_COMPLETE).wait(
1143
+ ChangeSetName=change_set_id
1144
+ )
1145
+ except botocore.exceptions.WaiterError as e:
1146
+ change_set = cfn_aws_client.cloudformation.describe_change_set(
1147
+ ChangeSetName=change_set_id
1148
+ )
1149
+ raise Exception(f"{change_set['Status']}: {change_set.get('StatusReason')}") from e
1150
+
1143
1151
  cfn_aws_client.cloudformation.execute_change_set(ChangeSetName=change_set_id)
1144
1152
  stack_waiter = cfn_aws_client.cloudformation.get_waiter(
1145
1153
  WAITER_STACK_UPDATE_COMPLETE if is_update else WAITER_STACK_CREATE_COMPLETE
@@ -1255,7 +1263,7 @@ def _has_stack_status(cfn_client, statuses: list[str]):
1255
1263
 
1256
1264
  @pytest.fixture
1257
1265
  def is_change_set_finished(aws_client):
1258
- def _is_change_set_finished(change_set_id: str, stack_name: Optional[str] = None):
1266
+ def _is_change_set_finished(change_set_id: str, stack_name: str | None = None):
1259
1267
  def _inner():
1260
1268
  kwargs = {"ChangeSetName": change_set_id}
1261
1269
  if stack_name:
@@ -1986,7 +1994,7 @@ def setup_sender_email_address(ses_verify_identity):
1986
1994
  email address and verify them.
1987
1995
  """
1988
1996
 
1989
- def inner(sender_email_address: Optional[str] = None) -> str:
1997
+ def inner(sender_email_address: str | None = None) -> str:
1990
1998
  if is_aws_cloud():
1991
1999
  if sender_email_address is None:
1992
2000
  raise ValueError(
@@ -2245,7 +2253,7 @@ def assert_host_customisation(monkeypatch):
2245
2253
  def asserter(
2246
2254
  url: str,
2247
2255
  *,
2248
- custom_host: Optional[str] = None,
2256
+ custom_host: str | None = None,
2249
2257
  ):
2250
2258
  if custom_host is not None:
2251
2259
  assert custom_host in url, f"Could not find `{custom_host}` in `{url}`"
@@ -2747,9 +2755,3 @@ def clean_up(
2747
2755
  call_safe(_delete_log_group)
2748
2756
 
2749
2757
  yield _clean_up
2750
-
2751
-
2752
- @pytest.fixture(params=["dill", "jsonpickle"])
2753
- def patch_default_encoder(request, monkeypatch):
2754
- backend = request.param
2755
- monkeypatch.setattr(config, "STATE_SERIALIZATION_BACKEND", backend)
@@ -63,8 +63,6 @@ def pytest_runtestloop(session: Session):
63
63
  return
64
64
  LOG.info("TEST_FORCE_LOCALSTACK_START is set, a Localstack instance will be created.")
65
65
 
66
- from localstack.utils.common import safe_requests
67
-
68
66
  if is_aws_cloud():
69
67
  localstack_config.DEFAULT_DELAY = 5
70
68
  localstack_config.DEFAULT_MAX_ATTEMPTS = 60
@@ -73,8 +71,6 @@ def pytest_runtestloop(session: Session):
73
71
  os.environ[ENV_INTERNAL_TEST_RUN] = "1"
74
72
  localstack_config.INCLUDE_STACK_TRACES_IN_HTTP_RESPONSE = True
75
73
 
76
- safe_requests.verify_ssl = False
77
-
78
74
  from localstack.runtime import current
79
75
 
80
76
  _started.set()