localstack-core 4.9.3.dev25__py3-none-any.whl → 4.9.3.dev27__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.
- localstack/aws/handlers/metric_handler.py +41 -1
- localstack/cli/exceptions.py +1 -1
- localstack/cli/localstack.py +4 -4
- localstack/cli/lpm.py +3 -4
- localstack/cli/profiles.py +1 -2
- localstack/config.py +18 -12
- localstack/constants.py +4 -0
- localstack/packages/api.py +9 -8
- localstack/packages/core.py +2 -2
- localstack/testing/pytest/cloudformation/fixtures.py +3 -3
- localstack/testing/pytest/container.py +4 -5
- localstack/testing/pytest/fixtures.py +20 -19
- localstack/testing/pytest/marking.py +5 -4
- localstack/testing/pytest/stepfunctions/utils.py +4 -3
- localstack/testing/pytest/util.py +1 -1
- localstack/testing/pytest/validation_tracking.py +1 -2
- localstack/utils/analytics/events.py +2 -2
- localstack/utils/analytics/metadata.py +1 -2
- localstack/utils/analytics/metrics/counter.py +6 -8
- localstack/utils/analytics/publisher.py +1 -2
- localstack/utils/analytics/service_request_aggregator.py +2 -2
- localstack/utils/archives.py +11 -11
- localstack/utils/aws/arns.py +7 -7
- localstack/utils/aws/aws_responses.py +7 -7
- localstack/utils/aws/aws_stack.py +2 -3
- localstack/utils/aws/message_forwarding.py +1 -2
- localstack/utils/aws/request_context.py +4 -5
- localstack/utils/batch_policy.py +3 -3
- localstack/utils/bootstrap.py +7 -7
- 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 +115 -131
- localstack/utils/container_utils/docker_cmd_client.py +42 -42
- localstack/utils/container_utils/docker_sdk_client.py +63 -62
- localstack/utils/diagnose.py +2 -3
- localstack/utils/docker_utils.py +3 -4
- localstack/utils/functions.py +3 -2
- localstack/utils/http.py +4 -5
- localstack/utils/json.py +3 -3
- localstack/utils/kinesis/kinesis_connector.py +2 -1
- localstack/utils/net.py +6 -6
- localstack/utils/no_exit_argument_parser.py +2 -2
- localstack/utils/numbers.py +2 -2
- localstack/utils/objects.py +6 -5
- localstack/utils/patch.py +2 -1
- localstack/utils/run.py +10 -9
- localstack/utils/scheduler.py +11 -11
- localstack/utils/server/tcp_proxy.py +2 -2
- localstack/utils/serving.py +2 -3
- localstack/utils/strings.py +10 -11
- localstack/utils/sync.py +2 -1
- localstack/utils/tagging.py +1 -4
- localstack/utils/testutil.py +5 -4
- localstack/utils/threads.py +2 -2
- localstack/utils/time.py +1 -2
- localstack/utils/urls.py +1 -3
- localstack/version.py +2 -2
- {localstack_core-4.9.3.dev25.dist-info → localstack_core-4.9.3.dev27.dist-info}/METADATA +2 -2
- {localstack_core-4.9.3.dev25.dist-info → localstack_core-4.9.3.dev27.dist-info}/RECORD +69 -69
- localstack_core-4.9.3.dev27.dist-info/plux.json +1 -0
- localstack_core-4.9.3.dev25.dist-info/plux.json +0 -1
- {localstack_core-4.9.3.dev25.data → localstack_core-4.9.3.dev27.data}/scripts/localstack +0 -0
- {localstack_core-4.9.3.dev25.data → localstack_core-4.9.3.dev27.data}/scripts/localstack-supervisor +0 -0
- {localstack_core-4.9.3.dev25.data → localstack_core-4.9.3.dev27.data}/scripts/localstack.bat +0 -0
- {localstack_core-4.9.3.dev25.dist-info → localstack_core-4.9.3.dev27.dist-info}/WHEEL +0 -0
- {localstack_core-4.9.3.dev25.dist-info → localstack_core-4.9.3.dev27.dist-info}/entry_points.txt +0 -0
- {localstack_core-4.9.3.dev25.dist-info → localstack_core-4.9.3.dev27.dist-info}/licenses/LICENSE.txt +0 -0
- {localstack_core-4.9.3.dev25.dist-info → localstack_core-4.9.3.dev27.dist-info}/top_level.txt +0 -0
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/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:
|
|
@@ -285,7 +285,7 @@ def lambda_event_source_mapping_arn(uuid: str, account_id: str, region_name: str
|
|
|
285
285
|
def lambda_function_or_layer_arn(
|
|
286
286
|
type: str,
|
|
287
287
|
entity_name: str,
|
|
288
|
-
version:
|
|
288
|
+
version: str | None,
|
|
289
289
|
account_id: str,
|
|
290
290
|
region_name: str,
|
|
291
291
|
) -> str:
|
|
@@ -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,10 +36,10 @@ 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
45
|
xmlns = xmlns or f"http://{service}.amazonaws.com/doc/2010-03-31/"
|
|
@@ -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
|
|
@@ -3,7 +3,6 @@ import json
|
|
|
3
3
|
import logging
|
|
4
4
|
import re
|
|
5
5
|
import uuid
|
|
6
|
-
from typing import Optional
|
|
7
6
|
|
|
8
7
|
from moto.events.models import events_backends
|
|
9
8
|
|
|
@@ -214,7 +213,7 @@ def list_of_parameters_to_object(items):
|
|
|
214
213
|
return {item.get("Key"): item.get("Value") for item in items}
|
|
215
214
|
|
|
216
215
|
|
|
217
|
-
def send_event_to_api_destination(target_arn, event, http_parameters:
|
|
216
|
+
def send_event_to_api_destination(target_arn, event, http_parameters: dict | None = None):
|
|
218
217
|
"""Send an event to an EventBridge API destination
|
|
219
218
|
See https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-api-destinations.html"""
|
|
220
219
|
|
|
@@ -4,7 +4,6 @@ This module has utilities relating to creating/parsing AWS requests.
|
|
|
4
4
|
|
|
5
5
|
import logging
|
|
6
6
|
import re
|
|
7
|
-
from typing import Optional
|
|
8
7
|
|
|
9
8
|
from rolo import Request as RoloRequest
|
|
10
9
|
|
|
@@ -30,7 +29,7 @@ def get_account_id_from_request(request: RoloRequest) -> str:
|
|
|
30
29
|
return get_account_id_from_access_key_id(access_key_id)
|
|
31
30
|
|
|
32
31
|
|
|
33
|
-
def extract_region_from_auth_header(headers) ->
|
|
32
|
+
def extract_region_from_auth_header(headers) -> str | None:
|
|
34
33
|
auth = headers.get("Authorization") or ""
|
|
35
34
|
region = re.sub(r".*Credential=[^/]+/[^/]+/([^/]+)/.*", r"\1", auth)
|
|
36
35
|
if region == auth:
|
|
@@ -38,12 +37,12 @@ def extract_region_from_auth_header(headers) -> Optional[str]:
|
|
|
38
37
|
return region
|
|
39
38
|
|
|
40
39
|
|
|
41
|
-
def extract_account_id_from_auth_header(headers) ->
|
|
40
|
+
def extract_account_id_from_auth_header(headers) -> str | None:
|
|
42
41
|
if access_key_id := extract_access_key_id_from_auth_header(headers):
|
|
43
42
|
return get_account_id_from_access_key_id(access_key_id)
|
|
44
43
|
|
|
45
44
|
|
|
46
|
-
def extract_access_key_id_from_auth_header(headers: dict[str, str]) ->
|
|
45
|
+
def extract_access_key_id_from_auth_header(headers: dict[str, str]) -> str | None:
|
|
47
46
|
auth = headers.get("Authorization") or ""
|
|
48
47
|
|
|
49
48
|
if auth.startswith("AWS4-"):
|
|
@@ -67,7 +66,7 @@ def extract_region_from_headers(headers) -> str:
|
|
|
67
66
|
return extract_region_from_auth_header(headers) or AWS_REGION_US_EAST_1
|
|
68
67
|
|
|
69
68
|
|
|
70
|
-
def extract_service_name_from_auth_header(headers: dict) ->
|
|
69
|
+
def extract_service_name_from_auth_header(headers: dict) -> str | None:
|
|
71
70
|
try:
|
|
72
71
|
auth_header = headers.get("authorization", "")
|
|
73
72
|
credential_scope = auth_header.split(",")[0].split()[1]
|
localstack/utils/batch_policy.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import copy
|
|
2
2
|
import time
|
|
3
|
-
from typing import Generic,
|
|
3
|
+
from typing import Generic, TypeVar, overload
|
|
4
4
|
|
|
5
5
|
from pydantic import Field
|
|
6
6
|
from pydantic.dataclasses import dataclass
|
|
@@ -47,8 +47,8 @@ class Batcher(Generic[T]):
|
|
|
47
47
|
assert batcher.flush() == ["item1", "item2", "item3", "item4"]
|
|
48
48
|
"""
|
|
49
49
|
|
|
50
|
-
max_count:
|
|
51
|
-
max_window:
|
|
50
|
+
max_count: int | None = Field(default=None, description="Maximum number of items", ge=0)
|
|
51
|
+
max_window: float | None = Field(
|
|
52
52
|
default=None, description="Maximum time window in seconds", ge=0
|
|
53
53
|
)
|
|
54
54
|
|
localstack/utils/bootstrap.py
CHANGED
|
@@ -9,9 +9,9 @@ import shlex
|
|
|
9
9
|
import signal
|
|
10
10
|
import threading
|
|
11
11
|
import time
|
|
12
|
-
from collections.abc import Iterable
|
|
12
|
+
from collections.abc import Callable, Iterable
|
|
13
13
|
from functools import wraps
|
|
14
|
-
from typing import Any
|
|
14
|
+
from typing import Any
|
|
15
15
|
|
|
16
16
|
from localstack import config, constants
|
|
17
17
|
from localstack.config import (
|
|
@@ -175,7 +175,7 @@ def get_docker_image_details(image_name: str = None) -> dict[str, str]:
|
|
|
175
175
|
return result
|
|
176
176
|
|
|
177
177
|
|
|
178
|
-
def get_image_environment_variable(env_name: str) ->
|
|
178
|
+
def get_image_environment_variable(env_name: str) -> str | None:
|
|
179
179
|
image_name = get_docker_image_to_start()
|
|
180
180
|
image_info = DOCKER_CLIENT.inspect_image(image_name)
|
|
181
181
|
image_envs = image_info["Config"]["Env"]
|
|
@@ -544,7 +544,7 @@ class ContainerConfigurators:
|
|
|
544
544
|
|
|
545
545
|
@staticmethod
|
|
546
546
|
def gateway_listen(
|
|
547
|
-
port:
|
|
547
|
+
port: int | Iterable[int] | HostAndPort | Iterable[HostAndPort],
|
|
548
548
|
):
|
|
549
549
|
"""
|
|
550
550
|
Uses the given ports to configure GATEWAY_LISTEN. For instance, ``gateway_listen([4566, 443])`` would
|
|
@@ -1000,7 +1000,7 @@ class RunningContainer:
|
|
|
1000
1000
|
return
|
|
1001
1001
|
raise
|
|
1002
1002
|
|
|
1003
|
-
def inspect(self) -> dict[str,
|
|
1003
|
+
def inspect(self) -> dict[str, dict | str]:
|
|
1004
1004
|
return self.container_client.inspect_container(container_name_or_id=self.id)
|
|
1005
1005
|
|
|
1006
1006
|
def attach(self):
|
|
@@ -1028,7 +1028,7 @@ class ContainerLogPrinter:
|
|
|
1028
1028
|
self.callback = callback
|
|
1029
1029
|
|
|
1030
1030
|
self._closed = threading.Event()
|
|
1031
|
-
self._stream:
|
|
1031
|
+
self._stream: CancellableStream | None = None
|
|
1032
1032
|
|
|
1033
1033
|
def _can_start_streaming(self):
|
|
1034
1034
|
if self._closed.is_set():
|
|
@@ -1338,7 +1338,7 @@ def start_infra_in_docker_detached(console, cli_params: dict[str, Any] = None):
|
|
|
1338
1338
|
console.log("detaching")
|
|
1339
1339
|
|
|
1340
1340
|
|
|
1341
|
-
def wait_container_is_ready(timeout:
|
|
1341
|
+
def wait_container_is_ready(timeout: float | None = None):
|
|
1342
1342
|
"""Blocks until the localstack main container is running and the ready marker has been printed."""
|
|
1343
1343
|
container_name = config.MAIN_CONTAINER_NAME
|
|
1344
1344
|
started = time.time()
|
|
@@ -2,7 +2,7 @@ import logging
|
|
|
2
2
|
import time
|
|
3
3
|
from datetime import datetime, timezone
|
|
4
4
|
from itertools import islice
|
|
5
|
-
from typing import
|
|
5
|
+
from typing import TypedDict
|
|
6
6
|
|
|
7
7
|
from werkzeug import Response as WerkzeugResponse
|
|
8
8
|
|
|
@@ -20,8 +20,8 @@ LOG = logging.getLogger(__name__)
|
|
|
20
20
|
class SqsMetricBatchData(TypedDict, total=False):
|
|
21
21
|
MetricName: str
|
|
22
22
|
QueueName: str
|
|
23
|
-
Value:
|
|
24
|
-
Unit:
|
|
23
|
+
Value: int | None
|
|
24
|
+
Unit: str | None
|
|
25
25
|
|
|
26
26
|
|
|
27
27
|
def dimension_lambda(kwargs):
|
|
@@ -30,7 +30,7 @@ def dimension_lambda(kwargs):
|
|
|
30
30
|
|
|
31
31
|
|
|
32
32
|
def publish_lambda_metric(
|
|
33
|
-
metric, value, kwargs, account_id:
|
|
33
|
+
metric, value, kwargs, account_id: str | None = None, region_name: str | None = None
|
|
34
34
|
):
|
|
35
35
|
# publish metric only if CloudWatch service is available
|
|
36
36
|
if not is_api_enabled("cloudwatch"):
|
|
@@ -155,7 +155,7 @@ def store_cloudwatch_logs(
|
|
|
155
155
|
log_stream_name,
|
|
156
156
|
log_output,
|
|
157
157
|
start_time=None,
|
|
158
|
-
auto_create_group:
|
|
158
|
+
auto_create_group: bool | None = True,
|
|
159
159
|
):
|
|
160
160
|
if not is_api_enabled("logs"):
|
|
161
161
|
return
|
localstack/utils/collections.py
CHANGED
|
@@ -5,10 +5,9 @@ and manipulate python collection (dicts, list, sets).
|
|
|
5
5
|
|
|
6
6
|
import logging
|
|
7
7
|
import re
|
|
8
|
-
from collections.abc import Iterable, Iterator, Mapping, Sized
|
|
8
|
+
from collections.abc import Callable, Iterable, Iterator, Mapping, Sized
|
|
9
9
|
from typing import (
|
|
10
10
|
Any,
|
|
11
|
-
Callable,
|
|
12
11
|
Optional,
|
|
13
12
|
TypedDict,
|
|
14
13
|
TypeVar,
|
|
@@ -116,7 +115,7 @@ class PaginatedList(list[_ListType]):
|
|
|
116
115
|
next_token: str = None,
|
|
117
116
|
page_size: int = None,
|
|
118
117
|
filter_function: Callable[[_ListType], bool] = None,
|
|
119
|
-
) -> tuple[list[_ListType],
|
|
118
|
+
) -> tuple[list[_ListType], str | None]:
|
|
120
119
|
if filter_function is not None:
|
|
121
120
|
result_list = list(filter(filter_function, self))
|
|
122
121
|
else:
|
|
@@ -148,7 +147,7 @@ class PaginatedList(list[_ListType]):
|
|
|
148
147
|
class CustomExpiryTTLCache(cachetools.TTLCache):
|
|
149
148
|
"""TTLCache that allows to set custom expiry times for individual keys."""
|
|
150
149
|
|
|
151
|
-
def set_expiry(self, key: Any, ttl:
|
|
150
|
+
def set_expiry(self, key: Any, ttl: float | int) -> float:
|
|
152
151
|
"""Set the expiry of the given key in a TTLCache to (<current_time> + <ttl>)"""
|
|
153
152
|
with self.timer as time:
|
|
154
153
|
# note: need to access the internal dunder API here
|
|
@@ -315,7 +314,7 @@ def is_list_or_tuple(obj) -> bool:
|
|
|
315
314
|
return isinstance(obj, (list, tuple))
|
|
316
315
|
|
|
317
316
|
|
|
318
|
-
def ensure_list(obj: Any, wrap_none=False) ->
|
|
317
|
+
def ensure_list(obj: Any, wrap_none=False) -> list | None:
|
|
319
318
|
"""Wrap the given object in a list, or return the object itself if it already is a list."""
|
|
320
319
|
if obj is None and not wrap_none:
|
|
321
320
|
return obj
|
|
@@ -414,7 +413,7 @@ def items_equivalent(list1, list2, comparator):
|
|
|
414
413
|
return True
|
|
415
414
|
|
|
416
415
|
|
|
417
|
-
def is_none_or_empty(obj:
|
|
416
|
+
def is_none_or_empty(obj: str | None | list | None) -> bool:
|
|
418
417
|
return (
|
|
419
418
|
obj is None
|
|
420
419
|
or (isinstance(obj, str) and obj.strip() == "")
|
|
@@ -475,7 +474,7 @@ def convert_to_typed_dict(typed_dict: type[T], obj: dict, strict: bool = False)
|
|
|
475
474
|
return result
|
|
476
475
|
|
|
477
476
|
|
|
478
|
-
def dict_multi_values(elements:
|
|
477
|
+
def dict_multi_values(elements: list | dict) -> dict[str, list[Any]]:
|
|
479
478
|
"""
|
|
480
479
|
Return a dictionary with the original keys from the list of dictionary and the
|
|
481
480
|
values are the list of values of the original dictionary.
|
|
@@ -516,7 +515,7 @@ def split_list_by(
|
|
|
516
515
|
return truthy, falsy
|
|
517
516
|
|
|
518
517
|
|
|
519
|
-
def is_comma_delimited_list(string: str, item_regex:
|
|
518
|
+
def is_comma_delimited_list(string: str, item_regex: str | None = None) -> bool:
|
|
520
519
|
"""
|
|
521
520
|
Checks if the given string is a comma-delimited list of items.
|
|
522
521
|
The optional `item_regex` parameter specifies the regex pattern for each item in the list.
|
|
@@ -2,7 +2,6 @@ import logging
|
|
|
2
2
|
import os
|
|
3
3
|
import re
|
|
4
4
|
from functools import lru_cache
|
|
5
|
-
from typing import Optional
|
|
6
5
|
|
|
7
6
|
from localstack import config, constants
|
|
8
7
|
from localstack.utils.container_utils.container_client import ContainerException
|
|
@@ -13,7 +12,7 @@ LOG = logging.getLogger(__name__)
|
|
|
13
12
|
|
|
14
13
|
|
|
15
14
|
@lru_cache
|
|
16
|
-
def get_main_container_network() ->
|
|
15
|
+
def get_main_container_network() -> str | None:
|
|
17
16
|
"""
|
|
18
17
|
Gets the main network of the LocalStack container (if we run in one, bridge otherwise)
|
|
19
18
|
If there are multiple networks connected to the LocalStack container, we choose the first as "main" network
|
|
@@ -50,7 +49,7 @@ def get_main_container_network() -> Optional[str]:
|
|
|
50
49
|
|
|
51
50
|
|
|
52
51
|
@lru_cache
|
|
53
|
-
def get_endpoint_for_network(network:
|
|
52
|
+
def get_endpoint_for_network(network: str | None = None) -> str:
|
|
54
53
|
"""
|
|
55
54
|
Get the LocalStack endpoint (= IP address) on the given network.
|
|
56
55
|
If a network is given, it will return the IP address/hostname of LocalStack on that network
|