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.

Files changed (70) hide show
  1. localstack/aws/handlers/metric_handler.py +41 -1
  2. localstack/cli/exceptions.py +1 -1
  3. localstack/cli/localstack.py +4 -4
  4. localstack/cli/lpm.py +3 -4
  5. localstack/cli/profiles.py +1 -2
  6. localstack/config.py +18 -12
  7. localstack/constants.py +4 -0
  8. localstack/packages/api.py +9 -8
  9. localstack/packages/core.py +2 -2
  10. localstack/testing/pytest/cloudformation/fixtures.py +3 -3
  11. localstack/testing/pytest/container.py +4 -5
  12. localstack/testing/pytest/fixtures.py +20 -19
  13. localstack/testing/pytest/marking.py +5 -4
  14. localstack/testing/pytest/stepfunctions/utils.py +4 -3
  15. localstack/testing/pytest/util.py +1 -1
  16. localstack/testing/pytest/validation_tracking.py +1 -2
  17. localstack/utils/analytics/events.py +2 -2
  18. localstack/utils/analytics/metadata.py +1 -2
  19. localstack/utils/analytics/metrics/counter.py +6 -8
  20. localstack/utils/analytics/publisher.py +1 -2
  21. localstack/utils/analytics/service_request_aggregator.py +2 -2
  22. localstack/utils/archives.py +11 -11
  23. localstack/utils/aws/arns.py +7 -7
  24. localstack/utils/aws/aws_responses.py +7 -7
  25. localstack/utils/aws/aws_stack.py +2 -3
  26. localstack/utils/aws/message_forwarding.py +1 -2
  27. localstack/utils/aws/request_context.py +4 -5
  28. localstack/utils/batch_policy.py +3 -3
  29. localstack/utils/bootstrap.py +7 -7
  30. localstack/utils/cloudwatch/cloudwatch_util.py +5 -5
  31. localstack/utils/collections.py +7 -8
  32. localstack/utils/config_listener.py +1 -1
  33. localstack/utils/container_networking.py +2 -3
  34. localstack/utils/container_utils/container_client.py +115 -131
  35. localstack/utils/container_utils/docker_cmd_client.py +42 -42
  36. localstack/utils/container_utils/docker_sdk_client.py +63 -62
  37. localstack/utils/diagnose.py +2 -3
  38. localstack/utils/docker_utils.py +3 -4
  39. localstack/utils/functions.py +3 -2
  40. localstack/utils/http.py +4 -5
  41. localstack/utils/json.py +3 -3
  42. localstack/utils/kinesis/kinesis_connector.py +2 -1
  43. localstack/utils/net.py +6 -6
  44. localstack/utils/no_exit_argument_parser.py +2 -2
  45. localstack/utils/numbers.py +2 -2
  46. localstack/utils/objects.py +6 -5
  47. localstack/utils/patch.py +2 -1
  48. localstack/utils/run.py +10 -9
  49. localstack/utils/scheduler.py +11 -11
  50. localstack/utils/server/tcp_proxy.py +2 -2
  51. localstack/utils/serving.py +2 -3
  52. localstack/utils/strings.py +10 -11
  53. localstack/utils/sync.py +2 -1
  54. localstack/utils/tagging.py +1 -4
  55. localstack/utils/testutil.py +5 -4
  56. localstack/utils/threads.py +2 -2
  57. localstack/utils/time.py +1 -2
  58. localstack/utils/urls.py +1 -3
  59. localstack/version.py +2 -2
  60. {localstack_core-4.9.3.dev25.dist-info → localstack_core-4.9.3.dev27.dist-info}/METADATA +2 -2
  61. {localstack_core-4.9.3.dev25.dist-info → localstack_core-4.9.3.dev27.dist-info}/RECORD +69 -69
  62. localstack_core-4.9.3.dev27.dist-info/plux.json +1 -0
  63. localstack_core-4.9.3.dev25.dist-info/plux.json +0 -1
  64. {localstack_core-4.9.3.dev25.data → localstack_core-4.9.3.dev27.data}/scripts/localstack +0 -0
  65. {localstack_core-4.9.3.dev25.data → localstack_core-4.9.3.dev27.data}/scripts/localstack-supervisor +0 -0
  66. {localstack_core-4.9.3.dev25.data → localstack_core-4.9.3.dev27.data}/scripts/localstack.bat +0 -0
  67. {localstack_core-4.9.3.dev25.dist-info → localstack_core-4.9.3.dev27.dist-info}/WHEEL +0 -0
  68. {localstack_core-4.9.3.dev25.dist-info → localstack_core-4.9.3.dev27.dist-info}/entry_points.txt +0 -0
  69. {localstack_core-4.9.3.dev25.dist-info → localstack_core-4.9.3.dev27.dist-info}/licenses/LICENSE.txt +0 -0
  70. {localstack_core-4.9.3.dev25.dist-info → localstack_core-4.9.3.dev27.dist-info}/top_level.txt +0 -0
