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.
- localstack/aws/api/cloudformation/__init__.py +18 -4
- localstack/aws/api/cloudwatch/__init__.py +41 -1
- localstack/aws/api/config/__init__.py +4 -0
- localstack/aws/api/core.py +6 -2
- localstack/aws/api/dynamodb/__init__.py +30 -0
- localstack/aws/api/ec2/__init__.py +1522 -65
- localstack/aws/api/iam/__init__.py +7 -0
- localstack/aws/api/kinesis/__init__.py +19 -0
- localstack/aws/api/kms/__init__.py +6 -0
- localstack/aws/api/lambda_/__init__.py +13 -0
- localstack/aws/api/logs/__init__.py +15 -0
- localstack/aws/api/redshift/__init__.py +9 -3
- localstack/aws/api/route53/__init__.py +5 -0
- localstack/aws/api/s3/__init__.py +12 -0
- localstack/aws/api/s3control/__init__.py +54 -0
- localstack/aws/api/ssm/__init__.py +2 -0
- localstack/aws/api/transcribe/__init__.py +17 -0
- localstack/aws/client.py +7 -2
- localstack/aws/forwarder.py +52 -5
- localstack/aws/handlers/analytics.py +1 -1
- localstack/aws/handlers/internal_requests.py +6 -1
- localstack/aws/handlers/logging.py +12 -2
- localstack/aws/handlers/metric_handler.py +41 -1
- localstack/aws/handlers/service.py +40 -20
- localstack/aws/mocking.py +2 -2
- localstack/aws/patches.py +2 -2
- localstack/aws/protocol/parser.py +459 -32
- localstack/aws/protocol/serializer.py +689 -69
- localstack/aws/protocol/service_router.py +120 -20
- localstack/aws/protocol/validate.py +1 -1
- localstack/aws/scaffold.py +1 -1
- localstack/aws/skeleton.py +4 -2
- localstack/aws/spec-patches.json +58 -0
- localstack/aws/spec.py +37 -16
- localstack/cli/exceptions.py +1 -1
- localstack/cli/localstack.py +6 -6
- localstack/cli/lpm.py +3 -4
- localstack/cli/plugins.py +1 -1
- localstack/cli/profiles.py +1 -2
- localstack/config.py +25 -18
- localstack/constants.py +4 -29
- localstack/dev/kubernetes/__main__.py +130 -7
- localstack/dev/run/configurators.py +1 -4
- localstack/dev/run/paths.py +1 -1
- localstack/dns/plugins.py +5 -1
- localstack/dns/server.py +13 -4
- localstack/logging/format.py +3 -3
- localstack/packages/api.py +9 -8
- localstack/packages/core.py +2 -2
- localstack/packages/plugins.py +0 -8
- localstack/runtime/analytics.py +3 -0
- localstack/runtime/hooks.py +1 -1
- localstack/runtime/init.py +2 -2
- localstack/runtime/main.py +5 -5
- localstack/runtime/patches.py +2 -2
- localstack/services/apigateway/helpers.py +1 -4
- localstack/services/apigateway/legacy/helpers.py +7 -8
- localstack/services/apigateway/legacy/integration.py +4 -3
- localstack/services/apigateway/legacy/invocations.py +6 -5
- localstack/services/apigateway/legacy/provider.py +148 -68
- localstack/services/apigateway/legacy/templates.py +1 -1
- localstack/services/apigateway/next_gen/execute_api/handlers/method_request.py +7 -2
- localstack/services/apigateway/next_gen/execute_api/handlers/resource_router.py +1 -2
- localstack/services/apigateway/next_gen/execute_api/integrations/aws.py +3 -0
- localstack/services/apigateway/next_gen/execute_api/integrations/http.py +3 -3
- localstack/services/apigateway/next_gen/execute_api/template_mapping.py +2 -2
- localstack/services/apigateway/next_gen/execute_api/test_invoke.py +114 -9
- localstack/services/apigateway/next_gen/provider.py +5 -0
- localstack/services/apigateway/resource_providers/aws_apigateway_resource.py +1 -1
- localstack/services/cloudformation/api_utils.py +4 -8
- localstack/services/cloudformation/cfn_utils.py +1 -1
- localstack/services/cloudformation/engine/entities.py +14 -4
- localstack/services/cloudformation/engine/template_deployer.py +6 -4
- localstack/services/cloudformation/engine/transformers.py +6 -4
- localstack/services/cloudformation/engine/v2/change_set_model.py +201 -13
- localstack/services/cloudformation/engine/v2/change_set_model_describer.py +52 -3
- localstack/services/cloudformation/engine/v2/change_set_model_executor.py +117 -76
- localstack/services/cloudformation/engine/v2/change_set_model_preproc.py +205 -52
- localstack/services/cloudformation/engine/v2/change_set_model_transform.py +350 -116
- localstack/services/cloudformation/engine/v2/change_set_model_validator.py +56 -14
- localstack/services/cloudformation/engine/v2/change_set_model_visitor.py +1 -0
- localstack/services/cloudformation/engine/v2/resolving.py +7 -5
- localstack/services/cloudformation/engine/yaml_parser.py +9 -2
- localstack/services/cloudformation/provider.py +7 -5
- localstack/services/cloudformation/resource_provider.py +7 -1
- localstack/services/cloudformation/resources.py +24149 -0
- localstack/services/cloudformation/service_models.py +2 -2
- localstack/services/cloudformation/v2/entities.py +19 -9
- localstack/services/cloudformation/v2/provider.py +336 -106
- localstack/services/cloudformation/v2/types.py +13 -7
- localstack/services/cloudformation/v2/utils.py +4 -1
- localstack/services/cloudwatch/alarm_scheduler.py +4 -1
- localstack/services/cloudwatch/provider.py +18 -13
- localstack/services/cloudwatch/provider_v2.py +25 -28
- localstack/services/dynamodb/packages.py +2 -1
- localstack/services/dynamodb/provider.py +42 -0
- localstack/services/dynamodb/server.py +2 -2
- localstack/services/dynamodb/v2/provider.py +42 -0
- localstack/services/ecr/resource_providers/aws_ecr_repository.py +5 -2
- localstack/services/edge.py +1 -1
- localstack/services/es/provider.py +2 -2
- localstack/services/events/event_rule_engine.py +31 -13
- localstack/services/events/models.py +4 -5
- localstack/services/events/provider.py +17 -14
- localstack/services/events/target.py +17 -9
- localstack/services/events/v1/provider.py +5 -5
- localstack/services/firehose/provider.py +14 -4
- localstack/services/iam/provider.py +11 -116
- localstack/services/iam/resources/policy_simulator.py +133 -0
- localstack/services/kinesis/models.py +15 -2
- localstack/services/kinesis/provider.py +86 -3
- localstack/services/kms/provider.py +14 -5
- localstack/services/lambda_/api_utils.py +6 -3
- localstack/services/lambda_/invocation/docker_runtime_executor.py +1 -1
- localstack/services/lambda_/invocation/event_manager.py +1 -1
- localstack/services/lambda_/invocation/internal_sqs_queue.py +5 -9
- localstack/services/lambda_/invocation/lambda_models.py +10 -7
- localstack/services/lambda_/invocation/lambda_service.py +5 -1
- localstack/services/lambda_/packages.py +1 -1
- localstack/services/lambda_/provider.py +4 -3
- localstack/services/lambda_/provider_utils.py +1 -1
- localstack/services/logs/provider.py +36 -19
- localstack/services/moto.py +2 -1
- localstack/services/opensearch/cluster.py +15 -7
- localstack/services/opensearch/packages.py +26 -7
- localstack/services/opensearch/provider.py +8 -2
- localstack/services/opensearch/versions.py +56 -7
- localstack/services/plugins.py +11 -7
- localstack/services/providers.py +10 -2
- localstack/services/redshift/provider.py +0 -21
- localstack/services/s3/constants.py +5 -2
- localstack/services/s3/cors.py +4 -4
- localstack/services/s3/models.py +1 -1
- localstack/services/s3/notifications.py +55 -39
- localstack/services/s3/presigned_url.py +35 -54
- localstack/services/s3/provider.py +73 -15
- localstack/services/s3/utils.py +42 -22
- localstack/services/s3/validation.py +46 -32
- localstack/services/s3/website_hosting.py +4 -2
- localstack/services/ses/provider.py +18 -8
- localstack/services/sns/constants.py +7 -1
- localstack/services/sns/executor.py +9 -2
- localstack/services/sns/provider.py +8 -5
- localstack/services/sns/publisher.py +31 -16
- localstack/services/sns/v2/models.py +167 -0
- localstack/services/sns/v2/provider.py +867 -0
- localstack/services/sns/v2/utils.py +130 -0
- localstack/services/sqs/constants.py +1 -1
- localstack/services/sqs/developer_api.py +205 -0
- localstack/services/sqs/models.py +48 -5
- localstack/services/sqs/provider.py +38 -311
- localstack/services/sqs/query_api.py +6 -2
- localstack/services/sqs/utils.py +121 -2
- localstack/services/ssm/provider.py +1 -1
- localstack/services/stepfunctions/asl/component/intrinsic/member.py +1 -1
- localstack/services/stepfunctions/asl/component/state/state_choice/comparison/comparison.py +5 -11
- localstack/services/stepfunctions/asl/component/state/state_choice/state_choice.py +2 -2
- localstack/services/stepfunctions/asl/component/state/state_execution/state_map/state_map.py +2 -2
- localstack/services/stepfunctions/asl/component/state/state_execution/state_parallel/state_parallel.py +1 -1
- localstack/services/stepfunctions/asl/component/state/state_execution/state_task/state_task.py +2 -2
- localstack/services/stepfunctions/asl/component/state/state_fail/state_fail.py +1 -1
- localstack/services/stepfunctions/asl/component/state/state_pass/state_pass.py +2 -2
- localstack/services/stepfunctions/asl/component/state/state_succeed/state_succeed.py +1 -1
- localstack/services/stepfunctions/asl/component/state/state_wait/state_wait.py +1 -1
- localstack/services/stepfunctions/asl/eval/environment.py +1 -1
- localstack/services/stepfunctions/asl/jsonata/jsonata.py +1 -1
- localstack/services/stepfunctions/backend/execution.py +2 -1
- localstack/services/stores.py +1 -1
- localstack/services/transcribe/provider.py +6 -1
- localstack/state/codecs.py +61 -0
- localstack/state/core.py +11 -5
- localstack/state/pickle.py +10 -49
- localstack/testing/aws/cloudformation_utils.py +1 -1
- localstack/testing/pytest/cloudformation/fixtures.py +3 -3
- localstack/testing/pytest/cloudformation/transformers.py +0 -0
- localstack/testing/pytest/container.py +4 -5
- localstack/testing/pytest/fixtures.py +33 -31
- localstack/testing/pytest/in_memory_localstack.py +0 -4
- localstack/testing/pytest/marking.py +38 -11
- localstack/testing/pytest/stepfunctions/utils.py +4 -3
- localstack/testing/pytest/util.py +1 -1
- localstack/testing/pytest/validation_tracking.py +1 -2
- localstack/testing/snapshots/transformer_utility.py +6 -1
- localstack/utils/analytics/events.py +2 -2
- localstack/utils/analytics/metadata.py +6 -4
- localstack/utils/analytics/metrics/counter.py +8 -15
- localstack/utils/analytics/publisher.py +1 -2
- localstack/utils/analytics/service_providers.py +19 -0
- localstack/utils/analytics/service_request_aggregator.py +2 -2
- localstack/utils/archives.py +11 -11
- localstack/utils/asyncio.py +2 -2
- localstack/utils/aws/arns.py +24 -29
- localstack/utils/aws/aws_responses.py +8 -8
- localstack/utils/aws/aws_stack.py +2 -3
- localstack/utils/aws/dead_letter_queue.py +1 -5
- localstack/utils/aws/message_forwarding.py +1 -2
- localstack/utils/aws/request_context.py +4 -5
- localstack/utils/aws/resources.py +1 -1
- localstack/utils/aws/templating.py +1 -1
- localstack/utils/batch_policy.py +3 -3
- localstack/utils/bootstrap.py +21 -13
- localstack/utils/catalog/catalog.py +139 -0
- localstack/utils/catalog/catalog_loader.py +119 -0
- localstack/utils/catalog/common.py +58 -0
- localstack/utils/catalog/plugins.py +28 -0
- localstack/utils/cloudwatch/cloudwatch_util.py +5 -5
- localstack/utils/collections.py +7 -8
- localstack/utils/config_listener.py +1 -1
- localstack/utils/container_networking.py +2 -3
- localstack/utils/container_utils/container_client.py +135 -136
- localstack/utils/container_utils/docker_cmd_client.py +85 -69
- localstack/utils/container_utils/docker_sdk_client.py +69 -66
- localstack/utils/crypto.py +10 -10
- localstack/utils/diagnose.py +3 -4
- localstack/utils/docker_utils.py +9 -5
- localstack/utils/files.py +33 -13
- localstack/utils/functions.py +4 -3
- localstack/utils/http.py +11 -11
- localstack/utils/json.py +20 -6
- localstack/utils/kinesis/kinesis_connector.py +2 -1
- localstack/utils/net.py +15 -9
- localstack/utils/no_exit_argument_parser.py +2 -2
- localstack/utils/numbers.py +9 -2
- localstack/utils/objects.py +7 -6
- localstack/utils/patch.py +10 -3
- localstack/utils/run.py +12 -11
- localstack/utils/scheduler.py +11 -11
- localstack/utils/server/tcp_proxy.py +2 -2
- localstack/utils/serving.py +3 -4
- localstack/utils/strings.py +15 -16
- localstack/utils/sync.py +126 -1
- localstack/utils/tagging.py +8 -6
- localstack/utils/testutil.py +8 -8
- localstack/utils/threads.py +2 -2
- localstack/utils/time.py +12 -4
- localstack/utils/urls.py +1 -3
- localstack/utils/xray/traceid.py +1 -1
- localstack/version.py +16 -3
- {localstack_core-4.7.1.dev49.dist-info → localstack_core-4.10.1.dev12.dist-info}/METADATA +18 -14
- {localstack_core-4.7.1.dev49.dist-info → localstack_core-4.10.1.dev12.dist-info}/RECORD +248 -239
- {localstack_core-4.7.1.dev49.dist-info → localstack_core-4.10.1.dev12.dist-info}/entry_points.txt +8 -4
- localstack_core-4.10.1.dev12.dist-info/plux.json +1 -0
- localstack/packages/terraform.py +0 -46
- localstack/services/cloudformation/deploy.html +0 -144
- localstack/services/cloudformation/deploy_ui.py +0 -47
- localstack/services/cloudformation/plugins.py +0 -12
- localstack_core-4.7.1.dev49.dist-info/plux.json +0 -1
- {localstack_core-4.7.1.dev49.data → localstack_core-4.10.1.dev12.data}/scripts/localstack +0 -0
- {localstack_core-4.7.1.dev49.data → localstack_core-4.10.1.dev12.data}/scripts/localstack-supervisor +0 -0
- {localstack_core-4.7.1.dev49.data → localstack_core-4.10.1.dev12.data}/scripts/localstack.bat +0 -0
- {localstack_core-4.7.1.dev49.dist-info → localstack_core-4.10.1.dev12.dist-info}/WHEEL +0 -0
- {localstack_core-4.7.1.dev49.dist-info → localstack_core-4.10.1.dev12.dist-info}/licenses/LICENSE.txt +0 -0
- {localstack_core-4.7.1.dev49.dist-info → localstack_core-4.10.1.dev12.dist-info}/top_level.txt +0 -0
|
@@ -3,7 +3,8 @@ Custom pytest mark typings
|
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
5
|
import os
|
|
6
|
-
from
|
|
6
|
+
from collections.abc import Callable
|
|
7
|
+
from typing import TYPE_CHECKING
|
|
7
8
|
|
|
8
9
|
import pytest
|
|
9
10
|
from _pytest.config import PytestPluginManager
|
|
@@ -36,13 +37,13 @@ class SkipSnapshotVerifyMarker:
|
|
|
36
37
|
def __call__(
|
|
37
38
|
self,
|
|
38
39
|
*,
|
|
39
|
-
paths: "
|
|
40
|
-
condition: "
|
|
40
|
+
paths: "list[str] | None" = None,
|
|
41
|
+
condition: "Callable[[...], bool] | None" = None,
|
|
41
42
|
): ...
|
|
42
43
|
|
|
43
44
|
|
|
44
45
|
class MultiRuntimeMarker:
|
|
45
|
-
def __call__(self, *, scenario: str, runtimes:
|
|
46
|
+
def __call__(self, *, scenario: str, runtimes: list[str] | None = None): ...
|
|
46
47
|
|
|
47
48
|
|
|
48
49
|
class SnapshotMarkers:
|
|
@@ -58,13 +59,27 @@ class Markers:
|
|
|
58
59
|
|
|
59
60
|
# test selection
|
|
60
61
|
acceptance_test = pytest.mark.acceptance_test
|
|
62
|
+
"""This test is an acceptance test"""
|
|
61
63
|
skip_offline = pytest.mark.skip_offline
|
|
64
|
+
"""Test is skipped if offline, as it requires some sort of internet connection to run"""
|
|
62
65
|
only_on_amd64 = pytest.mark.only_on_amd64
|
|
66
|
+
"""Test requires ability of the system to execute amd64 binaries"""
|
|
63
67
|
only_on_arm64 = pytest.mark.only_on_arm64
|
|
68
|
+
"""Test requires ability of the system to execute arm64 binaries"""
|
|
64
69
|
resource_heavy = pytest.mark.resource_heavy
|
|
65
|
-
|
|
66
|
-
|
|
70
|
+
"""Test is very resource heavy, and might be skipped in CI"""
|
|
71
|
+
requires_in_container = pytest.mark.requires_in_container
|
|
72
|
+
"""Test requires LocalStack to run inside a container"""
|
|
73
|
+
requires_in_process = pytest.mark.requires_in_process
|
|
74
|
+
"""The test and the LS instance have to be run in the same process"""
|
|
75
|
+
requires_docker = pytest.mark.requires_docker
|
|
76
|
+
"""The test requires docker or a compatible container engine - will not work on kubernetes"""
|
|
67
77
|
lambda_runtime_update = pytest.mark.lambda_runtime_update
|
|
78
|
+
"""Tests to execute when updating snapshots for a new Lambda runtime"""
|
|
79
|
+
k8s_always_run = pytest.mark.k8s_always_run
|
|
80
|
+
"""This tests will always run against k8s environment"""
|
|
81
|
+
skip_k8s = pytest.mark.skip_k8s
|
|
82
|
+
"""This test will be skipped in k8s environment"""
|
|
68
83
|
|
|
69
84
|
|
|
70
85
|
# pytest plugin
|
|
@@ -131,8 +146,8 @@ def filter_by_markers(config: "Config", items: list[pytest.Item]):
|
|
|
131
146
|
"Add network connectivity and remove the --offline option when running "
|
|
132
147
|
"the test."
|
|
133
148
|
)
|
|
134
|
-
|
|
135
|
-
reason="Test requires execution inside
|
|
149
|
+
requires_in_container = pytest.mark.skip(
|
|
150
|
+
reason="Test requires execution inside a container (e.g., to install system packages)"
|
|
136
151
|
)
|
|
137
152
|
only_on_amd64 = pytest.mark.skip(
|
|
138
153
|
reason="Test uses features that are currently only supported for AMD64. Skipping in CI."
|
|
@@ -144,8 +159,8 @@ def filter_by_markers(config: "Config", items: list[pytest.Item]):
|
|
|
144
159
|
for item in items:
|
|
145
160
|
if is_offline and "skip_offline" in item.keywords:
|
|
146
161
|
item.add_marker(skip_offline)
|
|
147
|
-
if not is_in_docker and "
|
|
148
|
-
item.add_marker(
|
|
162
|
+
if not is_in_docker and "requires_in_container" in item.keywords:
|
|
163
|
+
item.add_marker(requires_in_container)
|
|
149
164
|
if is_in_ci and not is_amd64 and "only_on_amd64" in item.keywords:
|
|
150
165
|
item.add_marker(only_on_amd64)
|
|
151
166
|
if is_in_ci and not is_arm64 and "only_on_arm64" in item.keywords:
|
|
@@ -177,7 +192,7 @@ def pytest_configure(config):
|
|
|
177
192
|
)
|
|
178
193
|
config.addinivalue_line(
|
|
179
194
|
"markers",
|
|
180
|
-
"
|
|
195
|
+
"requires_in_container: mark the test as running only in a container (e.g., requires installation of system packages)",
|
|
181
196
|
)
|
|
182
197
|
config.addinivalue_line(
|
|
183
198
|
"markers",
|
|
@@ -208,3 +223,15 @@ def pytest_configure(config):
|
|
|
208
223
|
"markers",
|
|
209
224
|
"multiruntime: parametrize test against multiple Lambda runtimes",
|
|
210
225
|
)
|
|
226
|
+
config.addinivalue_line(
|
|
227
|
+
"markers",
|
|
228
|
+
"requires_docker: mark the test as requiring docker (or a compatible container engine) - will not work on kubernetes.",
|
|
229
|
+
)
|
|
230
|
+
config.addinivalue_line(
|
|
231
|
+
"markers",
|
|
232
|
+
"requires_in_process: mark the test as requiring the test to run inside the same process as LocalStack - will not work if tests are run against a running LS container.",
|
|
233
|
+
)
|
|
234
|
+
config.addinivalue_line(
|
|
235
|
+
"markers",
|
|
236
|
+
"k8s_always_run: mark the test to always run in k8s environment. This allows us to run tests that would otherwise be skipped, such as localstack_only tests.",
|
|
237
|
+
)
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import json
|
|
2
2
|
import logging
|
|
3
|
-
from
|
|
3
|
+
from collections.abc import Callable
|
|
4
|
+
from typing import Final
|
|
4
5
|
|
|
5
6
|
from botocore.exceptions import ClientError
|
|
6
7
|
from jsonpath_ng.ext import parse
|
|
@@ -402,8 +403,8 @@ def create_state_machine_with_iam_role(
|
|
|
402
403
|
create_state_machine,
|
|
403
404
|
snapshot,
|
|
404
405
|
definition: Definition,
|
|
405
|
-
logging_configuration:
|
|
406
|
-
state_machine_name:
|
|
406
|
+
logging_configuration: LoggingConfiguration | None = None,
|
|
407
|
+
state_machine_name: str | None = None,
|
|
407
408
|
state_machine_type: StateMachineType = StateMachineType.STANDARD,
|
|
408
409
|
):
|
|
409
410
|
snf_role_arn = create_state_machine_iam_role(target_aws_client=target_aws_client)
|
|
@@ -9,7 +9,6 @@ import datetime
|
|
|
9
9
|
import json
|
|
10
10
|
import os
|
|
11
11
|
from pathlib import Path
|
|
12
|
-
from typing import Optional
|
|
13
12
|
|
|
14
13
|
import pytest
|
|
15
14
|
from pluggy import Result
|
|
@@ -28,7 +27,7 @@ Stores information from call execution phase about whether the test failed.
|
|
|
28
27
|
"""
|
|
29
28
|
|
|
30
29
|
|
|
31
|
-
def find_validation_data_for_item(item: pytest.Item) ->
|
|
30
|
+
def find_validation_data_for_item(item: pytest.Item) -> dict | None:
|
|
32
31
|
base_path = os.path.join(item.fspath.dirname, item.fspath.purebasename)
|
|
33
32
|
snapshot_path = f"{base_path}.validation.json"
|
|
34
33
|
|
|
@@ -45,6 +45,10 @@ PATTERN_KEY_ARN = re.compile(
|
|
|
45
45
|
r"arn:(aws[a-zA-Z-]*)?:([a-zA-Z0-9-_.]+)?:([^:]+)?:(\d{12})?:key/[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}"
|
|
46
46
|
)
|
|
47
47
|
|
|
48
|
+
PATTERN_MRK_KEY_ARN = re.compile(
|
|
49
|
+
r"arn:(aws[a-zA-Z-]*)?:([a-zA-Z0-9-_.]+)?:([^:]+)?:(\d{12})?:key/mrk-[a-fA-F0-9]{32}"
|
|
50
|
+
)
|
|
51
|
+
|
|
48
52
|
|
|
49
53
|
# TODO: split into generic/aws and put into lib
|
|
50
54
|
class TransformerUtility:
|
|
@@ -500,7 +504,7 @@ class TransformerUtility:
|
|
|
500
504
|
replacement="<stream-name>",
|
|
501
505
|
),
|
|
502
506
|
TransformerUtility.key_value(
|
|
503
|
-
"ContinuationSequenceNumber", "
|
|
507
|
+
"ContinuationSequenceNumber", "continuation_sequence_number"
|
|
504
508
|
),
|
|
505
509
|
]
|
|
506
510
|
|
|
@@ -573,6 +577,7 @@ class TransformerUtility:
|
|
|
573
577
|
TransformerUtility.key_value("CiphertextBlob", reference_replacement=False),
|
|
574
578
|
TransformerUtility.key_value("Plaintext", reference_replacement=False),
|
|
575
579
|
RegexTransformer(PATTERN_KEY_ARN, replacement="<key-arn>"),
|
|
580
|
+
RegexTransformer(PATTERN_MRK_KEY_ARN, replacement="<mrk-key-arn>"),
|
|
576
581
|
]
|
|
577
582
|
|
|
578
583
|
@staticmethod
|
|
@@ -2,7 +2,6 @@ import dataclasses
|
|
|
2
2
|
import logging
|
|
3
3
|
import os
|
|
4
4
|
import platform
|
|
5
|
-
from typing import Optional
|
|
6
5
|
|
|
7
6
|
from localstack import config
|
|
8
7
|
from localstack.constants import VERSION
|
|
@@ -41,7 +40,7 @@ class ClientMetadata:
|
|
|
41
40
|
k = "*" * len(k)
|
|
42
41
|
d["api_key"] = k
|
|
43
42
|
|
|
44
|
-
return "ClientMetadata(
|
|
43
|
+
return f"ClientMetadata({d})"
|
|
45
44
|
|
|
46
45
|
|
|
47
46
|
def get_version_string() -> str:
|
|
@@ -148,7 +147,10 @@ def is_license_activated() -> bool:
|
|
|
148
147
|
|
|
149
148
|
return licensingv2.get_licensed_environment().activated
|
|
150
149
|
except Exception:
|
|
151
|
-
LOG.
|
|
150
|
+
LOG.error(
|
|
151
|
+
"Could not determine license activation status",
|
|
152
|
+
exc_info=LOG.isEnabledFor(logging.DEBUG),
|
|
153
|
+
)
|
|
152
154
|
return False
|
|
153
155
|
|
|
154
156
|
|
|
@@ -198,7 +200,7 @@ def _generate_machine_id() -> str:
|
|
|
198
200
|
return f"gen_{long_uid()[:12]}"
|
|
199
201
|
|
|
200
202
|
|
|
201
|
-
def get_api_key_or_auth_token() ->
|
|
203
|
+
def get_api_key_or_auth_token() -> str | None:
|
|
202
204
|
# TODO: this is duplicated code from ext, but should probably migrate that to localstack
|
|
203
205
|
auth_token = os.environ.get("LOCALSTACK_AUTH_TOKEN", "").strip("'\" ")
|
|
204
206
|
if auth_token:
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import threading
|
|
2
2
|
from collections import defaultdict
|
|
3
3
|
from dataclasses import dataclass
|
|
4
|
-
from typing import Any
|
|
4
|
+
from typing import Any
|
|
5
5
|
|
|
6
6
|
from localstack import config
|
|
7
7
|
|
|
@@ -38,7 +38,7 @@ class LabeledCounterPayload:
|
|
|
38
38
|
value: int
|
|
39
39
|
type: str
|
|
40
40
|
schema_version: int
|
|
41
|
-
labels: dict[str,
|
|
41
|
+
labels: dict[str, str | float]
|
|
42
42
|
|
|
43
43
|
def as_dict(self) -> dict[str, Any]:
|
|
44
44
|
payload_dict = {
|
|
@@ -66,7 +66,7 @@ class ThreadSafeCounter:
|
|
|
66
66
|
_count: int
|
|
67
67
|
|
|
68
68
|
def __init__(self):
|
|
69
|
-
super(
|
|
69
|
+
super().__init__()
|
|
70
70
|
self._mutex = threading.Lock()
|
|
71
71
|
self._count = 0
|
|
72
72
|
|
|
@@ -140,15 +140,11 @@ class LabeledCounter(Metric):
|
|
|
140
140
|
|
|
141
141
|
_type: str
|
|
142
142
|
_labels: list[str]
|
|
143
|
-
_label_values: tuple[
|
|
144
|
-
_counters_by_label_values: defaultdict[
|
|
145
|
-
tuple[Optional[Union[str, float]], ...], ThreadSafeCounter
|
|
146
|
-
]
|
|
143
|
+
_label_values: tuple[str | float | None, ...]
|
|
144
|
+
_counters_by_label_values: defaultdict[tuple[str | float | None, ...], ThreadSafeCounter]
|
|
147
145
|
|
|
148
146
|
def __init__(self, namespace: str, name: str, labels: list[str], schema_version: int = 1):
|
|
149
|
-
super(
|
|
150
|
-
namespace=namespace, name=name, schema_version=schema_version
|
|
151
|
-
)
|
|
147
|
+
super().__init__(namespace=namespace, name=name, schema_version=schema_version)
|
|
152
148
|
|
|
153
149
|
if not labels:
|
|
154
150
|
raise ValueError("At least one label is required; the labels list cannot be empty.")
|
|
@@ -164,7 +160,7 @@ class LabeledCounter(Metric):
|
|
|
164
160
|
self._counters_by_label_values = defaultdict(ThreadSafeCounter)
|
|
165
161
|
MetricRegistry().register(self)
|
|
166
162
|
|
|
167
|
-
def labels(self, **kwargs:
|
|
163
|
+
def labels(self, **kwargs: str | float | None) -> ThreadSafeCounter:
|
|
168
164
|
"""
|
|
169
165
|
Create a scoped counter instance with specific label values.
|
|
170
166
|
|
|
@@ -200,10 +196,7 @@ class LabeledCounter(Metric):
|
|
|
200
196
|
)
|
|
201
197
|
|
|
202
198
|
# Create labels dictionary
|
|
203
|
-
labels_dict =
|
|
204
|
-
label_name: label_value
|
|
205
|
-
for label_name, label_value in zip(self._labels, label_values)
|
|
206
|
-
}
|
|
199
|
+
labels_dict = dict(zip(self._labels, label_values, strict=False))
|
|
207
200
|
|
|
208
201
|
payload.append(
|
|
209
202
|
LabeledCounterPayload(
|
|
@@ -4,7 +4,6 @@ import logging
|
|
|
4
4
|
import threading
|
|
5
5
|
import time
|
|
6
6
|
from queue import Full, Queue
|
|
7
|
-
from typing import Optional
|
|
8
7
|
|
|
9
8
|
from localstack import config
|
|
10
9
|
from localstack.utils.threads import start_thread, start_worker_thread
|
|
@@ -93,7 +92,7 @@ class PublisherBuffer(EventHandler):
|
|
|
93
92
|
self._stopping.set()
|
|
94
93
|
self._command_queue.put(self._cmd_stop)
|
|
95
94
|
|
|
96
|
-
def close_sync(self, timeout:
|
|
95
|
+
def close_sync(self, timeout: float | None = None):
|
|
97
96
|
self.close()
|
|
98
97
|
return self._stopped.wait(timeout)
|
|
99
98
|
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from localstack.runtime import hooks
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
@hooks.on_runtime_ready()
|
|
5
|
+
def publish_provider_assignment():
|
|
6
|
+
"""
|
|
7
|
+
Publishes the service provider assignment to the analytics service.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from localstack.config import SERVICE_PROVIDER_CONFIG
|
|
11
|
+
from localstack.services.plugins import SERVICE_PLUGINS
|
|
12
|
+
from localstack.utils.analytics import log
|
|
13
|
+
|
|
14
|
+
provider_assignment = {
|
|
15
|
+
service: f"localstack.aws.provider/{service}:{SERVICE_PROVIDER_CONFIG[service]}"
|
|
16
|
+
for service in SERVICE_PLUGINS.list_available()
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
log.event("ls_service_provider_assignment", provider_assignment)
|
|
@@ -2,7 +2,7 @@ import datetime
|
|
|
2
2
|
import logging
|
|
3
3
|
import threading
|
|
4
4
|
from collections import Counter
|
|
5
|
-
from typing import NamedTuple
|
|
5
|
+
from typing import NamedTuple
|
|
6
6
|
|
|
7
7
|
from localstack import config
|
|
8
8
|
from localstack.runtime.shutdown import SHUTDOWN_HANDLERS
|
|
@@ -20,7 +20,7 @@ class ServiceRequestInfo(NamedTuple):
|
|
|
20
20
|
service: str
|
|
21
21
|
operation: str
|
|
22
22
|
status_code: int
|
|
23
|
-
err_type:
|
|
23
|
+
err_type: str | None = None
|
|
24
24
|
|
|
25
25
|
|
|
26
26
|
class ServiceRequestAggregator:
|
localstack/utils/archives.py
CHANGED
|
@@ -8,7 +8,7 @@ import tempfile
|
|
|
8
8
|
import time
|
|
9
9
|
import zipfile
|
|
10
10
|
from subprocess import Popen
|
|
11
|
-
from typing import IO, Literal
|
|
11
|
+
from typing import IO, Literal
|
|
12
12
|
|
|
13
13
|
from localstack.constants import MAVEN_REPO_URL
|
|
14
14
|
from localstack.utils.files import load_file, mkdir, new_tmp_file, rm_rf, save_file
|
|
@@ -22,7 +22,7 @@ from .strings import truncate
|
|
|
22
22
|
LOG = logging.getLogger(__name__)
|
|
23
23
|
|
|
24
24
|
|
|
25
|
-
StrPath =
|
|
25
|
+
StrPath = str | os.PathLike
|
|
26
26
|
|
|
27
27
|
|
|
28
28
|
def is_zip_file(content):
|
|
@@ -30,13 +30,13 @@ def is_zip_file(content):
|
|
|
30
30
|
return zipfile.is_zipfile(stream)
|
|
31
31
|
|
|
32
32
|
|
|
33
|
-
def get_unzipped_size(zip_file:
|
|
33
|
+
def get_unzipped_size(zip_file: str | IO[bytes]):
|
|
34
34
|
"""Returns the size of the unzipped file."""
|
|
35
35
|
with zipfile.ZipFile(zip_file, "r") as zip_ref:
|
|
36
36
|
return sum(f.file_size for f in zip_ref.infolist())
|
|
37
37
|
|
|
38
38
|
|
|
39
|
-
def unzip(path: str, target_dir: str, overwrite: bool = True) ->
|
|
39
|
+
def unzip(path: str, target_dir: str, overwrite: bool = True) -> str | Popen | None:
|
|
40
40
|
from localstack.utils.platform import is_debian
|
|
41
41
|
|
|
42
42
|
use_native_cmd = is_debian() or is_command_available("unzip")
|
|
@@ -99,7 +99,7 @@ def create_zip_file_python(
|
|
|
99
99
|
base_dir: StrPath,
|
|
100
100
|
zip_file: StrPath,
|
|
101
101
|
mode: Literal["r", "w", "x", "a"] = "w",
|
|
102
|
-
content_root:
|
|
102
|
+
content_root: str | None = None,
|
|
103
103
|
):
|
|
104
104
|
with zipfile.ZipFile(zip_file, mode) as zip_file:
|
|
105
105
|
for root, dirs, files in os.walk(base_dir):
|
|
@@ -122,7 +122,7 @@ def add_file_to_jar(class_file, class_url, target_jar, base_dir=None):
|
|
|
122
122
|
|
|
123
123
|
|
|
124
124
|
def update_jar_manifest(
|
|
125
|
-
jar_file_name: str, parent_dir: str, search:
|
|
125
|
+
jar_file_name: str, parent_dir: str, search: str | re.Pattern, replace: str
|
|
126
126
|
):
|
|
127
127
|
manifest_file_path = "META-INF/MANIFEST.MF"
|
|
128
128
|
jar_path = os.path.join(parent_dir, jar_file_name)
|
|
@@ -174,10 +174,10 @@ def upgrade_jar_file(base_dir: str, file_glob: str, maven_asset: str):
|
|
|
174
174
|
def download_and_extract(
|
|
175
175
|
archive_url: str,
|
|
176
176
|
target_dir: str,
|
|
177
|
-
retries:
|
|
178
|
-
sleep:
|
|
179
|
-
tmp_archive:
|
|
180
|
-
checksum_url:
|
|
177
|
+
retries: int | None = 0,
|
|
178
|
+
sleep: int | None = 3,
|
|
179
|
+
tmp_archive: str | None = None,
|
|
180
|
+
checksum_url: str | None = None,
|
|
181
181
|
) -> None:
|
|
182
182
|
"""
|
|
183
183
|
Download and extract an archive to a target directory with optional checksum verification.
|
|
@@ -250,7 +250,7 @@ def download_and_extract_with_retry(
|
|
|
250
250
|
archive_url,
|
|
251
251
|
tmp_archive,
|
|
252
252
|
target_dir,
|
|
253
|
-
checksum_url:
|
|
253
|
+
checksum_url: str | None = None,
|
|
254
254
|
):
|
|
255
255
|
try:
|
|
256
256
|
download_and_extract(
|
localstack/utils/asyncio.py
CHANGED
|
@@ -40,12 +40,12 @@ class AdaptiveThreadPool(DaemonAwareThreadPool):
|
|
|
40
40
|
|
|
41
41
|
def __init__(self, core_size=None):
|
|
42
42
|
self.core_size = core_size or self.DEFAULT_CORE_POOL_SIZE
|
|
43
|
-
super(
|
|
43
|
+
super().__init__(max_workers=self.core_size)
|
|
44
44
|
|
|
45
45
|
def submit(self, fn, *args, **kwargs):
|
|
46
46
|
# if idle threads are available, don't spin new threads
|
|
47
47
|
if self.has_idle_threads():
|
|
48
|
-
return super(
|
|
48
|
+
return super().submit(fn, *args, **kwargs)
|
|
49
49
|
|
|
50
50
|
def _run(*tmpargs):
|
|
51
51
|
return fn(*args, **kwargs)
|
localstack/utils/aws/arns.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
import re
|
|
3
3
|
from functools import cache
|
|
4
|
-
from typing import
|
|
4
|
+
from typing import TypedDict
|
|
5
5
|
|
|
6
6
|
from botocore.utils import ArnParser, InvalidArnException
|
|
7
7
|
|
|
@@ -27,7 +27,7 @@ PARTITION_NAMES = list(REGION_PREFIX_TO_PARTITION.values()) + [DEFAULT_PARTITION
|
|
|
27
27
|
ARN_PARTITION_REGEX = r"^arn:(" + "|".join(sorted(PARTITION_NAMES)) + ")"
|
|
28
28
|
|
|
29
29
|
|
|
30
|
-
def get_partition(region:
|
|
30
|
+
def get_partition(region: str | None) -> str:
|
|
31
31
|
if not region:
|
|
32
32
|
return DEFAULT_PARTITION
|
|
33
33
|
if region in PARTITION_NAMES:
|
|
@@ -65,28 +65,28 @@ def parse_arn(arn: str) -> ArnData:
|
|
|
65
65
|
return _arn_parser.parse_arn(arn)
|
|
66
66
|
|
|
67
67
|
|
|
68
|
-
def extract_account_id_from_arn(arn: str) ->
|
|
68
|
+
def extract_account_id_from_arn(arn: str) -> str | None:
|
|
69
69
|
try:
|
|
70
70
|
return parse_arn(arn).get("account")
|
|
71
71
|
except InvalidArnException:
|
|
72
72
|
return None
|
|
73
73
|
|
|
74
74
|
|
|
75
|
-
def extract_region_from_arn(arn: str) ->
|
|
75
|
+
def extract_region_from_arn(arn: str) -> str | None:
|
|
76
76
|
try:
|
|
77
77
|
return parse_arn(arn).get("region")
|
|
78
78
|
except InvalidArnException:
|
|
79
79
|
return None
|
|
80
80
|
|
|
81
81
|
|
|
82
|
-
def extract_service_from_arn(arn: str) ->
|
|
82
|
+
def extract_service_from_arn(arn: str) -> str | None:
|
|
83
83
|
try:
|
|
84
84
|
return parse_arn(arn).get("service")
|
|
85
85
|
except InvalidArnException:
|
|
86
86
|
return None
|
|
87
87
|
|
|
88
88
|
|
|
89
|
-
def extract_resource_from_arn(arn: str) ->
|
|
89
|
+
def extract_resource_from_arn(arn: str) -> str | None:
|
|
90
90
|
try:
|
|
91
91
|
return parse_arn(arn).get("resource")
|
|
92
92
|
except InvalidArnException:
|
|
@@ -98,8 +98,10 @@ def extract_resource_from_arn(arn: str) -> Optional[str]:
|
|
|
98
98
|
#
|
|
99
99
|
|
|
100
100
|
|
|
101
|
-
def _resource_arn(
|
|
102
|
-
|
|
101
|
+
def _resource_arn(
|
|
102
|
+
name: str, pattern: str, account_id: str, region_name: str, allow_colons=False
|
|
103
|
+
) -> str:
|
|
104
|
+
if ":" in name and not allow_colons:
|
|
103
105
|
return name
|
|
104
106
|
if len(pattern.split("%s")) == 4:
|
|
105
107
|
return pattern % (get_partition(region_name), account_id, name)
|
|
@@ -120,7 +122,7 @@ def iam_role_arn(role_name: str, account_id: str, region_name: str) -> str:
|
|
|
120
122
|
return role_name
|
|
121
123
|
if re.match(f"{ARN_PARTITION_REGEX}:iam::", role_name):
|
|
122
124
|
return role_name
|
|
123
|
-
return "arn
|
|
125
|
+
return f"arn:{get_partition(region_name)}:iam::{account_id}:role/{role_name}"
|
|
124
126
|
|
|
125
127
|
|
|
126
128
|
def iam_resource_arn(resource: str, account_id: str, role: str = None) -> str:
|
|
@@ -180,13 +182,7 @@ def dynamodb_table_arn(table_name: str, account_id: str, region_name: str) -> st
|
|
|
180
182
|
def dynamodb_stream_arn(
|
|
181
183
|
table_name: str, latest_stream_label: str, account_id: str, region_name: str
|
|
182
184
|
) -> str:
|
|
183
|
-
return "arn
|
|
184
|
-
get_partition(region_name),
|
|
185
|
-
region_name,
|
|
186
|
-
account_id,
|
|
187
|
-
table_name,
|
|
188
|
-
latest_stream_label,
|
|
189
|
-
)
|
|
185
|
+
return f"arn:{get_partition(region_name)}:dynamodb:{region_name}:{account_id}:table/{table_name}/stream/{latest_stream_label}"
|
|
190
186
|
|
|
191
187
|
|
|
192
188
|
#
|
|
@@ -291,7 +287,7 @@ def lambda_event_source_mapping_arn(uuid: str, account_id: str, region_name: str
|
|
|
291
287
|
def lambda_function_or_layer_arn(
|
|
292
288
|
type: str,
|
|
293
289
|
entity_name: str,
|
|
294
|
-
version:
|
|
290
|
+
version: str | None,
|
|
295
291
|
account_id: str,
|
|
296
292
|
region_name: str,
|
|
297
293
|
) -> str:
|
|
@@ -453,7 +449,7 @@ def s3_bucket_arn(bucket_name_or_arn: str, region="us-east-1") -> str:
|
|
|
453
449
|
|
|
454
450
|
def sqs_queue_arn(queue_name: str, account_id: str, region_name: str) -> str:
|
|
455
451
|
queue_name = queue_name.split("/")[-1]
|
|
456
|
-
return "arn
|
|
452
|
+
return f"arn:{get_partition(region_name)}:sqs:{region_name}:{account_id}:{queue_name}"
|
|
457
453
|
|
|
458
454
|
|
|
459
455
|
#
|
|
@@ -462,20 +458,13 @@ def sqs_queue_arn(queue_name: str, account_id: str, region_name: str) -> str:
|
|
|
462
458
|
|
|
463
459
|
|
|
464
460
|
def apigateway_restapi_arn(api_id: str, account_id: str, region_name: str) -> str:
|
|
465
|
-
return
|
|
466
|
-
get_partition(region_name)
|
|
467
|
-
region_name,
|
|
468
|
-
account_id,
|
|
469
|
-
api_id,
|
|
461
|
+
return (
|
|
462
|
+
f"arn:{get_partition(region_name)}:apigateway:{region_name}:{account_id}:/restapis/{api_id}"
|
|
470
463
|
)
|
|
471
464
|
|
|
472
465
|
|
|
473
466
|
def apigateway_invocations_arn(lambda_uri: str, region_name: str) -> str:
|
|
474
|
-
return "arn
|
|
475
|
-
get_partition(region_name),
|
|
476
|
-
region_name,
|
|
477
|
-
lambda_uri,
|
|
478
|
-
)
|
|
467
|
+
return f"arn:{get_partition(region_name)}:apigateway:{region_name}:lambda:path/2015-03-31/functions/{lambda_uri}/invocations"
|
|
479
468
|
|
|
480
469
|
|
|
481
470
|
#
|
|
@@ -487,6 +476,12 @@ def sns_topic_arn(topic_name: str, account_id: str, region_name: str) -> str:
|
|
|
487
476
|
return f"arn:{get_partition(region_name)}:sns:{region_name}:{account_id}:{topic_name}"
|
|
488
477
|
|
|
489
478
|
|
|
479
|
+
def sns_platform_application_arn(
|
|
480
|
+
platform_application_name: str, platform: str, account_id: str, region_name: str
|
|
481
|
+
) -> str:
|
|
482
|
+
return f"arn:{get_partition(region_name)}:sns:{region_name}:{account_id}:app/{platform}/{platform_application_name}"
|
|
483
|
+
|
|
484
|
+
|
|
490
485
|
#
|
|
491
486
|
# ECR
|
|
492
487
|
#
|
|
@@ -555,7 +550,7 @@ def lambda_function_name(name_or_arn: str) -> str:
|
|
|
555
550
|
if ":" in name_or_arn:
|
|
556
551
|
arn = parse_arn(name_or_arn)
|
|
557
552
|
if arn["service"] != "lambda":
|
|
558
|
-
raise ValueError("arn is not a lambda arn
|
|
553
|
+
raise ValueError(f"arn is not a lambda arn {name_or_arn}")
|
|
559
554
|
|
|
560
555
|
return parse_arn(name_or_arn)["resource"].split(":")[1]
|
|
561
556
|
else:
|
|
@@ -3,7 +3,7 @@ import datetime
|
|
|
3
3
|
import json
|
|
4
4
|
import re
|
|
5
5
|
from binascii import crc32
|
|
6
|
-
from typing import Any
|
|
6
|
+
from typing import Any
|
|
7
7
|
from urllib.parse import parse_qs
|
|
8
8
|
|
|
9
9
|
import xmltodict
|
|
@@ -36,13 +36,13 @@ def requests_error_response_json(message, code=500, error_type="InternalFailure"
|
|
|
36
36
|
|
|
37
37
|
def requests_error_response_xml(
|
|
38
38
|
message: str,
|
|
39
|
-
code:
|
|
40
|
-
code_string:
|
|
41
|
-
service:
|
|
42
|
-
xmlns:
|
|
39
|
+
code: int | None = 400,
|
|
40
|
+
code_string: str | None = "InvalidParameter",
|
|
41
|
+
service: str | None = None,
|
|
42
|
+
xmlns: str | None = None,
|
|
43
43
|
):
|
|
44
44
|
response = RequestsResponse()
|
|
45
|
-
xmlns = xmlns or "http
|
|
45
|
+
xmlns = xmlns or f"http://{service}.amazonaws.com/doc/2010-03-31/"
|
|
46
46
|
response._content = f"""<ErrorResponse xmlns="{xmlns}"><Error>
|
|
47
47
|
<Type>Sender</Type>
|
|
48
48
|
<Code>{code_string}</Code>
|
|
@@ -100,7 +100,7 @@ def requests_error_response_xml_signature_calculation(
|
|
|
100
100
|
|
|
101
101
|
def requests_error_response(
|
|
102
102
|
req_headers: dict,
|
|
103
|
-
message:
|
|
103
|
+
message: str | bytes,
|
|
104
104
|
code: int = 500,
|
|
105
105
|
error_type: str = "InternalFailure",
|
|
106
106
|
service: str = None,
|
|
@@ -201,7 +201,7 @@ def parse_query_string(url_or_qs: str, multi_values=False) -> dict[str, str]:
|
|
|
201
201
|
return result
|
|
202
202
|
|
|
203
203
|
|
|
204
|
-
def calculate_crc32(content:
|
|
204
|
+
def calculate_crc32(content: str | bytes) -> int:
|
|
205
205
|
return crc32(to_bytes(content)) & 0xFFFFFFFF
|
|
206
206
|
|
|
207
207
|
|
|
@@ -2,7 +2,6 @@ import logging
|
|
|
2
2
|
import re
|
|
3
3
|
import socket
|
|
4
4
|
from functools import lru_cache
|
|
5
|
-
from typing import Union
|
|
6
5
|
|
|
7
6
|
import boto3
|
|
8
7
|
|
|
@@ -46,7 +45,7 @@ def get_boto3_region() -> str:
|
|
|
46
45
|
return boto3.session.Session().region_name
|
|
47
46
|
|
|
48
47
|
|
|
49
|
-
def get_local_service_url(service_name_or_port:
|
|
48
|
+
def get_local_service_url(service_name_or_port: str | int) -> str:
|
|
50
49
|
"""Return the local service URL for the given service name or port."""
|
|
51
50
|
# TODO(srw): we don't need to differentiate on service name any more, so remove the argument
|
|
52
51
|
if isinstance(service_name_or_port, int):
|
|
@@ -68,7 +67,7 @@ def get_s3_hostname():
|
|
|
68
67
|
|
|
69
68
|
|
|
70
69
|
def fix_account_id_in_arns(
|
|
71
|
-
response, replacement: str, colon_delimiter: str = ":", existing:
|
|
70
|
+
response, replacement: str, colon_delimiter: str = ":", existing: str | list[str] = None
|
|
72
71
|
):
|
|
73
72
|
"""Fix the account ID in the ARNs returned in the given Flask response or string"""
|
|
74
73
|
from moto.core import DEFAULT_ACCOUNT_ID
|