localstack-core 4.9.3.dev26__py3-none-any.whl → 4.9.3.dev28__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/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 +12 -12
- localstack/packages/api.py +9 -8
- localstack/packages/core.py +2 -2
- localstack/services/dynamodb/provider.py +24 -0
- localstack/services/dynamodb/v2/provider.py +24 -0
- 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.dev26.dist-info → localstack_core-4.9.3.dev28.dist-info}/METADATA +2 -2
- {localstack_core-4.9.3.dev26.dist-info → localstack_core-4.9.3.dev28.dist-info}/RECORD +69 -69
- localstack_core-4.9.3.dev28.dist-info/plux.json +1 -0
- localstack_core-4.9.3.dev26.dist-info/plux.json +0 -1
- {localstack_core-4.9.3.dev26.data → localstack_core-4.9.3.dev28.data}/scripts/localstack +0 -0
- {localstack_core-4.9.3.dev26.data → localstack_core-4.9.3.dev28.data}/scripts/localstack-supervisor +0 -0
- {localstack_core-4.9.3.dev26.data → localstack_core-4.9.3.dev28.data}/scripts/localstack.bat +0 -0
- {localstack_core-4.9.3.dev26.dist-info → localstack_core-4.9.3.dev28.dist-info}/WHEEL +0 -0
- {localstack_core-4.9.3.dev26.dist-info → localstack_core-4.9.3.dev28.dist-info}/entry_points.txt +0 -0
- {localstack_core-4.9.3.dev26.dist-info → localstack_core-4.9.3.dev28.dist-info}/licenses/LICENSE.txt +0 -0
- {localstack_core-4.9.3.dev26.dist-info → localstack_core-4.9.3.dev28.dist-info}/top_level.txt +0 -0
localstack/cli/exceptions.py
CHANGED
|
@@ -12,7 +12,7 @@ class CLIError(ClickException):
|
|
|
12
12
|
def format_message(self) -> str:
|
|
13
13
|
return click.style(f"❌ Error: {self.message}", fg="red")
|
|
14
14
|
|
|
15
|
-
def show(self, file: t.
|
|
15
|
+
def show(self, file: t.IO[t.Any] | None = None) -> None:
|
|
16
16
|
if file is None:
|
|
17
17
|
file = get_text_stderr()
|
|
18
18
|
|
localstack/cli/localstack.py
CHANGED
|
@@ -3,7 +3,7 @@ import logging
|
|
|
3
3
|
import os
|
|
4
4
|
import sys
|
|
5
5
|
import traceback
|
|
6
|
-
from typing import
|
|
6
|
+
from typing import TypedDict
|
|
7
7
|
|
|
8
8
|
import click
|
|
9
9
|
import requests
|
|
@@ -320,8 +320,8 @@ class DockerStatus(TypedDict, total=False):
|
|
|
320
320
|
image_tag: str
|
|
321
321
|
image_id: str
|
|
322
322
|
image_created: str
|
|
323
|
-
container_name:
|
|
324
|
-
container_ip:
|
|
323
|
+
container_name: str | None
|
|
324
|
+
container_ip: str | None
|
|
325
325
|
|
|
326
326
|
|
|
327
327
|
def _print_docker_status(format_: str) -> None:
|
|
@@ -699,7 +699,7 @@ def cmd_logs(follow: bool, tail: int) -> None:
|
|
|
699
699
|
metavar="N",
|
|
700
700
|
)
|
|
701
701
|
@publish_invocation
|
|
702
|
-
def cmd_wait(timeout:
|
|
702
|
+
def cmd_wait(timeout: float | None = None) -> None:
|
|
703
703
|
"""
|
|
704
704
|
Wait for the LocalStack runtime to be up and running.
|
|
705
705
|
|
localstack/cli/lpm.py
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import itertools
|
|
2
2
|
import logging
|
|
3
3
|
from multiprocessing.pool import ThreadPool
|
|
4
|
-
from typing import Optional
|
|
5
4
|
|
|
6
5
|
import click
|
|
7
6
|
from rich.console import Console
|
|
@@ -75,9 +74,9 @@ def _do_install_package(package: Package, version: str = None, target: InstallTa
|
|
|
75
74
|
)
|
|
76
75
|
def install(
|
|
77
76
|
package: list[str],
|
|
78
|
-
parallel:
|
|
79
|
-
version:
|
|
80
|
-
target:
|
|
77
|
+
parallel: int | None = 1,
|
|
78
|
+
version: str | None = None,
|
|
79
|
+
target: str | None = None,
|
|
81
80
|
):
|
|
82
81
|
"""Install one or more packages."""
|
|
83
82
|
try:
|
localstack/cli/profiles.py
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import argparse
|
|
2
2
|
import os
|
|
3
3
|
import sys
|
|
4
|
-
from typing import Optional
|
|
5
4
|
|
|
6
5
|
# important: this needs to be free of localstack imports
|
|
7
6
|
|
|
@@ -44,7 +43,7 @@ def set_and_remove_profile_from_sys_argv():
|
|
|
44
43
|
os.environ["CONFIG_PROFILE"] = profile.strip()
|
|
45
44
|
|
|
46
45
|
|
|
47
|
-
def parse_p_argument(args) ->
|
|
46
|
+
def parse_p_argument(args) -> str | None:
|
|
48
47
|
"""
|
|
49
48
|
Lightweight arg parsing to find the first occurrence of ``-p <config>``, or ``-p=<config>`` and return the value of
|
|
50
49
|
``<config>`` from the given arguments.
|
localstack/config.py
CHANGED
|
@@ -10,7 +10,7 @@ import time
|
|
|
10
10
|
import warnings
|
|
11
11
|
from collections import defaultdict
|
|
12
12
|
from collections.abc import Mapping
|
|
13
|
-
from typing import Any,
|
|
13
|
+
from typing import Any, TypeVar
|
|
14
14
|
|
|
15
15
|
from localstack import constants
|
|
16
16
|
from localstack.constants import (
|
|
@@ -209,13 +209,13 @@ class Directories:
|
|
|
209
209
|
return str(self.__dict__)
|
|
210
210
|
|
|
211
211
|
|
|
212
|
-
def eval_log_type(env_var_name: str) ->
|
|
212
|
+
def eval_log_type(env_var_name: str) -> str | bool:
|
|
213
213
|
"""Get the log type from environment variable"""
|
|
214
214
|
ls_log = os.environ.get(env_var_name, "").lower().strip()
|
|
215
215
|
return ls_log if ls_log in LOG_LEVELS else False
|
|
216
216
|
|
|
217
217
|
|
|
218
|
-
def parse_boolean_env(env_var_name: str) ->
|
|
218
|
+
def parse_boolean_env(env_var_name: str) -> bool | None:
|
|
219
219
|
"""Parse the value of the given env variable and return True/False, or None if it is not a boolean value."""
|
|
220
220
|
value = os.environ.get(env_var_name, "").lower().strip()
|
|
221
221
|
if value in TRUE_STRINGS:
|
|
@@ -650,7 +650,7 @@ class UniqueHostAndPortList(list[HostAndPort]):
|
|
|
650
650
|
- Identical identical hosts and ports are de-duped
|
|
651
651
|
"""
|
|
652
652
|
|
|
653
|
-
def __init__(self, iterable:
|
|
653
|
+
def __init__(self, iterable: list[HostAndPort] | None = None):
|
|
654
654
|
super().__init__(iterable or [])
|
|
655
655
|
self._ensure_unique()
|
|
656
656
|
|
|
@@ -1509,10 +1509,10 @@ def get_protocol() -> str:
|
|
|
1509
1509
|
|
|
1510
1510
|
|
|
1511
1511
|
def external_service_url(
|
|
1512
|
-
host:
|
|
1513
|
-
port:
|
|
1514
|
-
protocol:
|
|
1515
|
-
subdomains:
|
|
1512
|
+
host: str | None = None,
|
|
1513
|
+
port: int | None = None,
|
|
1514
|
+
protocol: str | None = None,
|
|
1515
|
+
subdomains: str | None = None,
|
|
1516
1516
|
) -> str:
|
|
1517
1517
|
"""Returns a service URL (e.g., SQS queue URL) to an external client (e.g., boto3) potentially running on another
|
|
1518
1518
|
machine than LocalStack. The configurations LOCALSTACK_HOST and USE_SSL can customize these returned URLs.
|
|
@@ -1529,10 +1529,10 @@ def external_service_url(
|
|
|
1529
1529
|
|
|
1530
1530
|
|
|
1531
1531
|
def internal_service_url(
|
|
1532
|
-
host:
|
|
1533
|
-
port:
|
|
1534
|
-
protocol:
|
|
1535
|
-
subdomains:
|
|
1532
|
+
host: str | None = None,
|
|
1533
|
+
port: int | None = None,
|
|
1534
|
+
protocol: str | None = None,
|
|
1535
|
+
subdomains: str | None = None,
|
|
1536
1536
|
) -> str:
|
|
1537
1537
|
"""Returns a service URL for internal use within LocalStack (i.e., same host).
|
|
1538
1538
|
The configuration USE_SSL can customize these returned URLs but LOCALSTACK_HOST has no effect.
|
localstack/packages/api.py
CHANGED
|
@@ -3,10 +3,11 @@ import functools
|
|
|
3
3
|
import logging
|
|
4
4
|
import os
|
|
5
5
|
from collections import defaultdict
|
|
6
|
+
from collections.abc import Callable
|
|
6
7
|
from enum import Enum
|
|
7
8
|
from inspect import getmodule
|
|
8
|
-
from threading import RLock
|
|
9
|
-
from typing import Any,
|
|
9
|
+
from threading import Lock, RLock
|
|
10
|
+
from typing import Any, Generic, ParamSpec, TypeVar
|
|
10
11
|
|
|
11
12
|
from plux import Plugin, PluginManager, PluginSpec # type: ignore
|
|
12
13
|
|
|
@@ -56,7 +57,7 @@ class PackageInstaller(abc.ABC):
|
|
|
56
57
|
multiple versions).
|
|
57
58
|
"""
|
|
58
59
|
|
|
59
|
-
def __init__(self, name: str, version: str, install_lock:
|
|
60
|
+
def __init__(self, name: str, version: str, install_lock: Lock | None = None):
|
|
60
61
|
"""
|
|
61
62
|
:param name: technical package name, f.e. "opensearch"
|
|
62
63
|
:param version: version of the package to install
|
|
@@ -70,7 +71,7 @@ class PackageInstaller(abc.ABC):
|
|
|
70
71
|
self.install_lock = install_lock or RLock()
|
|
71
72
|
self._setup_for_target: dict[InstallTarget, bool] = defaultdict(lambda: False)
|
|
72
73
|
|
|
73
|
-
def install(self, target:
|
|
74
|
+
def install(self, target: InstallTarget | None = None) -> None:
|
|
74
75
|
"""
|
|
75
76
|
Performs the package installation.
|
|
76
77
|
|
|
@@ -210,7 +211,7 @@ class Package(abc.ABC, Generic[T]):
|
|
|
210
211
|
"""
|
|
211
212
|
return self.get_installer(version).get_installed_dir()
|
|
212
213
|
|
|
213
|
-
def install(self, version: str | None = None, target:
|
|
214
|
+
def install(self, version: str | None = None, target: InstallTarget | None = None) -> None:
|
|
214
215
|
"""
|
|
215
216
|
Installs the package in the given version in the preferred target location.
|
|
216
217
|
:param version: version of the package to install. If None, the default version of the package will be used.
|
|
@@ -274,7 +275,7 @@ class MultiPackageInstaller(PackageInstaller):
|
|
|
274
275
|
assert len(package_installer) > 0
|
|
275
276
|
self.package_installer = package_installer
|
|
276
277
|
|
|
277
|
-
def install(self, target:
|
|
278
|
+
def install(self, target: InstallTarget | None = None) -> None:
|
|
278
279
|
"""
|
|
279
280
|
Installs the different packages this installer is composed of.
|
|
280
281
|
|
|
@@ -356,7 +357,7 @@ class PackagesPluginManager(PluginManager[PackagesPlugin]): # type: ignore[misc
|
|
|
356
357
|
)
|
|
357
358
|
|
|
358
359
|
def get_packages(
|
|
359
|
-
self, package_names: list[str], version:
|
|
360
|
+
self, package_names: list[str], version: str | None = None
|
|
360
361
|
) -> list[Package[PackageInstaller]]:
|
|
361
362
|
# Plugin names are unique, but there could be multiple packages with the same name in different scopes
|
|
362
363
|
plugin_specs_per_name = defaultdict(list)
|
|
@@ -390,7 +391,7 @@ T2 = TypeVar("T2")
|
|
|
390
391
|
def package(
|
|
391
392
|
name: str | None = None,
|
|
392
393
|
scope: str = "community",
|
|
393
|
-
should_load:
|
|
394
|
+
should_load: Callable[[], bool] | None = None,
|
|
394
395
|
) -> Callable[[Callable[[], Package[Any] | list[Package[Any]]]], PluginSpec]:
|
|
395
396
|
"""
|
|
396
397
|
Decorator for marking methods that create Package instances as a PackagePlugin.
|
localstack/packages/core.py
CHANGED
|
@@ -4,7 +4,7 @@ import re
|
|
|
4
4
|
from abc import ABC
|
|
5
5
|
from functools import lru_cache
|
|
6
6
|
from sys import version_info
|
|
7
|
-
from typing import Any
|
|
7
|
+
from typing import Any
|
|
8
8
|
|
|
9
9
|
import requests
|
|
10
10
|
|
|
@@ -238,7 +238,7 @@ class NodePackageInstaller(ExecutableInstaller):
|
|
|
238
238
|
self,
|
|
239
239
|
package_name: str,
|
|
240
240
|
version: str,
|
|
241
|
-
package_spec:
|
|
241
|
+
package_spec: str | None = None,
|
|
242
242
|
main_module: str = "main.js",
|
|
243
243
|
):
|
|
244
244
|
"""
|
|
@@ -748,6 +748,9 @@ class DynamoDBProvider(DynamodbApi, ServiceLifecycleHook):
|
|
|
748
748
|
if "NumberOfDecreasesToday" not in table_description["ProvisionedThroughput"]:
|
|
749
749
|
table_description["ProvisionedThroughput"]["NumberOfDecreasesToday"] = 0
|
|
750
750
|
|
|
751
|
+
if "WarmThroughput" in table_description:
|
|
752
|
+
table_description["WarmThroughput"]["Status"] = "UPDATING"
|
|
753
|
+
|
|
751
754
|
tags = table_definitions.pop("Tags", [])
|
|
752
755
|
if tags:
|
|
753
756
|
get_store(context.account_id, context.region).TABLE_TAGS[table_arn] = {
|
|
@@ -765,6 +768,13 @@ class DynamoDBProvider(DynamodbApi, ServiceLifecycleHook):
|
|
|
765
768
|
) -> DeleteTableOutput:
|
|
766
769
|
global_table_region = self.get_global_table_region(context, table_name)
|
|
767
770
|
|
|
771
|
+
self.ensure_table_exists(
|
|
772
|
+
context.account_id,
|
|
773
|
+
global_table_region,
|
|
774
|
+
table_name,
|
|
775
|
+
error_message=f"Requested resource not found: Table: {table_name} not found",
|
|
776
|
+
)
|
|
777
|
+
|
|
768
778
|
# Limitation note: On AWS, for a replicated table, if the source table is deleted, the replicated tables continue to exist.
|
|
769
779
|
# This is not the case for LocalStack, where all replicated tables will also be removed if source is deleted.
|
|
770
780
|
|
|
@@ -825,6 +835,9 @@ class DynamoDBProvider(DynamodbApi, ServiceLifecycleHook):
|
|
|
825
835
|
table_description["TableClassSummary"] = {
|
|
826
836
|
"TableClass": table_definitions["TableClass"]
|
|
827
837
|
}
|
|
838
|
+
if warm_throughput := table_definitions.get("WarmThroughput"):
|
|
839
|
+
table_description["WarmThroughput"] = warm_throughput.copy()
|
|
840
|
+
table_description["WarmThroughput"].setdefault("Status", "ACTIVE")
|
|
828
841
|
|
|
829
842
|
if "GlobalSecondaryIndexes" in table_description:
|
|
830
843
|
for gsi in table_description["GlobalSecondaryIndexes"]:
|
|
@@ -837,6 +850,17 @@ class DynamoDBProvider(DynamodbApi, ServiceLifecycleHook):
|
|
|
837
850
|
# Terraform depends on this parity for update operations
|
|
838
851
|
gsi["ProvisionedThroughput"] = default_values | gsi.get("ProvisionedThroughput", {})
|
|
839
852
|
|
|
853
|
+
# Set defaults for warm throughput
|
|
854
|
+
if "WarmThroughput" not in table_description:
|
|
855
|
+
billing_mode = table_definitions.get("BillingMode") if table_definitions else None
|
|
856
|
+
table_description["WarmThroughput"] = {
|
|
857
|
+
"ReadUnitsPerSecond": 12000 if billing_mode == "PAY_PER_REQUEST" else 5,
|
|
858
|
+
"WriteUnitsPerSecond": 4000 if billing_mode == "PAY_PER_REQUEST" else 5,
|
|
859
|
+
}
|
|
860
|
+
table_description["WarmThroughput"]["Status"] = (
|
|
861
|
+
table_description.get("TableStatus") or "ACTIVE"
|
|
862
|
+
)
|
|
863
|
+
|
|
840
864
|
return DescribeTableOutput(
|
|
841
865
|
Table=select_from_typed_dict(TableDescription, table_description)
|
|
842
866
|
)
|
|
@@ -560,6 +560,9 @@ class DynamoDBProvider(DynamodbApi, ServiceLifecycleHook):
|
|
|
560
560
|
if "NumberOfDecreasesToday" not in table_description["ProvisionedThroughput"]:
|
|
561
561
|
table_description["ProvisionedThroughput"]["NumberOfDecreasesToday"] = 0
|
|
562
562
|
|
|
563
|
+
if "WarmThroughput" in table_description:
|
|
564
|
+
table_description["WarmThroughput"]["Status"] = "UPDATING"
|
|
565
|
+
|
|
563
566
|
tags = table_definitions.pop("Tags", [])
|
|
564
567
|
if tags:
|
|
565
568
|
get_store(context.account_id, context.region).TABLE_TAGS[table_arn] = {
|
|
@@ -577,6 +580,13 @@ class DynamoDBProvider(DynamodbApi, ServiceLifecycleHook):
|
|
|
577
580
|
) -> DeleteTableOutput:
|
|
578
581
|
global_table_region = self.get_global_table_region(context, table_name)
|
|
579
582
|
|
|
583
|
+
self.ensure_table_exists(
|
|
584
|
+
context.account_id,
|
|
585
|
+
global_table_region,
|
|
586
|
+
table_name,
|
|
587
|
+
error_message=f"Requested resource not found: Table: {table_name} not found",
|
|
588
|
+
)
|
|
589
|
+
|
|
580
590
|
# Limitation note: On AWS, for a replicated table, if the source table is deleted, the replicated tables continue to exist.
|
|
581
591
|
# This is not the case for LocalStack, where all replicated tables will also be removed if source is deleted.
|
|
582
592
|
|
|
@@ -636,6 +646,9 @@ class DynamoDBProvider(DynamodbApi, ServiceLifecycleHook):
|
|
|
636
646
|
table_description["TableClassSummary"] = {
|
|
637
647
|
"TableClass": table_definitions["TableClass"]
|
|
638
648
|
}
|
|
649
|
+
if warm_throughput := table_definitions.get("WarmThroughput"):
|
|
650
|
+
table_description["WarmThroughput"] = warm_throughput.copy()
|
|
651
|
+
table_description["WarmThroughput"].setdefault("Status", "ACTIVE")
|
|
639
652
|
|
|
640
653
|
if "GlobalSecondaryIndexes" in table_description:
|
|
641
654
|
for gsi in table_description["GlobalSecondaryIndexes"]:
|
|
@@ -648,6 +661,17 @@ class DynamoDBProvider(DynamodbApi, ServiceLifecycleHook):
|
|
|
648
661
|
# Terraform depends on this parity for update operations
|
|
649
662
|
gsi["ProvisionedThroughput"] = default_values | gsi.get("ProvisionedThroughput", {})
|
|
650
663
|
|
|
664
|
+
# Set defaults for warm throughput
|
|
665
|
+
if "WarmThroughput" not in table_description:
|
|
666
|
+
billing_mode = table_definitions.get("BillingMode") if table_definitions else None
|
|
667
|
+
table_description["WarmThroughput"] = {
|
|
668
|
+
"ReadUnitsPerSecond": 12000 if billing_mode == "PAY_PER_REQUEST" else 5,
|
|
669
|
+
"WriteUnitsPerSecond": 4000 if billing_mode == "PAY_PER_REQUEST" else 5,
|
|
670
|
+
}
|
|
671
|
+
table_description["WarmThroughput"]["Status"] = (
|
|
672
|
+
table_description.get("TableStatus") or "ACTIVE"
|
|
673
|
+
)
|
|
674
|
+
|
|
651
675
|
return DescribeTableOutput(
|
|
652
676
|
Table=select_from_typed_dict(TableDescription, table_description)
|
|
653
677
|
)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import json
|
|
2
2
|
from collections import defaultdict
|
|
3
|
-
from collections.abc import Generator
|
|
4
|
-
from typing import
|
|
3
|
+
from collections.abc import Callable, Generator
|
|
4
|
+
from typing import TypedDict
|
|
5
5
|
|
|
6
6
|
import pytest
|
|
7
7
|
from botocore.exceptions import WaiterError
|
|
@@ -13,7 +13,7 @@ from localstack.utils.strings import short_uid
|
|
|
13
13
|
|
|
14
14
|
|
|
15
15
|
class NormalizedEvent(TypedDict):
|
|
16
|
-
PhysicalResourceId:
|
|
16
|
+
PhysicalResourceId: str | None
|
|
17
17
|
LogicalResourceId: str
|
|
18
18
|
ResourceType: str
|
|
19
19
|
ResourceStatus: str
|
|
@@ -2,8 +2,7 @@ import logging
|
|
|
2
2
|
import os
|
|
3
3
|
import shlex
|
|
4
4
|
import threading
|
|
5
|
-
from collections.abc import Generator
|
|
6
|
-
from typing import Callable, Optional
|
|
5
|
+
from collections.abc import Callable, Generator
|
|
7
6
|
|
|
8
7
|
import pytest
|
|
9
8
|
|
|
@@ -38,8 +37,8 @@ class ContainerFactory:
|
|
|
38
37
|
self,
|
|
39
38
|
# convenience properties
|
|
40
39
|
pro: bool = False,
|
|
41
|
-
publish:
|
|
42
|
-
configurators:
|
|
40
|
+
publish: list[int] | None = None,
|
|
41
|
+
configurators: list[ContainerConfigurator] | None = None,
|
|
43
42
|
# ContainerConfig properties
|
|
44
43
|
**kwargs,
|
|
45
44
|
) -> Container:
|
|
@@ -172,7 +171,7 @@ def container_factory() -> Generator[ContainerFactory, None, None]:
|
|
|
172
171
|
|
|
173
172
|
@pytest.fixture(scope="session")
|
|
174
173
|
def wait_for_localstack_ready():
|
|
175
|
-
def _wait_for(container: RunningContainer, timeout:
|
|
174
|
+
def _wait_for(container: RunningContainer, timeout: float | None = None):
|
|
176
175
|
container.wait_until_ready(timeout)
|
|
177
176
|
|
|
178
177
|
poll_condition(
|
|
@@ -6,7 +6,8 @@ import os
|
|
|
6
6
|
import re
|
|
7
7
|
import textwrap
|
|
8
8
|
import time
|
|
9
|
-
from
|
|
9
|
+
from collections.abc import Callable
|
|
10
|
+
from typing import TYPE_CHECKING, Any, Unpack
|
|
10
11
|
|
|
11
12
|
import botocore.auth
|
|
12
13
|
import botocore.config
|
|
@@ -357,8 +358,8 @@ def sqs_create_queue(aws_client):
|
|
|
357
358
|
def sqs_receive_messages_delete(aws_client):
|
|
358
359
|
def factory(
|
|
359
360
|
queue_url: str,
|
|
360
|
-
expected_messages:
|
|
361
|
-
wait_time:
|
|
361
|
+
expected_messages: int | None = None,
|
|
362
|
+
wait_time: int | None = 5,
|
|
362
363
|
):
|
|
363
364
|
response = aws_client.sqs.receive_message(
|
|
364
365
|
QueueUrl=queue_url,
|
|
@@ -706,7 +707,7 @@ def route53_hosted_zone(aws_client):
|
|
|
706
707
|
def transcribe_create_job(s3_bucket, aws_client):
|
|
707
708
|
job_names = []
|
|
708
709
|
|
|
709
|
-
def _create_job(audio_file: str, params:
|
|
710
|
+
def _create_job(audio_file: str, params: dict[str, Any] | None = None) -> str:
|
|
710
711
|
s3_key = "test-clip.wav"
|
|
711
712
|
|
|
712
713
|
if not params:
|
|
@@ -1085,18 +1086,18 @@ def deploy_cfn_template(
|
|
|
1085
1086
|
|
|
1086
1087
|
def _deploy(
|
|
1087
1088
|
*,
|
|
1088
|
-
is_update:
|
|
1089
|
-
stack_name:
|
|
1090
|
-
change_set_name:
|
|
1091
|
-
template:
|
|
1092
|
-
template_path:
|
|
1093
|
-
template_mapping:
|
|
1094
|
-
parameters:
|
|
1095
|
-
role_arn:
|
|
1096
|
-
max_wait:
|
|
1097
|
-
delay_between_polls:
|
|
1098
|
-
custom_aws_client:
|
|
1099
|
-
raw_parameters:
|
|
1089
|
+
is_update: bool | None = False,
|
|
1090
|
+
stack_name: str | None = None,
|
|
1091
|
+
change_set_name: str | None = None,
|
|
1092
|
+
template: str | None = None,
|
|
1093
|
+
template_path: str | os.PathLike | None = None,
|
|
1094
|
+
template_mapping: dict[str, Any] | None = None,
|
|
1095
|
+
parameters: dict[str, str] | None = None,
|
|
1096
|
+
role_arn: str | None = None,
|
|
1097
|
+
max_wait: int | None = None,
|
|
1098
|
+
delay_between_polls: int | None = 2,
|
|
1099
|
+
custom_aws_client: ServiceLevelClientFactory | None = None,
|
|
1100
|
+
raw_parameters: list[Parameter] | None = None,
|
|
1100
1101
|
) -> DeployResult:
|
|
1101
1102
|
if is_update:
|
|
1102
1103
|
assert stack_name
|
|
@@ -1262,7 +1263,7 @@ def _has_stack_status(cfn_client, statuses: list[str]):
|
|
|
1262
1263
|
|
|
1263
1264
|
@pytest.fixture
|
|
1264
1265
|
def is_change_set_finished(aws_client):
|
|
1265
|
-
def _is_change_set_finished(change_set_id: str, stack_name:
|
|
1266
|
+
def _is_change_set_finished(change_set_id: str, stack_name: str | None = None):
|
|
1266
1267
|
def _inner():
|
|
1267
1268
|
kwargs = {"ChangeSetName": change_set_id}
|
|
1268
1269
|
if stack_name:
|
|
@@ -1993,7 +1994,7 @@ def setup_sender_email_address(ses_verify_identity):
|
|
|
1993
1994
|
email address and verify them.
|
|
1994
1995
|
"""
|
|
1995
1996
|
|
|
1996
|
-
def inner(sender_email_address:
|
|
1997
|
+
def inner(sender_email_address: str | None = None) -> str:
|
|
1997
1998
|
if is_aws_cloud():
|
|
1998
1999
|
if sender_email_address is None:
|
|
1999
2000
|
raise ValueError(
|
|
@@ -2252,7 +2253,7 @@ def assert_host_customisation(monkeypatch):
|
|
|
2252
2253
|
def asserter(
|
|
2253
2254
|
url: str,
|
|
2254
2255
|
*,
|
|
2255
|
-
custom_host:
|
|
2256
|
+
custom_host: str | None = None,
|
|
2256
2257
|
):
|
|
2257
2258
|
if custom_host is not None:
|
|
2258
2259
|
assert custom_host in url, f"Could not find `{custom_host}` in `{url}`"
|
|
@@ -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:
|
|
@@ -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
|
|
|
@@ -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
|
|
@@ -201,7 +200,7 @@ def _generate_machine_id() -> str:
|
|
|
201
200
|
return f"gen_{long_uid()[:12]}"
|
|
202
201
|
|
|
203
202
|
|
|
204
|
-
def get_api_key_or_auth_token() ->
|
|
203
|
+
def get_api_key_or_auth_token() -> str | None:
|
|
205
204
|
# TODO: this is duplicated code from ext, but should probably migrate that to localstack
|
|
206
205
|
auth_token = os.environ.get("LOCALSTACK_AUTH_TOKEN", "").strip("'\" ")
|
|
207
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 = {
|
|
@@ -140,10 +140,8 @@ 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
147
|
super().__init__(namespace=namespace, name=name, schema_version=schema_version)
|
|
@@ -162,7 +160,7 @@ class LabeledCounter(Metric):
|
|
|
162
160
|
self._counters_by_label_values = defaultdict(ThreadSafeCounter)
|
|
163
161
|
MetricRegistry().register(self)
|
|
164
162
|
|
|
165
|
-
def labels(self, **kwargs:
|
|
163
|
+
def labels(self, **kwargs: str | float | None) -> ThreadSafeCounter:
|
|
166
164
|
"""
|
|
167
165
|
Create a scoped counter instance with specific label values.
|
|
168
166
|
|
|
@@ -198,7 +196,7 @@ class LabeledCounter(Metric):
|
|
|
198
196
|
)
|
|
199
197
|
|
|
200
198
|
# Create labels dictionary
|
|
201
|
-
labels_dict = dict(zip(self._labels, label_values))
|
|
199
|
+
labels_dict = dict(zip(self._labels, label_values, strict=False))
|
|
202
200
|
|
|
203
201
|
payload.append(
|
|
204
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
|
|
|
@@ -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:
|