@@ -6,7 +6,7 @@ import os
6
6
  import re
7
7
  import shlex
8
8
  import subprocess
9
- from typing import Callable, Optional, Union
9
+ from collections.abc import Callable
10
10
 
11
11
  from localstack import config
12
12
  from localstack.utils.collections import ensure_list
@@ -96,7 +96,7 @@ class CmdDockerClient(ContainerClient):
96
96
  different response payloads or error messages returned by the `docker` vs `podman` commands.
97
97
  """
98
98
 
99
- default_run_outfile: Optional[str] = None
99
+ default_run_outfile: str | None = None
100
100
 
101
101
  def _docker_cmd(self) -> list[str]:
102
102
  """
@@ -289,7 +289,7 @@ class CmdDockerClient(ContainerClient):
289
289
  f"Docker process returned with errorcode {e.returncode}", e.stdout, e.stderr
290
290
  ) from e
291
291
 
292
- def list_containers(self, filter: Union[list[str], str, None] = None, all=True) -> list[dict]:
292
+ def list_containers(self, filter: list[str] | str | None = None, all=True) -> list[dict]:
293
293
  filter = [filter] if isinstance(filter, str) else filter
294
294
  cmd = self._docker_cmd()
295
295
  cmd.append("ps")
@@ -365,8 +365,8 @@ class CmdDockerClient(ContainerClient):
365
365
  def pull_image(
366
366
  self,
367
367
  docker_image: str,
368
- platform: Optional[DockerPlatform] = None,
369
- log_handler: Optional[Callable[[str], None]] = None,
368
+ platform: DockerPlatform | None = None,
369
+ log_handler: Callable[[str], None] | None = None,
370
370
  ) -> None:
371
371
  cmd = self._docker_cmd()
372
372
  docker_image = self.registry_resolver_strategy.resolve(docker_image)
@@ -420,7 +420,7 @@ class CmdDockerClient(ContainerClient):
420
420
  dockerfile_path: str,
421
421
  image_name: str,
422
422
  context_path: str = None,
423
- platform: Optional[DockerPlatform] = None,
423
+ platform: DockerPlatform | None = None,
424
424
  ):
425
425
  cmd = self._docker_cmd()
426
426
  dockerfile_path = Util.resolve_dockerfile_path(dockerfile_path)
@@ -497,7 +497,7 @@ class CmdDockerClient(ContainerClient):
497
497
 
498
498
  return CancellableProcessStream(process)
499
499
 
500
- def _inspect_object(self, object_name_or_id: str) -> dict[str, Union[dict, list, str]]:
500
+ def _inspect_object(self, object_name_or_id: str) -> dict[str, dict | list | str]:
501
501
  cmd = self._docker_cmd()
502
502
  cmd += ["inspect", "--format", "{{json .}}", object_name_or_id]
503
503
  try:
@@ -524,7 +524,7 @@ class CmdDockerClient(ContainerClient):
524
524
  )
525
525
  return object_data
526
526
 
527
- def inspect_container(self, container_name_or_id: str) -> dict[str, Union[dict, str]]:
527
+ def inspect_container(self, container_name_or_id: str) -> dict[str, dict | str]:
528
528
  try:
529
529
  return self._inspect_object(container_name_or_id)
530
530
  except NoSuchObject as e:
@@ -535,7 +535,7 @@ class CmdDockerClient(ContainerClient):
535
535
  image_name: str,
536
536
  pull: bool = True,
537
537
  strip_wellknown_repo_prefixes: bool = True,
538
- ) -> dict[str, Union[dict, list, str]]:
538
+ ) -> dict[str, dict | list | str]:
539
539
  image_name = self.registry_resolver_strategy.resolve(image_name)
540
540
  try:
541
541
  result = self._inspect_object(image_name)
@@ -577,7 +577,7 @@ class CmdDockerClient(ContainerClient):
577
577
  f"Docker process returned with errorcode {e.returncode}", e.stdout, e.stderr
578
578
  ) from e
579
579
 
580
- def inspect_network(self, network_name: str) -> dict[str, Union[dict, str]]:
580
+ def inspect_network(self, network_name: str) -> dict[str, dict | str]:
581
581
  try:
582
582
  return self._inspect_object(network_name)
583
583
  except NoSuchObject as e:
@@ -587,7 +587,7 @@ class CmdDockerClient(ContainerClient):
587
587
  self,
