localstack-core 4.10.1.dev42__py3-none-any.whl → 4.12.1.dev18__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.

Potentially problematic release.


This version of localstack-core might be problematic. Click here for more details.

Files changed (158) hide show
  1. localstack/aws/api/apigateway/__init__.py +42 -0
  2. localstack/aws/api/cloudformation/__init__.py +161 -0
  3. localstack/aws/api/ec2/__init__.py +1178 -12
  4. localstack/aws/api/iam/__init__.py +228 -0
  5. localstack/aws/api/kms/__init__.py +1 -0
  6. localstack/aws/api/lambda_/__init__.py +1034 -66
  7. localstack/aws/api/logs/__init__.py +500 -0
  8. localstack/aws/api/opensearch/__init__.py +100 -0
  9. localstack/aws/api/redshift/__init__.py +69 -0
  10. localstack/aws/api/resourcegroupstaggingapi/__init__.py +36 -0
  11. localstack/aws/api/route53/__init__.py +45 -0
  12. localstack/aws/api/route53resolver/__init__.py +1 -0
  13. localstack/aws/api/s3/__init__.py +64 -0
  14. localstack/aws/api/s3control/__init__.py +19 -0
  15. localstack/aws/api/secretsmanager/__init__.py +37 -23
  16. localstack/aws/api/stepfunctions/__init__.py +52 -10
  17. localstack/aws/api/sts/__init__.py +52 -0
  18. localstack/aws/connect.py +35 -15
  19. localstack/aws/handlers/logging.py +8 -4
  20. localstack/aws/handlers/service.py +11 -2
  21. localstack/aws/protocol/serializer.py +1 -1
  22. localstack/config.py +8 -0
  23. localstack/constants.py +3 -0
  24. localstack/deprecations.py +0 -6
  25. localstack/dev/kubernetes/__main__.py +39 -14
  26. localstack/runtime/analytics.py +11 -0
  27. localstack/services/acm/provider.py +17 -1
  28. localstack/services/apigateway/legacy/provider.py +28 -15
  29. localstack/services/cloudformation/engine/template_preparer.py +6 -2
  30. localstack/services/cloudformation/engine/v2/change_set_model.py +9 -0
  31. localstack/services/cloudformation/engine/v2/change_set_model_preproc.py +15 -1
  32. localstack/services/cloudformation/engine/v2/change_set_resource_support_checker.py +114 -0
  33. localstack/services/cloudformation/provider.py +26 -1
  34. localstack/services/cloudformation/provider_utils.py +20 -0
  35. localstack/services/cloudformation/resource_provider.py +5 -4
  36. localstack/services/cloudformation/scaffolding/__main__.py +94 -22
  37. localstack/services/cloudformation/v2/provider.py +41 -0
  38. localstack/services/cloudwatch/provider.py +10 -3
  39. localstack/services/cloudwatch/provider_v2.py +6 -3
  40. localstack/services/configservice/provider.py +5 -1
  41. localstack/services/dynamodb/provider.py +1 -0
  42. localstack/services/dynamodb/v2/provider.py +1 -0
  43. localstack/services/dynamodbstreams/provider.py +6 -0
  44. localstack/services/dynamodbstreams/v2/provider.py +6 -0
  45. localstack/services/ec2/provider.py +6 -0
  46. localstack/services/es/provider.py +6 -0
  47. localstack/services/events/provider.py +4 -0
  48. localstack/services/events/v1/provider.py +9 -0
  49. localstack/services/firehose/provider.py +5 -0
  50. localstack/services/iam/provider.py +4 -0
  51. localstack/services/kinesis/packages.py +1 -1
  52. localstack/services/kms/models.py +16 -22
  53. localstack/services/kms/provider.py +4 -0
  54. localstack/services/lambda_/analytics.py +11 -2
  55. localstack/services/lambda_/api_utils.py +37 -20
  56. localstack/services/lambda_/event_source_mapping/pollers/stream_poller.py +1 -1
  57. localstack/services/lambda_/invocation/assignment.py +4 -1
  58. localstack/services/lambda_/invocation/event_manager.py +15 -11
  59. localstack/services/lambda_/invocation/execution_environment.py +21 -2
  60. localstack/services/lambda_/invocation/lambda_models.py +31 -2
  61. localstack/services/lambda_/invocation/lambda_service.py +62 -3
  62. localstack/services/lambda_/invocation/models.py +9 -1
  63. localstack/services/lambda_/invocation/version_manager.py +18 -3
  64. localstack/services/lambda_/provider.py +307 -106
  65. localstack/services/lambda_/resource_providers/aws_lambda_function.py +33 -1
  66. localstack/services/lambda_/runtimes.py +3 -1
  67. localstack/services/logs/provider.py +9 -0
  68. localstack/services/opensearch/packages.py +34 -20
  69. localstack/services/opensearch/provider.py +53 -3
  70. localstack/services/resource_groups/provider.py +5 -1
  71. localstack/services/resourcegroupstaggingapi/provider.py +6 -1
  72. localstack/services/route53/provider.py +7 -0
  73. localstack/services/route53resolver/provider.py +5 -0
  74. localstack/services/s3/constants.py +5 -0
  75. localstack/services/s3/exceptions.py +9 -0
  76. localstack/services/s3/models.py +9 -1
  77. localstack/services/s3/provider.py +51 -43
  78. localstack/services/s3/utils.py +81 -15
  79. localstack/services/s3control/provider.py +107 -2
  80. localstack/services/s3control/validation.py +50 -0
  81. localstack/services/scheduler/provider.py +4 -2
  82. localstack/services/secretsmanager/provider.py +4 -0
  83. localstack/services/ses/provider.py +4 -0
  84. localstack/services/sns/constants.py +16 -1
  85. localstack/services/sns/provider.py +5 -0
  86. localstack/services/sns/publisher.py +15 -6
  87. localstack/services/sns/v2/models.py +9 -0
  88. localstack/services/sns/v2/provider.py +750 -19
  89. localstack/services/sns/v2/utils.py +12 -0
  90. localstack/services/sqs/constants.py +6 -0
  91. localstack/services/sqs/provider.py +9 -1
  92. localstack/services/sqs/resource_providers/aws_sqs_queue.py +61 -46
  93. localstack/services/ssm/provider.py +6 -0
  94. localstack/services/stepfunctions/asl/component/common/path/result_path.py +1 -1
  95. localstack/services/stepfunctions/asl/component/state/state_execution/execute_state.py +0 -1
  96. localstack/services/stepfunctions/asl/component/state/state_execution/state_map/state_map.py +0 -1
  97. localstack/services/stepfunctions/asl/component/state/state_execution/state_task/lambda_eval_utils.py +8 -8
  98. localstack/services/stepfunctions/asl/component/state/state_execution/state_task/{mock_eval_utils.py → local_mock_eval_utils.py} +13 -9
  99. localstack/services/stepfunctions/asl/component/state/state_execution/state_task/service/state_task_service.py +6 -6
  100. localstack/services/stepfunctions/asl/component/state/state_execution/state_task/service/state_task_service_callback.py +1 -1
  101. localstack/services/stepfunctions/asl/component/state/state_fail/state_fail.py +4 -0
  102. localstack/services/stepfunctions/asl/component/test_state/state/base_mock.py +118 -0
  103. localstack/services/stepfunctions/asl/component/test_state/state/common.py +82 -0
  104. localstack/services/stepfunctions/asl/component/test_state/state/execution.py +139 -0
  105. localstack/services/stepfunctions/asl/component/test_state/state/map.py +77 -0
  106. localstack/services/stepfunctions/asl/component/test_state/state/task.py +44 -0
  107. localstack/services/stepfunctions/asl/eval/environment.py +30 -22
  108. localstack/services/stepfunctions/asl/eval/states.py +1 -1
  109. localstack/services/stepfunctions/asl/eval/test_state/environment.py +49 -9
  110. localstack/services/stepfunctions/asl/eval/test_state/program_state.py +22 -0
  111. localstack/services/stepfunctions/asl/jsonata/jsonata.py +5 -1
  112. localstack/services/stepfunctions/asl/parse/preprocessor.py +67 -24
  113. localstack/services/stepfunctions/asl/parse/test_state/asl_parser.py +5 -4
  114. localstack/services/stepfunctions/asl/parse/test_state/preprocessor.py +222 -31
  115. localstack/services/stepfunctions/asl/static_analyser/test_state/test_state_analyser.py +256 -22
  116. localstack/services/stepfunctions/backend/execution.py +10 -11
  117. localstack/services/stepfunctions/backend/execution_worker.py +5 -5
  118. localstack/services/stepfunctions/backend/test_state/execution.py +36 -0
  119. localstack/services/stepfunctions/backend/test_state/execution_worker.py +33 -1
  120. localstack/services/stepfunctions/backend/test_state/test_state_mock.py +127 -0
  121. localstack/services/stepfunctions/local_mocking/__init__.py +9 -0
  122. localstack/services/stepfunctions/{mocking → local_mocking}/mock_config.py +24 -17
  123. localstack/services/stepfunctions/provider.py +83 -25
  124. localstack/services/stepfunctions/test_state/mock_config.py +47 -0
  125. localstack/services/sts/provider.py +7 -0
  126. localstack/services/support/provider.py +5 -1
  127. localstack/services/swf/provider.py +5 -1
  128. localstack/services/transcribe/provider.py +7 -0
  129. localstack/testing/aws/lambda_utils.py +1 -1
  130. localstack/testing/aws/util.py +2 -1
  131. localstack/testing/config.py +1 -0
  132. localstack/testing/pytest/fixtures.py +28 -0
  133. localstack/testing/snapshots/transformer_utility.py +5 -0
  134. localstack/utils/analytics/publisher.py +37 -155
  135. localstack/utils/analytics/service_request_aggregator.py +6 -4
  136. localstack/utils/aws/arns.py +7 -0
  137. localstack/utils/aws/client_types.py +2 -4
  138. localstack/utils/batching.py +258 -0
  139. localstack/utils/bootstrap.py +2 -2
  140. localstack/utils/catalog/catalog.py +3 -2
  141. localstack/utils/collections.py +23 -11
  142. localstack/utils/container_utils/container_client.py +22 -13
  143. localstack/utils/container_utils/docker_cmd_client.py +6 -6
  144. localstack/version.py +2 -2
  145. {localstack_core-4.10.1.dev42.dist-info → localstack_core-4.12.1.dev18.dist-info}/METADATA +7 -7
  146. {localstack_core-4.10.1.dev42.dist-info → localstack_core-4.12.1.dev18.dist-info}/RECORD +155 -146
  147. localstack_core-4.12.1.dev18.dist-info/plux.json +1 -0
  148. localstack/services/stepfunctions/mocking/__init__.py +0 -0
  149. localstack/utils/batch_policy.py +0 -124
  150. localstack_core-4.10.1.dev42.dist-info/plux.json +0 -1
  151. /localstack/services/stepfunctions/{mocking → local_mocking}/mock_config_file.py +0 -0
  152. {localstack_core-4.10.1.dev42.data → localstack_core-4.12.1.dev18.data}/scripts/localstack +0 -0
  153. {localstack_core-4.10.1.dev42.data → localstack_core-4.12.1.dev18.data}/scripts/localstack-supervisor +0 -0
  154. {localstack_core-4.10.1.dev42.data → localstack_core-4.12.1.dev18.data}/scripts/localstack.bat +0 -0
  155. {localstack_core-4.10.1.dev42.dist-info → localstack_core-4.12.1.dev18.dist-info}/WHEEL +0 -0
  156. {localstack_core-4.10.1.dev42.dist-info → localstack_core-4.12.1.dev18.dist-info}/entry_points.txt +0 -0
  157. {localstack_core-4.10.1.dev42.dist-info → localstack_core-4.12.1.dev18.dist-info}/licenses/LICENSE.txt +0 -0
  158. {localstack_core-4.10.1.dev42.dist-info → localstack_core-4.12.1.dev18.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,139 @@
1
+ from collections.abc import Callable
2
+ from functools import partial
3
+
4
+ from localstack.services.stepfunctions.asl.component.common.catch.catcher_outcome import (
5
+ CatcherOutcomeCaught,
6
+ )
7
+ from localstack.services.stepfunctions.asl.component.common.error_name.failure_event import (
8
+ FailureEvent,
9
+ )
10
+ from localstack.services.stepfunctions.asl.component.common.query_language import (
11
+ QueryLanguageMode,
12
+ )
13
+ from localstack.services.stepfunctions.asl.component.common.retry.retrier_decl import RetrierDecl
14
+ from localstack.services.stepfunctions.asl.component.common.retry.retrier_outcome import (
15
+ RetrierOutcome,
16
+ )
17
+ from localstack.services.stepfunctions.asl.component.common.retry.retry_outcome import RetryOutcome
18
+ from localstack.services.stepfunctions.asl.component.state.state_execution.execute_state import (
19
+ ExecutionState,
20
+ )
21
+ from localstack.services.stepfunctions.asl.component.test_state.state.base_mock import (
22
+ MockedBaseState,
23
+ )
24
+ from localstack.services.stepfunctions.asl.eval.test_state.environment import TestStateEnvironment
25
+ from localstack.services.stepfunctions.asl.utils.encoding import to_json_str
26
+
27
+
28
+ class MockedStateExecution(MockedBaseState[ExecutionState]):
29
+ def add_inspection_data(self, env: TestStateEnvironment):
30
+ if self._wrapped.query_language.query_language_mode == QueryLanguageMode.JSONPath:
31
+ if "afterResultSelector" not in env.inspection_data:
32
+ # HACK: A DistributedItemProcessorEvalInput is added to the stack and never popped off
33
+ # during an error case. So we need to check the inspected value is correct before
34
+ # adding it to our inspectionData.
35
+ if isinstance(env.stack[-1], (dict, str, int, float)):
36
+ env.inspection_data["afterResultSelector"] = to_json_str(env.stack[-1])
37
+
38
+ if catch := self._wrapped.catch:
39
+ for ind, catcher in enumerate(catch.catchers):
40
+ original_fn = catcher._eval_body
41
+ catcher._eval_body = self.with_catch_state_id(original_fn, ind)
42
+
43
+ if retry := self._wrapped.retry:
44
+ for ind, retrier in enumerate(retry.retriers):
45
+ original_fn = retrier._eval_body
46
+ retrier._eval_body = self.with_retry_state_id(retrier, ind)
47
+
48
+ def _apply_patches(self):
49
+ if not isinstance(self._wrapped, ExecutionState):
50
+ raise ValueError("Can only apply MockedStateExecution patches to an ExecutionState")
51
+ state = self._wrapped
52
+
53
+ if state.query_language.query_language_mode == QueryLanguageMode.JSONPath:
54
+ self._eval_with_inspect(self._wrapped.input_path, "afterInputPath")
55
+ self._eval_with_inspect(self._wrapped.result_path, "afterResultPath")
56
+
57
+ self._eval_with_inspect(self._wrapped.result_selector, "afterResultSelector")
58
+ original_eval_execution = self._wrapped._eval_execution
59
+
60
+ if self._wrapped.catch:
61
+ original_fn = self._wrapped._handle_catch
62
+ self._wrapped._handle_catch = partial(self._handle_catch, original_fn)
63
+
64
+ if self._wrapped.retry:
65
+ original_fn = self._wrapped._handle_retry
66
+ self._wrapped._handle_retry = partial(self._handle_retry, original_fn)
67
+
68
+ self._wrapped._eval_execution = self.wrap_with_post_return(
69
+ method=original_eval_execution,
70
+ post_return_fn=self.add_inspection_data,
71
+ )
72
+
73
+ @staticmethod
74
+ def with_catch_state_id(
75
+ original_eval_body: Callable[[TestStateEnvironment], None], state_id: int
76
+ ) -> Callable[[TestStateEnvironment], None]:
77
+ def _wrapped(env: TestStateEnvironment):
78
+ original_eval_body(env)
79
+
80
+ if isinstance(env.stack[-1], CatcherOutcomeCaught):
81
+ if not (error_details := env.inspection_data.get("errorDetails")):
82
+ error_details = env.inspection_data["errorDetails"] = {}
83
+
84
+ error_details["catchIndex"] = state_id
85
+
86
+ return _wrapped
87
+
88
+ @staticmethod
89
+ def with_retry_state_id(
90
+ retrier: RetrierDecl, state_id: int
91
+ ) -> Callable[[TestStateEnvironment], None]:
92
+ original_retrier_eval_body = retrier._eval_body
93
+
94
+ def _wrapped(env: TestStateEnvironment):
95
+ if (retry_count := env.mock._state_configuration.get("retrierRetryCount", 0)) > 0:
96
+ retrier.max_attempts._store_attempt_number(env, retry_count - 1)
97
+
98
+ original_retrier_eval_body(env)
99
+
100
+ if not (error_details := env.inspection_data.get("errorDetails")):
101
+ error_details = env.inspection_data["errorDetails"] = {}
102
+
103
+ error_details["retryIndex"] = state_id
104
+ if env.stack[-1] == RetrierOutcome.Executed:
105
+ # TODO(gregfurman): Ideally, retryBackoffIntervalSeconds should be written to inspectionData
106
+ # within the retrier.backoff_rate decleration (perhaps at _access_next_multiplier).
107
+ rate = retrier.backoff_rate.rate
108
+ interval = retrier.interval_seconds.seconds
109
+ error_details["retryBackoffIntervalSeconds"] = int(interval * (rate**retry_count))
110
+
111
+ return _wrapped
112
+
113
+ @staticmethod
114
+ def _handle_catch(
115
+ original_handle_catch: Callable[[TestStateEnvironment, FailureEvent], None],
116
+ env: TestStateEnvironment,
117
+ failure_event: FailureEvent,
118
+ ) -> None:
119
+ original_handle_catch(env, failure_event)
120
+
121
+ spec: dict[str, str] = ExecutionState._construct_error_output_value(failure_event)
122
+ error, cause = spec.get("Error"), spec.get("Cause")
123
+
124
+ env.set_caught_error(env.next_state_name, error, cause)
125
+
126
+ @staticmethod
127
+ def _handle_retry(
128
+ original_handle_retry: Callable[[TestStateEnvironment, FailureEvent], RetryOutcome],
129
+ env: TestStateEnvironment,
130
+ failure_event: FailureEvent,
131
+ ) -> RetryOutcome:
132
+ res = original_handle_retry(env, failure_event)
133
+
134
+ spec: dict[str, str] = ExecutionState._construct_error_output_value(failure_event)
135
+ error, cause = spec.get("Error"), spec.get("Cause")
136
+
137
+ if res == RetryOutcome.CanRetry:
138
+ env.set_retriable_error(error, cause)
139
+ return res
@@ -0,0 +1,77 @@
1
+ from localstack.services.stepfunctions.asl.component.common.error_name.states_error_name_type import (
2
+ StatesErrorNameType,
3
+ )
4
+ from localstack.services.stepfunctions.asl.component.common.query_language import (
5
+ QueryLanguageMode,
6
+ )
7
+ from localstack.services.stepfunctions.asl.component.state.state_execution.state_map.state_map import (
8
+ StateMap,
9
+ )
10
+ from localstack.services.stepfunctions.asl.component.test_state.state.base_mock import (
11
+ MockedBaseState,
12
+ )
13
+ from localstack.services.stepfunctions.asl.component.test_state.state.execution import (
14
+ MockedStateExecution,
15
+ )
16
+ from localstack.services.stepfunctions.asl.eval.test_state.environment import TestStateEnvironment
17
+ from localstack.services.stepfunctions.backend.test_state.test_state_mock import (
18
+ TestStateResponseThrow,
19
+ )
20
+
21
+
22
+ class MockedStateMap(MockedBaseState[StateMap]):
23
+ def add_inspection_data(self, env: TestStateEnvironment):
24
+ if tolerated_failure_percentage := env.inspection_data.get("toleratedFailurePercentage"):
25
+ env.inspection_data["toleratedFailurePercentage"] = float(tolerated_failure_percentage)
26
+
27
+ if tolerated_failure_count := env.inspection_data.get("toleratedFailureCount"):
28
+ env.inspection_data["toleratedFailureCount"] = int(tolerated_failure_count)
29
+
30
+ @classmethod
31
+ def before_mock(cls, env: TestStateEnvironment):
32
+ if not env.mock or not env.mock._state_configuration:
33
+ return
34
+
35
+ if not cls._wrapped.catch and not cls._wrapped.retry:
36
+ return
37
+
38
+ if failure_count := env.mock._state_configuration.get("mapIterationFailureCount"):
39
+ max_failure_count = (
40
+ cls._wrapped.tolerated_failure_count_decl._eval_tolerated_failure_count(env)
41
+ )
42
+ if failure_count > max_failure_count:
43
+ error_response = TestStateResponseThrow(
44
+ error=StatesErrorNameType.StatesExceedToleratedFailureThreshold.to_name(),
45
+ cause="The specified tolerated failure threshold was exceeded",
46
+ )
47
+ env.mock.add_result(error_response)
48
+ return
49
+
50
+ def _apply_patches(self):
51
+ self._wrapped = MockedStateExecution.wrap(self._wrapped)
52
+
53
+ if self._wrapped.query_language.query_language_mode == QueryLanguageMode.JSONPath:
54
+ self._eval_with_inspect(self._wrapped.items_path, "afterInputPath")
55
+ self._eval_with_inspect(self._wrapped.item_selector, "afterItemsSelector")
56
+
57
+ original_eval_max_concurrency = self._wrapped.max_concurrency_decl._eval_max_concurrency
58
+ original_iteration_component_eval_body = self._wrapped.iteration_component._eval_body
59
+ original_eval_execution = self._wrapped._eval_execution
60
+
61
+ # HACK(gregfurman): Ideally we should be using the "$$.Map.Item.Index" to access each item of the
62
+ # mocked result list. This is turning out to be quite complicated, so instead just patch the
63
+ # StateMap's max concurrency decleration to always eval to '1' -- making the map run in serial.
64
+ def mock_max_concurrency(env: TestStateEnvironment) -> int:
65
+ # always set concurrency to 1 but inspection data is accurate to original
66
+ env.inspection_data["maxConcurrency"] = original_eval_max_concurrency(env)
67
+ return 1
68
+
69
+ self._wrapped._eval_execution = self.wrap_with_post_return(
70
+ method=original_eval_execution,
71
+ post_return_fn=self.add_inspection_data,
72
+ )
73
+
74
+ self._wrapped.max_concurrency_decl._eval_max_concurrency = mock_max_concurrency
75
+ self._wrapped.iteration_component._eval_body = self.wrap_with_mock(
76
+ original_iteration_component_eval_body
77
+ )
@@ -0,0 +1,44 @@
1
+ from localstack.services.stepfunctions.asl.component.common.query_language import (
2
+ QueryLanguageMode,
3
+ )
4
+ from localstack.services.stepfunctions.asl.component.state.state_execution.state_task.service.state_task_service import (
5
+ StateTaskService,
6
+ )
7
+ from localstack.services.stepfunctions.asl.component.state.state_execution.state_task.state_task import (
8
+ StateTask,
9
+ )
10
+ from localstack.services.stepfunctions.asl.component.test_state.state.base_mock import (
11
+ MockedBaseState,
12
+ )
13
+ from localstack.services.stepfunctions.asl.component.test_state.state.execution import (
14
+ MockedStateExecution,
15
+ )
16
+ from localstack.services.stepfunctions.asl.eval.test_state.environment import TestStateEnvironment
17
+ from localstack.services.stepfunctions.asl.utils.encoding import to_json_str
18
+
19
+
20
+ class MockedStateTask(MockedBaseState[StateTask]):
21
+ def add_inspection_data(self, env: TestStateEnvironment):
22
+ if self._wrapped.query_language.query_language_mode == QueryLanguageMode.JSONPath:
23
+ if "afterParameters" not in env.inspection_data:
24
+ env.inspection_data["afterParameters"] = to_json_str(env.states.get_input())
25
+
26
+ def _apply_patches(self):
27
+ self._wrapped = MockedStateExecution.wrap(self._wrapped)
28
+
29
+ if self._wrapped.query_language.query_language_mode == QueryLanguageMode.JSONPath:
30
+ self._eval_with_inspect(self._wrapped.parargs, "afterParameters")
31
+
32
+ if isinstance(self._wrapped, StateTaskService):
33
+ self._wrapped._eval_service_task = self.wrap_with_mock(self._wrapped._eval_service_task)
34
+
35
+ original_eval_execution = self._wrapped._eval_execution
36
+
37
+ def mock_eval_execution(env: TestStateEnvironment, *args, **kwargs):
38
+ original_eval_execution(env, *args, **kwargs)
39
+ result = to_json_str(env.stack[-1])
40
+ env.inspection_data["result"] = result
41
+
42
+ self._wrapped._eval_execution = self.wrap_with_post_return(
43
+ mock_eval_execution, self.add_inspection_data
44
+ )
@@ -3,7 +3,7 @@ from __future__ import annotations
3
3
  import copy
4
4
  import logging
5
5
  import threading
6
- from typing import Any, Final, Optional
6
+ from typing import Any, Final, Optional, Self
7
7
 
8
8
  from localstack.aws.api.stepfunctions import (
9
9
  Arn,
@@ -34,7 +34,10 @@ from localstack.services.stepfunctions.asl.eval.program_state import (
34
34
  from localstack.services.stepfunctions.asl.eval.states import ContextObjectData, States
35
35
  from localstack.services.stepfunctions.asl.eval.variable_store import VariableStore
36
36
  from localstack.services.stepfunctions.backend.activity import Activity
37
- from localstack.services.stepfunctions.mocking.mock_config import MockedResponse, MockTestCase
37
+ from localstack.services.stepfunctions.local_mocking.mock_config import (
38
+ LocalMockedResponse,
39
+ LocalMockTestCase,
40
+ )
38
41
 
39
42
  LOG = logging.getLogger(__name__)
40
43
 
@@ -52,7 +55,7 @@ class Environment:
52
55
  callback_pool_manager: CallbackPoolManager
53
56
  map_run_record_pool_manager: MapRunRecordPoolManager
54
57
  activity_store: Final[dict[Arn, Activity]]
55
- mock_test_case: MockTestCase | None = None
58
+ local_mock_test_case: LocalMockTestCase | None = None
56
59
 
57
60
  _frames: Final[list[Environment]]
58
61
  _is_frame: bool = False
@@ -71,7 +74,7 @@ class Environment:
71
74
  cloud_watch_logging_session: CloudWatchLoggingSession | None,
72
75
  activity_store: dict[Arn, Activity],
73
76
  variable_store: VariableStore | None = None,
74
- mock_test_case: MockTestCase | None = None,
77
+ local_mock_test_case: LocalMockTestCase | None = None,
75
78
  ):
76
79
  super().__init__()
77
80
  self._state_mutex = threading.RLock()
@@ -89,7 +92,7 @@ class Environment:
89
92
 
90
93
  self.activity_store = activity_store
91
94
 
92
- self.mock_test_case = mock_test_case
95
+ self.local_mock_test_case = local_mock_test_case
93
96
 
94
97
  self._frames = []
95
98
  self._is_frame = False
@@ -101,9 +104,9 @@ class Environment:
101
104
 
102
105
  @classmethod
103
106
  def as_frame_of(
104
- cls, env: Environment, event_history_frame_cache: EventHistoryContext | None = None
105
- ) -> Environment:
106
- return Environment.as_inner_frame_of(
107
+ cls, env: Self, event_history_frame_cache: EventHistoryContext | None = None
108
+ ) -> Self:
109
+ return cls.as_inner_frame_of(
107
110
  env=env,
108
111
  variable_store=env.variable_store,
109
112
  event_history_frame_cache=event_history_frame_cache,
@@ -112,10 +115,10 @@ class Environment:
112
115
  @classmethod
113
116
  def as_inner_frame_of(
114
117
  cls,
115
- env: Environment,
118
+ env: Self,
116
119
  variable_store: VariableStore,
117
120
  event_history_frame_cache: EventHistoryContext | None = None,
118
- ) -> Environment:
121
+ ) -> Self:
119
122
  # Construct the frame's context object data.
120
123
  context = ContextObjectData(
121
124
  Execution=env.states.context_object.context_object_data["Execution"],
@@ -138,8 +141,8 @@ class Environment:
138
141
  cloud_watch_logging_session=env.cloud_watch_logging_session,
139
142
  activity_store=env.activity_store,
140
143
  variable_store=variable_store,
141
- mock_test_case=env.mock_test_case,
142
144
  )
145
+ frame.local_mock_test_case = env.local_mock_test_case
143
146
  frame._is_frame = True
144
147
  frame.event_manager = env.event_manager
145
148
  if "State" in env.states.context_object.context_object_data:
@@ -267,32 +270,37 @@ class Environment:
267
270
  def is_standard_workflow(self) -> bool:
268
271
  return self.execution_type == StateMachineType.STANDARD
269
272
 
270
- def is_mocked_mode(self) -> bool:
273
+ def is_test_state_mocked_mode(self) -> bool:
274
+ return False
275
+
276
+ def is_local_mocked_mode(self) -> bool:
271
277
  """
272
- Returns True if the state machine is running in mock mode and the current
273
- state has a defined mock configuration in the target environment or frame;
274
- otherwise, returns False.
278
+ Returns True if:
279
+ - the state machine is running in Step Functions Local mode
280
+ - the current state has a defined Local mock configuration in the target environment or frame
281
+
282
+ Otherwise, returns False.
275
283
  """
276
284
  return (
277
- self.mock_test_case is not None
278
- and self.next_state_name in self.mock_test_case.state_mocked_responses
285
+ self.local_mock_test_case is not None
286
+ and self.next_state_name in self.local_mock_test_case.state_mocked_responses
279
287
  )
280
288
 
281
- def get_current_mocked_response(self) -> MockedResponse:
282
- if not self.is_mocked_mode():
289
+ def get_current_local_mocked_response(self) -> LocalMockedResponse:
290
+ if not self.is_local_mocked_mode():
283
291
  raise RuntimeError(
284
292
  "Cannot retrieve mocked response: execution is not operating in mocked mode"
285
293
  )
286
294
  state_name = self.next_state_name
287
- state_mocked_responses: Optional = self.mock_test_case.state_mocked_responses.get(
295
+ state_mocked_responses: Optional = self.local_mock_test_case.state_mocked_responses.get(
288
296
  state_name
289
297
  )
290
298
  if state_mocked_responses is None:
291
- raise RuntimeError(f"No mocked response definition for state '{state_name}'")
299
+ raise RuntimeError(f"No Local mocked response definition for state '{state_name}'")
292
300
  retry_count = self.states.context_object.context_object_data["State"]["RetryCount"]
293
301
  if len(state_mocked_responses.mocked_responses) <= retry_count:
294
302
  raise RuntimeError(
295
- f"No mocked response definition for state '{state_name}' "
303
+ f"No Local mocked response definition for state '{state_name}' "
296
304
  f"and retry number '{retry_count}'"
297
305
  )
298
306
  return state_mocked_responses.mocked_responses[retry_count]
@@ -82,7 +82,7 @@ class States:
82
82
  context_object: Final[ContextObject]
83
83
 
84
84
  def __init__(self, context: ContextObjectData):
85
- input_value = context["Execution"]["Input"]
85
+ input_value = context.get("Execution", {}).get("Input", {})
86
86
  self._states_data = StatesData(input=input_value, context=context)
87
87
  self.context_object = ContextObject(context_object=context)
88
88
 
@@ -1,5 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
+ from typing import Self
4
+
3
5
  from localstack.aws.api.stepfunctions import Arn, InspectionData, StateMachineType
4
6
  from localstack.services.stepfunctions.asl.eval.environment import Environment
5
7
  from localstack.services.stepfunctions.asl.eval.evaluation_details import AWSExecutionDetails
@@ -14,14 +16,18 @@ from localstack.services.stepfunctions.asl.eval.program_state import (
14
16
  )
15
17
  from localstack.services.stepfunctions.asl.eval.states import ContextObjectData
16
18
  from localstack.services.stepfunctions.asl.eval.test_state.program_state import (
19
+ ProgramCaughtError,
17
20
  ProgramChoiceSelected,
21
+ ProgramRetriable,
18
22
  )
19
23
  from localstack.services.stepfunctions.asl.eval.variable_store import VariableStore
20
24
  from localstack.services.stepfunctions.backend.activity import Activity
25
+ from localstack.services.stepfunctions.backend.test_state.test_state_mock import TestStateMock
21
26
 
22
27
 
23
28
  class TestStateEnvironment(Environment):
24
29
  inspection_data: InspectionData
30
+ mock: TestStateMock
25
31
 
26
32
  def __init__(
27
33
  self,
@@ -31,6 +37,8 @@ class TestStateEnvironment(Environment):
31
37
  event_history_context: EventHistoryContext,
32
38
  activity_store: dict[Arn, Activity],
33
39
  cloud_watch_logging_session: CloudWatchLoggingSession | None = None,
40
+ variable_store: VariableStore | None = None,
41
+ mock: TestStateMock | None = None,
34
42
  ):
35
43
  super().__init__(
36
44
  aws_execution_details=aws_execution_details,
@@ -39,30 +47,43 @@ class TestStateEnvironment(Environment):
39
47
  event_history_context=event_history_context,
40
48
  cloud_watch_logging_session=cloud_watch_logging_session,
41
49
  activity_store=activity_store,
50
+ variable_store=variable_store,
42
51
  )
43
52
  self.inspection_data = InspectionData()
53
+ self.mock = mock
54
+
55
+ def is_test_state_mocked_mode(self) -> bool:
56
+ return self.mock.is_mocked()
44
57
 
58
+ @classmethod
45
59
  def as_frame_of(
46
60
  cls,
47
- env: TestStateEnvironment,
61
+ env: Self,
48
62
  event_history_frame_cache: EventHistoryContext | None = None,
49
- ) -> Environment:
50
- frame = super().as_frame_of(env=env, event_history_frame_cache=event_history_frame_cache)
51
- frame.inspection_data = env.inspection_data
52
- return frame
63
+ ) -> Self:
64
+ if (mocked_context := env.mock.get_context()) is not None:
65
+ env.states.context_object.context_object_data = mocked_context
53
66
 
67
+ return cls.as_inner_frame_of(
68
+ env=env,
69
+ variable_store=env.variable_store,
70
+ event_history_frame_cache=event_history_frame_cache,
71
+ )
72
+
73
+ @classmethod
54
74
  def as_inner_frame_of(
55
75
  cls,
56
- env: TestStateEnvironment,
76
+ env: Self,
57
77
  variable_store: VariableStore,
58
78
  event_history_frame_cache: EventHistoryContext | None = None,
59
- ) -> Environment:
79
+ ) -> Self:
60
80
  frame = super().as_inner_frame_of(
61
81
  env=env,
62
82
  event_history_frame_cache=event_history_frame_cache,
63
83
  variable_store=variable_store,
64
84
  )
65
85
  frame.inspection_data = env.inspection_data
86
+ frame.mock = env.mock
66
87
  return frame
67
88
 
68
89
  def set_choice_selected(self, next_state_name: str) -> None:
@@ -71,5 +92,24 @@ class TestStateEnvironment(Environment):
71
92
  self._program_state = ProgramChoiceSelected(next_state_name=next_state_name)
72
93
  self.program_state_event.set()
73
94
  self.program_state_event.clear()
74
- else:
75
- raise RuntimeError("Cannot set choice selected for non running ProgramState.")
95
+
96
+ def set_caught_error(self, next_state_name: str, error: str, cause: str) -> None:
97
+ with self._state_mutex:
98
+ if isinstance(self._program_state, ProgramRunning):
99
+ self._program_state = ProgramCaughtError(
100
+ next_state_name=next_state_name,
101
+ error=error,
102
+ cause=cause,
103
+ )
104
+ self.program_state_event.set()
105
+ self.program_state_event.clear()
106
+
107
+ def set_retriable_error(self, error: str, cause: str) -> None:
108
+ with self._state_mutex:
109
+ if isinstance(self._program_state, ProgramRunning):
110
+ self._program_state = ProgramRetriable(
111
+ error=error,
112
+ cause=cause,
113
+ )
114
+ self.program_state_event.set()
115
+ self.program_state_event.clear()
@@ -9,3 +9,25 @@ class ProgramChoiceSelected(ProgramState):
9
9
  def __init__(self, next_state_name: str):
10
10
  super().__init__()
11
11
  self.next_state_name = next_state_name
12
+
13
+
14
+ class ProgramCaughtError(ProgramState):
15
+ next_state_name: Final[str]
16
+ error: Final[str]
17
+ cause: Final[str]
18
+
19
+ def __init__(self, next_state_name: str, error: str, cause: str):
20
+ super().__init__()
21
+ self.next_state_name = next_state_name
22
+ self.error = error
23
+ self.cause = cause
24
+
25
+
26
+ class ProgramRetriable(ProgramState):
27
+ error: Final[str]
28
+ cause: Final[str]
29
+
30
+ def __init__(self, error: str, cause: str):
31
+ super().__init__()
32
+ self.error = error
33
+ self.cause = cause
@@ -33,7 +33,11 @@ _PATTERN_VARIABLE_REFERENCE = re.compile(
33
33
  # allowing escapes
34
34
  r"(?:\"(?:\\.|[^\"\\])*\"|\'(?:\\.|[^\'\\])*\')"
35
35
  r"|"
36
- # 3) Capturing branch for $$, $identifier[.prop…], or lone $
36
+ # 3) Non-capturing branch for bracket expressions [...]
37
+ # Consume these to avoid capturing $ inside them
38
+ r"(?:\[[^\[\]]*\])"
39
+ r"|"
40
+ # 4) Capturing branch for $$, $identifier[.prop…], or lone $
37
41
  r"(\$\$|\$[A-Za-z0-9_$]+(?:\.[A-Za-z0-9_][A-Za-z0-9_$]*)*|\$)"
38
42
  )
39
43