588
588
  network_name: str,
589
589
  container_name_or_id: str,
590
- aliases: Optional[list] = None,
590
+ aliases: list | None = None,
591
591
  link_local_ips: list[str] = None,
592
592
  ) -> None:
593
593
  LOG.debug(
@@ -652,7 +652,7 @@ class CmdDockerClient(ContainerClient):
652
652
  f"Docker process returned with errorcode {e.returncode}", e.stdout, e.stderr
653
653
  ) from e
654
654
 
655
- def login(self, username: str, password: str, registry: Optional[str] = None) -> None:
655
+ def login(self, username: str, password: str, registry: str | None = None) -> None:
656
656
  cmd = self._docker_cmd()
657
657
  # TODO specify password via stdin
658
658
  cmd += ["login", "-u", username, "-p", password]
@@ -709,13 +709,13 @@ class CmdDockerClient(ContainerClient):
709
709
  def exec_in_container(
710
710
  self,
711
711
  container_name_or_id: str,
712
- command: Union[list[str], str],
712
+ command: list[str] | str,
713
713
  interactive=False,
714
714
  detach=False,
715
- env_vars: Optional[dict[str, Optional[str]]] = None,
716
- stdin: Optional[bytes] = None,
717
- user: Optional[str] = None,
718
- workdir: Optional[str] = None,
715
+ env_vars: dict[str, str | None] | None = None,
716
+ stdin: bytes | None = None,
717
+ user: str | None = None,
718
+ workdir: str | None = None,
719
719
  ) -> tuple[bytes, bytes]:
720
720
  env_file = None
721
721
  cmd = self._docker_cmd()
@@ -745,7 +745,7 @@ class CmdDockerClient(ContainerClient):
745
745
  stdin=None,
746
746
  interactive: bool = False,
747
747
  attach: bool = False,
748
- flags: Optional[str] = None,
748
+ flags: str | None = None,
749
749
  ) -> tuple[bytes, bytes]:
750
750
  cmd = self._docker_cmd() + ["start"]
751
751
  if flags:
@@ -803,31 +803,31 @@ class CmdDockerClient(ContainerClient):
803
803
  action: str,
804
804
  image_name: str,
805
805
  *,
806
- name: Optional[str] = None,
807
- entrypoint: Optional[Union[list[str], str]] = None,
806
+ name: str | None = None,
807
+ entrypoint: list[str] | str | None = None,
808
808
  remove: bool = False,
809
809
  interactive: bool = False,
810
810
  tty: bool = False,
811
811
  detach: bool = False,
812
- command: Optional[Union[list[str], str]] = None,
813
- volumes: Optional[list[SimpleVolumeBind]] = None,
814
- ports: Optional[PortMappings] = None,
815
- exposed_ports: Optional[list[str]] = None,
816
- env_vars: Optional[dict[str, str]] = None,
817
- user: Optional[str] = None,
818
- cap_add: Optional[list[str]] = None,
819
- cap_drop: Optional[list[str]] = None,
820
- security_opt: Optional[list[str]] = None,
821
- network: Optional[str] = None,
822
- dns: Optional[Union[str, list[str]]] = None,
823
- additional_flags: Optional[str] = None,
824
- workdir: Optional[str] = None,
825
- privileged: Optional[bool] = None,
826
- labels: Optional[dict[str, str]] = None,
827
- platform: Optional[DockerPlatform] = None,
828
- ulimits: Optional[list[Ulimit]] = None,
829
- init: Optional[bool] = None,
830
- log_config: Optional[LogConfig] = None,
812
+ command: list[str] | str | None = None,
813
+ volumes: list[SimpleVolumeBind] | None = None,
814
+ ports: PortMappings | None = None,
815
+ exposed_ports: list[str] | None = None,
816
+ env_vars: dict[str, str] | None = None,
817
+ user: str | None = None,
818
+ cap_add: list[str] | None = None,
819
+ cap_drop: list[str] | None = None,
820
+ security_opt: list[str] | None = None,
821
+ network: str | None = None,
822
+ dns: str | list[str] | None = None,
823
+ additional_flags: str | None = None,
824
+ workdir: str | None = None,
825
+ privileged: bool | None = None,
826
+ labels: dict[str, str] | None = None,
827
+ platform: DockerPlatform | None = None,
828
+ ulimits: list[Ulimit] | None = None,
829
+ init: bool | None = None,
830
+ log_config: LogConfig | None = None,
831
831
  ) -> tuple[list[str], str]:
832
832
  env_file = None
833
833
  cmd = self._docker_cmd() + [action]
@@ -900,7 +900,7 @@ class CmdDockerClient(ContainerClient):
900
900
  return cmd, env_file
901
901
 
902
902
  @staticmethod
903
- def _map_to_volume_param(volume: Union[SimpleVolumeBind, BindMount, VolumeDirMount]) -> str:
903
+ def _map_to_volume_param(volume: SimpleVolumeBind | BindMount | VolumeDirMount) -> str:
904
904
  """
905
905
  Maps the mount volume, to a parameter for the -v docker cli argument.
906
906
 
@@ -928,7 +928,7 @@ class CmdDockerClient(ContainerClient):
928
928
  )
929
929
 
930
930
  def _check_output_and_raise_no_such_container_error(
931
- self, container_name_or_id: str, output: str, error: Optional[str] = None
931
+ self, container_name_or_id: str, output: str, error: str | None = None
932
932
  ):
933
933
  """
934
934
  Check the given client invocation output and raise a `NoSuchContainer` exception if it
@@ -938,7 +938,7 @@ class CmdDockerClient(ContainerClient):
938
938
  if any(msg.lower() in output.lower() for msg in possible_not_found_messages):
939
939
  raise NoSuchContainer(container_name_or_id, stdout=output, stderr=error)
940
940
 
941
- def _transform_container_labels(self, labels: Union[str, dict[str, str]]) -> dict[str, str]:
941
+ def _transform_container_labels(self, labels: str | dict[str, str]) -> dict[str, str]:
942
942
  """
943
943
  Transforms the container labels returned by the docker command from the key-value pair format to a dict
944
944
  :param labels: Input string, comma separated key value pairs. Example: key1=value1,key2=value2
@@ -6,9 +6,10 @@ import queue
6
6
  import re
7
7
  import socket
8
8
  import threading
9
+ from collections.abc import Callable
9
10
  from functools import cache
10
11
  from time import sleep
11
- from typing import Callable, Optional, Union, cast
12
+ from typing import cast
12
13
  from urllib.parse import quote
13
14
 
14
15
  import docker
@@ -56,7 +57,7 @@ class SdkDockerClient(ContainerClient):
56
57
  is doing some of the heavy lifting for us to support both target platforms.
57
58
  """
58
59
 
59
- docker_client: Optional[DockerClient]
60
+ docker_client: DockerClient | None
60
61
 
61
62
  def __init__(self):
62
63
  try:
@@ -280,7 +281,7 @@ class SdkDockerClient(ContainerClient):
280
281
  except APIError as e:
281
282
  raise ContainerException() from e
282
283
 
283
- def list_containers(self, filter: Union[list[str], str, None] = None, all=True) -> list[dict]:
284
+ def list_containers(self, filter: list[str] | str | None = None, all=True) -> list[dict]:
284
285
  if filter:
285
286
  filter = [filter] if isinstance(filter, str) else filter
286
287
  filter = dict([f.split("=", 1) for f in filter])
@@ -339,14 +340,14 @@ class SdkDockerClient(ContainerClient):
339
340
  def pull_image(
340
341
  self,
341
342
  docker_image: str,
342
- platform: Optional[DockerPlatform] = None,
343
- log_handler: Optional[Callable[[str], None]] = None,
343
+ platform: DockerPlatform | None = None,
344
+ log_handler: Callable[[str], None] | None = None,
344
345
  ) -> None:
345
346
  LOG.debug("Pulling Docker image: %s", docker_image)
346
347
  # some path in the docker image string indicates a custom repository
347
348
 
348
349
  docker_image = self.registry_resolver_strategy.resolve(docker_image)
349
- kwargs: dict[str, Union[str, bool]] = {"platform": platform}
350
+ kwargs: dict[str, str | bool] = {"platform": platform}
350
351
  try:
351
352
  if log_handler:
352
353
  # Use a lower-level API, as the 'stream' argument is not available in the higher-level `pull`-API
@@ -391,7 +392,7 @@ class SdkDockerClient(ContainerClient):
391
392
  dockerfile_path: str,
392
393
  image_name: str,
393
394
  context_path: str = None,
394
- platform: Optional[DockerPlatform] = None,
395
+ platform: DockerPlatform | None = None,
395
396
  ):
396
397
  try:
397
398
  dockerfile_path = Util.resolve_dockerfile_path(dockerfile_path)
@@ -468,7 +469,7 @@ class SdkDockerClient(ContainerClient):
468
469
  except APIError as e:
469
470
  raise ContainerException() from e
470
471
 
471
- def inspect_container(self, container_name_or_id: str) -> dict[str, Union[dict, str]]:
472
+ def inspect_container(self, container_name_or_id: str) -> dict[str, dict | str]:
472
473
  try:
473
474
  return self.client().containers.get(container_name_or_id).attrs
474
475
  except NotFound:
@@ -481,7 +482,7 @@ class SdkDockerClient(ContainerClient):
481
482
  image_name: str,
482
483
  pull: bool = True,
483
484
  strip_wellknown_repo_prefixes: bool = True,
484
- ) -> dict[str, Union[dict, list, str]]:
485
+ ) -> dict[str, dict | list | str]:
485
486
  image_name = self.registry_resolver_strategy.resolve(image_name)
486
487
  try:
487
488
  result = self.client().images.get(image_name).attrs
@@ -515,7 +516,7 @@ class SdkDockerClient(ContainerClient):
515
516
  except APIError as e:
516
517
  raise ContainerException() from e
517
518
 
518
- def inspect_network(self, network_name: str) -> dict[str, Union[dict, str]]:
519
+ def inspect_network(self, network_name: str) -> dict[str, dict | str]:
519
520
  try:
520
521
  return self.client().networks.get(network_name).attrs
521
522
  except NotFound:
@@ -527,7 +528,7 @@ class SdkDockerClient(ContainerClient):
527
528
  self,
528
529
  network_name: str,
529
530
  container_name_or_id: str,
530
- aliases: Optional[list] = None,
531
+ aliases: list | None = None,
531
532
  link_local_ips: list[str] = None,
532
533
  ) -> None:
533
534
  LOG.debug(
@@ -621,7 +622,7 @@ class SdkDockerClient(ContainerClient):
621
622
  stdin=None,
622
623
  interactive: bool = False,
623
624
  attach: bool = False,
624
- flags: Optional[str] = None,
625
+ flags: str | None = None,
625
626
  ) -> tuple[bytes, bytes]:
626
627
  LOG.debug("Starting container %s", container_name_or_id)
627
628
  try:
@@ -670,7 +671,7 @@ class SdkDockerClient(ContainerClient):
670
671
  sock.sendall(to_bytes(stdin))
671
672
  sock.shutdown(socket.SHUT_WR)
672
673
  stdout, stderr = self._read_from_sock(sock, False)
673
- except socket.timeout:
674
+ except TimeoutError:
674
675
  LOG.debug(
675
676
  "Socket timeout when talking to the I/O streams of Docker container '%s'",
676
677
  container_name_or_id,
@@ -703,31 +704,31 @@ class SdkDockerClient(ContainerClient):
703
704
  self,
704
705
  image_name: str,
705
706
  *,
706
- name: Optional[str] = None,
707
- entrypoint: Optional[Union[list[str], str]] = None,
707
+ name: str | None = None,
708
+ entrypoint: list[str] | str | None = None,
708
709
  remove: bool = False,
709
710
  interactive: bool = False,
710
711
  tty: bool = False,
711
712
  detach: bool = False,
712
- command: Optional[Union[list[str], str]] = None,
713
- volumes: Optional[list[SimpleVolumeBind]] = None,
714
- ports: Optional[PortMappings] = None,
715
- exposed_ports: Optional[list[str]] = None,
716
- env_vars: Optional[dict[str, str]] = None,
717
- user: Optional[str] = None,
718
- cap_add: Optional[list[str]] = None,
719
- cap_drop: Optional[list[str]] = None,
720
- security_opt: Optional[list[str]] = None,
721
- network: Optional[str] = None,
722
- dns: Optional[Union[str, list[str]]] = None,
723
- additional_flags: Optional[str] = None,
724
- workdir: Optional[str] = None,
725
- privileged: Optional[bool] = None,
726
- labels: Optional[dict[str, str]] = None,
727
- platform: Optional[DockerPlatform] = None,
728
- ulimits: Optional[list[Ulimit]] = None,
729
- init: Optional[bool] = None,
730
- log_config: Optional[LogConfig] = None,
713
+ command: list[str] | str | None = None,
714
+ volumes: list[SimpleVolumeBind] | None = None,
715
+ ports: PortMappings | None = None,
716
+ exposed_ports: list[str] | None = None,
717
+ env_vars: dict[str, str] | None = None,
718
+ user: str | None = None,
719
+ cap_add: list[str] | None = None,
720
+ cap_drop: list[str] | None = None,
721
+ security_opt: list[str] | None = None,
722
+ network: str | None = None,
723
+ dns: str | list[str] | None = None,
724
+ additional_flags: str | None = None,
725
+ workdir: str | None = None,
726
+ privileged: bool | None = None,
727
+ labels: dict[str, str] | None = None,
728
+ platform: DockerPlatform | None = None,
729
+ ulimits: list[Ulimit] | None = None,
730
+ init: bool | None = None,
731
+ log_config: LogConfig | None = None,
731
732
  ) -> str:
732
733
  LOG.debug("Creating container with attributes: %s", locals())
733
734
  extra_hosts = None
@@ -834,31 +835,31 @@ class SdkDockerClient(ContainerClient):
834
835
  image_name: str,
835
836
  stdin=None,
836
837
  *,
837
- name: Optional[str] = None,
838
- entrypoint: Optional[str] = None,
838
+ name: str | None = None,
839
+ entrypoint: str | None = None,
839
840
  remove: bool = False,
840
841
  interactive: bool = False,
841
842
  tty: bool = False,
842
843
  detach: bool = False,
843
- command: Optional[Union[list[str], str]] = None,
844
- volumes: Optional[list[SimpleVolumeBind]] = None,
845
- ports: Optional[PortMappings] = None,
846
- exposed_ports: Optional[list[str]] = None,
847
- env_vars: Optional[dict[str, str]] = None,
848
- user: Optional[str] = None,
849
- cap_add: Optional[list[str]] = None,
850
- cap_drop: Optional[list[str]] = None,
851
- security_opt: Optional[list[str]] = None,
852
- network: Optional[str] = None,
853
- dns: Optional[str] = None,
854
- additional_flags: Optional[str] = None,
855
- workdir: Optional[str] = None,
856
- labels: Optional[dict[str, str]] = None,
857
- platform: Optional[DockerPlatform] = None,
858
- privileged: Optional[bool] = None,
859
- ulimits: Optional[list[Ulimit]] = None,
860
- init: Optional[bool] = None,
861
- log_config: Optional[LogConfig] = None,
844
+ command: list[str] | str | None = None,
845
+ volumes: list[SimpleVolumeBind] | None = None,
846
+ ports: PortMappings | None = None,
847
+ exposed_ports: list[str] | None = None,
848
+ env_vars: dict[str, str] | None = None,
849
+ user: str | None = None,
850
+ cap_add: list[str] | None = None,
851
+ cap_drop: list[str] | None = None,
852
+ security_opt: list[str] | None = None,
853
+ network: str | None = None,
854
+ dns: str | None = None,
855
+ additional_flags: str | None = None,
856
+ workdir: str | None = None,
857
+ labels: dict[str, str] | None = None,
858
+ platform: DockerPlatform | None = None,
859
+ privileged: bool | None = None,
860
+ ulimits: list[Ulimit] | None = None,
861
+ init: bool | None = None,
862
+ log_config: LogConfig | None = None,
862
863
  ) -> tuple[bytes, bytes]:
863
864
  LOG.debug("Running container with image: %s", image_name)
864
865
  container = None
@@ -905,13 +906,13 @@ class SdkDockerClient(ContainerClient):
905
906
  def exec_in_container(
906
907
  self,
907
908
  container_name_or_id: str,
908
- command: Union[list[str], str],
909
+ command: list[str] | str,
909
910
  interactive=False,
910
911
  detach=False,
911
- env_vars: Optional[dict[str, Optional[str]]] = None,
912
- stdin: Optional[bytes] = None,
913
- user: Optional[str] = None,
914
- workdir: Optional[str] = None,
912
+ env_vars: dict[str, str | None] | None = None,
913
+ stdin: bytes | None = None,
914
+ user: str | None = None,
915
+ workdir: str | None = None,
915
916
  ) -> tuple[bytes, bytes]:
916
917
  LOG.debug("Executing command in container %s: %s", container_name_or_id, command)
917
918
  try:
@@ -938,7 +939,7 @@ class SdkDockerClient(ContainerClient):
938
939
  sock.shutdown(socket.SHUT_WR)
939
940
  stdout, stderr = self._read_from_sock(sock, tty)
940
941
  return stdout, stderr
941
- except socket.timeout:
942
+ except TimeoutError:
942
943
  pass
943
944
  else:
944
945
  if detach:
@@ -959,7 +960,7 @@ class SdkDockerClient(ContainerClient):
959
960
  except APIError as e:
960
961
  raise ContainerException() from e
961
962
 
962
- def login(self, username: str, password: str, registry: Optional[str] = None) -> None:
963
+ def login(self, username: str, password: str, registry: str | None = None) -> None:
963
964
  LOG.debug("Docker login for %s", username)
964
965
  try:
965
966
  self.client().login(username, password=password, registry=registry, reauth=True)
@@ -3,7 +3,6 @@
3
3
  import inspect
4
4
  import os
5
5
  import socket
6
- from typing import Optional, Union
7
6
 
8
7
  from localstack import config
9
8
  from localstack.constants import DEFAULT_VOLUME_DIR
@@ -77,14 +76,14 @@ def get_localstack_config() -> dict:
77
76
  return result
78
77
 
79
78
 
80
- def inspect_main_container() -> Union[str, dict]:
79
+ def inspect_main_container() -> str | dict:
81
80
  try:
82
81
  return DOCKER_CLIENT.inspect_container(get_main_container_name())
83
82
  except Exception as e:
84
83
  return f"inspect failed: {e}"
85
84
 
86
85
 
87
- def get_localstack_version() -> dict[str, Optional[str]]:
86
+ def get_localstack_version() -> dict[str, str | None]:
88
87
  return {
89
88
  "build-date": os.environ.get("LOCALSTACK_BUILD_DATE"),
90
89
  "build-git-hash": os.environ.get("LOCALSTACK_BUILD_GIT_HASH"),
@@ -2,7 +2,6 @@ import functools
2
2
  import logging
3
3
  import platform
4
4
  import random
5
- from typing import Optional, Union
6
5
 
7
6
  from localstack import config
8
7
  from localstack.constants import DEFAULT_VOLUME_DIR, DOCKER_IMAGE_NAME
@@ -80,7 +79,7 @@ def inspect_current_container_mounts() -> list[VolumeInfo]:
80
79
 
81
80
 
82
81
  @functools.lru_cache
83
- def get_default_volume_dir_mount() -> Optional[VolumeInfo]:
82
+ def get_default_volume_dir_mount() -> VolumeInfo | None:
84
83
  """
85
84
  Returns the volume information of LocalStack's DEFAULT_VOLUME_DIR (/var/lib/localstack), if mounted,
86
85
  else it returns None. If we're not currently in docker a VauleError is raised. in a container, a ValueError is
@@ -133,8 +132,8 @@ def get_host_path_for_path_in_docker(path):
133
132
 
134
133
 
135
134
  def container_ports_can_be_bound(
136
- ports: Union[IntOrPort, list[IntOrPort]],
137
- address: Optional[str] = None,
135
+ ports: IntOrPort | list[IntOrPort],
136
+ address: str | None = None,
138
137
  ) -> bool:
139
138
  """Determine whether a given list of ports can be bound by Docker containers
140
139
 
@@ -3,7 +3,8 @@
3
3
  import functools
4
4
  import inspect
5
5
  import logging
6
- from typing import Any, Callable, Optional
6
+ from collections.abc import Callable
7
+ from typing import Any
7
8
 
8
9
  LOG = logging.getLogger(__name__)
9
10
 
@@ -20,7 +21,7 @@ def run_safe(_python_lambda, *args, _default=None, **kwargs):
20
21
 
21
22
  def call_safe(
22
23
  func: Callable, args: tuple = None, kwargs: dict = None, exception_message: str = None
23
- ) -> Optional[Any]:
24
+ ) -> Any | None:
24
25
  """
25
26
  Call the given function with the given arguments, and if it fails, log the given exception_message.
26
27
  If logging.DEBUG is set for the logger, then we also log the traceback.
localstack/utils/http.py CHANGED
@@ -2,7 +2,6 @@ import logging
2
2
  import math
3
3
  import os
4
4
  import re
5
- from typing import Optional, Union
6
5
  from urllib.parse import parse_qs, parse_qsl, urlencode, urlparse, urlunparse
7
6
 
8
7
  import requests
@@ -54,7 +53,7 @@ def create_chunked_data(data, chunk_size: int = 80):
54
53
  return ret
55
54
 
56
55
 
57
- def canonicalize_headers(headers: Union[dict, CaseInsensitiveDict]) -> dict:
56
+ def canonicalize_headers(headers: dict | CaseInsensitiveDict) -> dict:
58
57
  if not headers:
59
58
  return headers
60
59
 
@@ -103,7 +102,7 @@ def add_query_params_to_url(uri: str, query_params: dict) -> str:
103
102
 
104
103
 
105
104
  def make_http_request(
106
- url: str, data: Union[bytes, str] = None, headers: dict[str, str] = None, method: str = "GET"
105
+ url: str, data: bytes | str = None, headers: dict[str, str] = None, method: str = "GET"
107
106
  ) -> Response:
108
107
  return requests.request(
109
108
  url=url, method=method, headers=headers, data=data, auth=NetrcBypassAuth(), verify=False
@@ -179,7 +178,7 @@ def download(
179
178
  path: str,
180
179
  verify_ssl: bool = True,
181
180
  timeout: float = None,
182
- request_headers: Optional[dict] = None,
181
+ request_headers: dict | None = None,
183
182
  quiet: bool = False,
184
183
  ) -> None:
185
184
  """Downloads file at url to the given path. Raises TimeoutError if the optional timeout (in secs) is reached.
@@ -291,7 +290,7 @@ def download_github_artifact(url: str, target_file: str, timeout: int = None):
291
290
  Optionally allows to define a timeout in seconds."""
292
291
 
293
292
  def do_download(
294
- download_url: str, request_headers: Optional[dict] = None, print_error: bool = False
293
+ download_url: str, request_headers: dict | None = None, print_error: bool = False
295
294
  ):
296
295
  try:
297
296
  download(download_url, target_file, timeout=timeout, request_headers=request_headers)
localstack/utils/json.py CHANGED
@@ -5,7 +5,7 @@ import logging
5
5
  import os
6
6
  from datetime import date, datetime
7
7
  from json import JSONDecodeError
8
- from typing import Any, Union
8
+ from typing import Any
9
9
 
10
10
  from localstack.config import HostAndPort
11
11
 
@@ -63,9 +63,9 @@ class FileMappedDocument(dict):
63
63
  concurrent writes, run load(). To save and overwrite the current document on disk, run save().
64
64
  """
65
65
 
66
- path: Union[str, os.PathLike]
66
+ path: str | os.PathLike
67
67
 
68
- def __init__(self, path: Union[str, os.PathLike], mode=0o664):
68
+ def __init__(self, path: str | os.PathLike, mode=0o664):
69
69
  super().__init__()
70
70
  self.path = path
71
71
  self.mode = mode
@@ -6,7 +6,8 @@ import re
6
6
  import socket
7
7
  import tempfile
8
8
  import threading
9
- from typing import Any, Callable
9
+ from collections.abc import Callable
10
+ from typing import Any
10
11
 
11
12
  from amazon_kclpy import kcl
12
13
  from amazon_kclpy.v2 import processor
localstack/utils/net.py CHANGED
@@ -5,7 +5,7 @@ import socket
5
5
  import threading
6
6
  from collections.abc import MutableMapping
7
7
  from contextlib import closing
8
- from typing import Any, NamedTuple, Optional, Union
8
+ from typing import Any, NamedTuple
9
9
  from urllib.parse import urlparse
10
10
 
11
11
  import dns.resolver
@@ -50,14 +50,14 @@ class Port(NamedTuple):
50
50
 
51
51
 
52
52
  # simple helper type to encapsulate int/Port argument types
53
- IntOrPort = Union[int, Port]
53
+ IntOrPort = int | Port
54
54
 
55
55
 
56
56
  def is_port_open(
57
- port_or_url: Union[int, str],
57
+ port_or_url: int | str,
58
58
  http_path: str = None,
59
59
  expect_success: bool = True,
60
- protocols: Optional[Union[str, list[str]]] = None,
60
+ protocols: str | list[str] | None = None,
61
61
  quiet: bool = True,
62
62
  ):
63
63
  from localstack.utils.http import safe_requests
@@ -275,7 +275,7 @@ def get_free_tcp_port_range(num_ports: int, max_attempts: int = 50) -> "PortRang
275
275
  raise PortNotAvailableException("reached max_attempts when trying to find port range")
276
276
 
277
277
 
278
- def resolve_hostname(hostname: str) -> Optional[str]:
278
+ def resolve_hostname(hostname: str) -> str | None:
279
279
  """Resolve the given hostname and return its IP address, or None if it cannot be resolved."""
280
280
  try:
281
281
  return socket.gethostbyname(hostname)
@@ -362,7 +362,7 @@ class PortRange:
362
362
  """
363
363
  return range(self.start, self.end + 1)
364
364
 
365
- def reserve_port(self, port: Optional[IntOrPort] = None, duration: Optional[int] = None) -> int:
365
+ def reserve_port(self, port: IntOrPort | None = None, duration: int | None = None) -> int:
366
366
  """
367
367
  Reserves the given port (if it is still free). If the given port is None, it reserves a free port from the
368
368
  configured port range for external services. If a port is given, it has to be within the configured
@@ -1,6 +1,6 @@
1
1
  import argparse
2
2
  import logging
3
- from typing import NoReturn, Optional
3
+ from typing import NoReturn
4
4
 
5
5
  LOG = logging.getLogger(__name__)
6
6
 
@@ -12,7 +12,7 @@ class NoExitArgumentParser(argparse.ArgumentParser):
12
12
  * ArgumentParser subclassing example: https://stackoverflow.com/a/59072378/6875981
13
13
  """
14
14
 
15
- def exit(self, status: int = ..., message: Optional[str] = ...) -> NoReturn:
15
+ def exit(self, status: int = ..., message: str | None = ...) -> NoReturn:
16
16
  LOG.warning("Error in argument parser but preventing exit: %s", message)
17
17
 
18
18
  def error(self, message: str) -> NoReturn: