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.
Files changed (253) hide show
  1. localstack/aws/api/cloudformation/__init__.py +18 -4
  2. localstack/aws/api/cloudwatch/__init__.py +41 -1
  3. localstack/aws/api/config/__init__.py +4 -0
  4. localstack/aws/api/core.py +6 -2
  5. localstack/aws/api/dynamodb/__init__.py +30 -0
  6. localstack/aws/api/ec2/__init__.py +1522 -65
  7. localstack/aws/api/iam/__init__.py +7 -0
  8. localstack/aws/api/kinesis/__init__.py +19 -0
  9. localstack/aws/api/kms/__init__.py +6 -0
  10. localstack/aws/api/lambda_/__init__.py +13 -0
  11. localstack/aws/api/logs/__init__.py +15 -0
  12. localstack/aws/api/redshift/__init__.py +9 -3
  13. localstack/aws/api/route53/__init__.py +5 -0
  14. localstack/aws/api/s3/__init__.py +12 -0
  15. localstack/aws/api/s3control/__init__.py +54 -0
  16. localstack/aws/api/ssm/__init__.py +2 -0
  17. localstack/aws/api/transcribe/__init__.py +17 -0
  18. localstack/aws/client.py +7 -2
  19. localstack/aws/forwarder.py +52 -5
  20. localstack/aws/handlers/analytics.py +1 -1
  21. localstack/aws/handlers/internal_requests.py +6 -1
  22. localstack/aws/handlers/logging.py +12 -2
  23. localstack/aws/handlers/metric_handler.py +41 -1
  24. localstack/aws/handlers/service.py +40 -20
  25. localstack/aws/mocking.py +2 -2
  26. localstack/aws/patches.py +2 -2
  27. localstack/aws/protocol/parser.py +459 -32
  28. localstack/aws/protocol/serializer.py +689 -69
  29. localstack/aws/protocol/service_router.py +120 -20
  30. localstack/aws/protocol/validate.py +1 -1
  31. localstack/aws/scaffold.py +1 -1
  32. localstack/aws/skeleton.py +4 -2
  33. localstack/aws/spec-patches.json +58 -0
  34. localstack/aws/spec.py +37 -16
  35. localstack/cli/exceptions.py +1 -1
  36. localstack/cli/localstack.py +6 -6
  37. localstack/cli/lpm.py +3 -4
  38. localstack/cli/plugins.py +1 -1
  39. localstack/cli/profiles.py +1 -2
  40. localstack/config.py +25 -18
  41. localstack/constants.py +4 -29
  42. localstack/dev/kubernetes/__main__.py +130 -7
  43. localstack/dev/run/configurators.py +1 -4
  44. localstack/dev/run/paths.py +1 -1
  45. localstack/dns/plugins.py +5 -1
  46. localstack/dns/server.py +13 -4
  47. localstack/logging/format.py +3 -3
  48. localstack/packages/api.py +9 -8
  49. localstack/packages/core.py +2 -2
  50. localstack/packages/plugins.py +0 -8
  51. localstack/runtime/analytics.py +3 -0
  52. localstack/runtime/hooks.py +1 -1
  53. localstack/runtime/init.py +2 -2
  54. localstack/runtime/main.py +5 -5
  55. localstack/runtime/patches.py +2 -2
  56. localstack/services/apigateway/helpers.py +1 -4
  57. localstack/services/apigateway/legacy/helpers.py +7 -8
  58. localstack/services/apigateway/legacy/integration.py +4 -3
  59. localstack/services/apigateway/legacy/invocations.py +6 -5
  60. localstack/services/apigateway/legacy/provider.py +148 -68
  61. localstack/services/apigateway/legacy/templates.py +1 -1
  62. localstack/services/apigateway/next_gen/execute_api/handlers/method_request.py +7 -2
  63. localstack/services/apigateway/next_gen/execute_api/handlers/resource_router.py +1 -2
  64. localstack/services/apigateway/next_gen/execute_api/integrations/aws.py +3 -0
  65. localstack/services/apigateway/next_gen/execute_api/integrations/http.py +3 -3
  66. localstack/services/apigateway/next_gen/execute_api/template_mapping.py +2 -2
  67. localstack/services/apigateway/next_gen/execute_api/test_invoke.py +114 -9
  68. localstack/services/apigateway/next_gen/provider.py +5 -0
  69. localstack/services/apigateway/resource_providers/aws_apigateway_resource.py +1 -1
  70. localstack/services/cloudformation/api_utils.py +4 -8
  71. localstack/services/cloudformation/cfn_utils.py +1 -1
  72. localstack/services/cloudformation/engine/entities.py +14 -4
  73. localstack/services/cloudformation/engine/template_deployer.py +6 -4
  74. localstack/services/cloudformation/engine/transformers.py +6 -4
  75. localstack/services/cloudformation/engine/v2/change_set_model.py +201 -13
  76. localstack/services/cloudformation/engine/v2/change_set_model_describer.py +52 -3
  77. localstack/services/cloudformation/engine/v2/change_set_model_executor.py +117 -76
  78. localstack/services/cloudformation/engine/v2/change_set_model_preproc.py +205 -52
  79. localstack/services/cloudformation/engine/v2/change_set_model_transform.py +350 -116
  80. localstack/services/cloudformation/engine/v2/change_set_model_validator.py +56 -14
  81. localstack/services/cloudformation/engine/v2/change_set_model_visitor.py +1 -0
  82. localstack/services/cloudformation/engine/v2/resolving.py +7 -5
  83. localstack/services/cloudformation/engine/yaml_parser.py +9 -2
  84. localstack/services/cloudformation/provider.py +7 -5
  85. localstack/services/cloudformation/resource_provider.py +7 -1
  86. localstack/services/cloudformation/resources.py +24149 -0
  87. localstack/services/cloudformation/service_models.py +2 -2
  88. localstack/services/cloudformation/v2/entities.py +19 -9
  89. localstack/services/cloudformation/v2/provider.py +336 -106
  90. localstack/services/cloudformation/v2/types.py +13 -7
  91. localstack/services/cloudformation/v2/utils.py +4 -1
  92. localstack/services/cloudwatch/alarm_scheduler.py +4 -1
  93. localstack/services/cloudwatch/provider.py +18 -13
  94. localstack/services/cloudwatch/provider_v2.py +25 -28
  95. localstack/services/dynamodb/packages.py +2 -1
  96. localstack/services/dynamodb/provider.py +42 -0
  97. localstack/services/dynamodb/server.py +2 -2
  98. localstack/services/dynamodb/v2/provider.py +42 -0
  99. localstack/services/ecr/resource_providers/aws_ecr_repository.py +5 -2
  100. localstack/services/edge.py +1 -1
  101. localstack/services/es/provider.py +2 -2
  102. localstack/services/events/event_rule_engine.py +31 -13
  103. localstack/services/events/models.py +4 -5
  104. localstack/services/events/provider.py +17 -14
  105. localstack/services/events/target.py +17 -9
  106. localstack/services/events/v1/provider.py +5 -5
  107. localstack/services/firehose/provider.py +14 -4
  108. localstack/services/iam/provider.py +11 -116
  109. localstack/services/iam/resources/policy_simulator.py +133 -0
  110. localstack/services/kinesis/models.py +15 -2
  111. localstack/services/kinesis/provider.py +86 -3
  112. localstack/services/kms/provider.py +14 -5
  113. localstack/services/lambda_/api_utils.py +6 -3
  114. localstack/services/lambda_/invocation/docker_runtime_executor.py +1 -1
  115. localstack/services/lambda_/invocation/event_manager.py +1 -1
  116. localstack/services/lambda_/invocation/internal_sqs_queue.py +5 -9
  117. localstack/services/lambda_/invocation/lambda_models.py +10 -7
  118. localstack/services/lambda_/invocation/lambda_service.py +5 -1
  119. localstack/services/lambda_/packages.py +1 -1
  120. localstack/services/lambda_/provider.py +4 -3
  121. localstack/services/lambda_/provider_utils.py +1 -1
  122. localstack/services/logs/provider.py +36 -19
  123. localstack/services/moto.py +2 -1
  124. localstack/services/opensearch/cluster.py +15 -7
  125. localstack/services/opensearch/packages.py +26 -7
  126. localstack/services/opensearch/provider.py +8 -2
  127. localstack/services/opensearch/versions.py +56 -7
  128. localstack/services/plugins.py +11 -7
  129. localstack/services/providers.py +10 -2
  130. localstack/services/redshift/provider.py +0 -21
  131. localstack/services/s3/constants.py +5 -2
  132. localstack/services/s3/cors.py +4 -4
  133. localstack/services/s3/models.py +1 -1
  134. localstack/services/s3/notifications.py +55 -39
  135. localstack/services/s3/presigned_url.py +35 -54
  136. localstack/services/s3/provider.py +73 -15
  137. localstack/services/s3/utils.py +42 -22
  138. localstack/services/s3/validation.py +46 -32
  139. localstack/services/s3/website_hosting.py +4 -2
  140. localstack/services/ses/provider.py +18 -8
  141. localstack/services/sns/constants.py +7 -1
  142. localstack/services/sns/executor.py +9 -2
  143. localstack/services/sns/provider.py +8 -5
  144. localstack/services/sns/publisher.py +31 -16
  145. localstack/services/sns/v2/models.py +167 -0
  146. localstack/services/sns/v2/provider.py +867 -0
  147. localstack/services/sns/v2/utils.py +130 -0
  148. localstack/services/sqs/constants.py +1 -1
  149. localstack/services/sqs/developer_api.py +205 -0
  150. localstack/services/sqs/models.py +48 -5
  151. localstack/services/sqs/provider.py +38 -311
  152. localstack/services/sqs/query_api.py +6 -2
  153. localstack/services/sqs/utils.py +121 -2
  154. localstack/services/ssm/provider.py +1 -1
  155. localstack/services/stepfunctions/asl/component/intrinsic/member.py +1 -1
  156. localstack/services/stepfunctions/asl/component/state/state_choice/comparison/comparison.py +5 -11
  157. localstack/services/stepfunctions/asl/component/state/state_choice/state_choice.py +2 -2
  158. localstack/services/stepfunctions/asl/component/state/state_execution/state_map/state_map.py +2 -2
  159. localstack/services/stepfunctions/asl/component/state/state_execution/state_parallel/state_parallel.py +1 -1
  160. localstack/services/stepfunctions/asl/component/state/state_execution/state_task/state_task.py +2 -2
  161. localstack/services/stepfunctions/asl/component/state/state_fail/state_fail.py +1 -1
  162. localstack/services/stepfunctions/asl/component/state/state_pass/state_pass.py +2 -2
  163. localstack/services/stepfunctions/asl/component/state/state_succeed/state_succeed.py +1 -1
  164. localstack/services/stepfunctions/asl/component/state/state_wait/state_wait.py +1 -1
  165. localstack/services/stepfunctions/asl/eval/environment.py +1 -1
  166. localstack/services/stepfunctions/asl/jsonata/jsonata.py +1 -1
  167. localstack/services/stepfunctions/backend/execution.py +2 -1
  168. localstack/services/stores.py +1 -1
  169. localstack/services/transcribe/provider.py +6 -1
  170. localstack/state/codecs.py +61 -0
  171. localstack/state/core.py +11 -5
  172. localstack/state/pickle.py +10 -49
  173. localstack/testing/aws/cloudformation_utils.py +1 -1
  174. localstack/testing/pytest/cloudformation/fixtures.py +3 -3
  175. localstack/testing/pytest/cloudformation/transformers.py +0 -0
  176. localstack/testing/pytest/container.py +4 -5
  177. localstack/testing/pytest/fixtures.py +33 -31
  178. localstack/testing/pytest/in_memory_localstack.py +0 -4
  179. localstack/testing/pytest/marking.py +38 -11
  180. localstack/testing/pytest/stepfunctions/utils.py +4 -3
  181. localstack/testing/pytest/util.py +1 -1
  182. localstack/testing/pytest/validation_tracking.py +1 -2
  183. localstack/testing/snapshots/transformer_utility.py +6 -1
  184. localstack/utils/analytics/events.py +2 -2
  185. localstack/utils/analytics/metadata.py +6 -4
  186. localstack/utils/analytics/metrics/counter.py +8 -15
  187. localstack/utils/analytics/publisher.py +1 -2
  188. localstack/utils/analytics/service_providers.py +19 -0
  189. localstack/utils/analytics/service_request_aggregator.py +2 -2
  190. localstack/utils/archives.py +11 -11
  191. localstack/utils/asyncio.py +2 -2
  192. localstack/utils/aws/arns.py +24 -29
  193. localstack/utils/aws/aws_responses.py +8 -8
  194. localstack/utils/aws/aws_stack.py +2 -3
  195. localstack/utils/aws/dead_letter_queue.py +1 -5
  196. localstack/utils/aws/message_forwarding.py +1 -2
  197. localstack/utils/aws/request_context.py +4 -5
  198. localstack/utils/aws/resources.py +1 -1
  199. localstack/utils/aws/templating.py +1 -1
  200. localstack/utils/batch_policy.py +3 -3
  201. localstack/utils/bootstrap.py +21 -13
  202. localstack/utils/catalog/catalog.py +139 -0
  203. localstack/utils/catalog/catalog_loader.py +119 -0
  204. localstack/utils/catalog/common.py +58 -0
  205. localstack/utils/catalog/plugins.py +28 -0
  206. localstack/utils/cloudwatch/cloudwatch_util.py +5 -5
  207. localstack/utils/collections.py +7 -8
  208. localstack/utils/config_listener.py +1 -1
  209. localstack/utils/container_networking.py +2 -3
  210. localstack/utils/container_utils/container_client.py +135 -136
  211. localstack/utils/container_utils/docker_cmd_client.py +85 -69
  212. localstack/utils/container_utils/docker_sdk_client.py +69 -66
  213. localstack/utils/crypto.py +10 -10
  214. localstack/utils/diagnose.py +3 -4
  215. localstack/utils/docker_utils.py +9 -5
  216. localstack/utils/files.py +33 -13
  217. localstack/utils/functions.py +4 -3
  218. localstack/utils/http.py +11 -11
  219. localstack/utils/json.py +20 -6
  220. localstack/utils/kinesis/kinesis_connector.py +2 -1
  221. localstack/utils/net.py +15 -9
  222. localstack/utils/no_exit_argument_parser.py +2 -2
  223. localstack/utils/numbers.py +9 -2
  224. localstack/utils/objects.py +7 -6
  225. localstack/utils/patch.py +10 -3
  226. localstack/utils/run.py +12 -11
  227. localstack/utils/scheduler.py +11 -11
  228. localstack/utils/server/tcp_proxy.py +2 -2
  229. localstack/utils/serving.py +3 -4
  230. localstack/utils/strings.py +15 -16
  231. localstack/utils/sync.py +126 -1
  232. localstack/utils/tagging.py +8 -6
  233. localstack/utils/testutil.py +8 -8
  234. localstack/utils/threads.py +2 -2
  235. localstack/utils/time.py +12 -4
  236. localstack/utils/urls.py +1 -3
  237. localstack/utils/xray/traceid.py +1 -1
  238. localstack/version.py +16 -3
  239. {localstack_core-4.7.1.dev49.dist-info → localstack_core-4.10.1.dev12.dist-info}/METADATA +18 -14
  240. {localstack_core-4.7.1.dev49.dist-info → localstack_core-4.10.1.dev12.dist-info}/RECORD +248 -239
  241. {localstack_core-4.7.1.dev49.dist-info → localstack_core-4.10.1.dev12.dist-info}/entry_points.txt +8 -4
  242. localstack_core-4.10.1.dev12.dist-info/plux.json +1 -0
  243. localstack/packages/terraform.py +0 -46
  244. localstack/services/cloudformation/deploy.html +0 -144
  245. localstack/services/cloudformation/deploy_ui.py +0 -47
  246. localstack/services/cloudformation/plugins.py +0 -12
  247. localstack_core-4.7.1.dev49.dist-info/plux.json +0 -1
  248. {localstack_core-4.7.1.dev49.data → localstack_core-4.10.1.dev12.data}/scripts/localstack +0 -0
  249. {localstack_core-4.7.1.dev49.data → localstack_core-4.10.1.dev12.data}/scripts/localstack-supervisor +0 -0
  250. {localstack_core-4.7.1.dev49.data → localstack_core-4.10.1.dev12.data}/scripts/localstack.bat +0 -0
  251. {localstack_core-4.7.1.dev49.dist-info → localstack_core-4.10.1.dev12.dist-info}/WHEEL +0 -0
  252. {localstack_core-4.7.1.dev49.dist-info → localstack_core-4.10.1.dev12.dist-info}/licenses/LICENSE.txt +0 -0
  253. {localstack_core-4.7.1.dev49.dist-info → localstack_core-4.10.1.dev12.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
  """
@@ -202,7 +202,7 @@ class CmdDockerClient(ContainerClient):
202
202
  except subprocess.CalledProcessError as e:
203
203
  self._check_and_raise_no_such_container_error(container_name, error=e)
204
204
  raise ContainerException(
205
- "Docker process returned with errorcode %s" % e.returncode, e.stdout, e.stderr
205
+ f"Docker process returned with errorcode {e.returncode}", e.stdout, e.stderr
206
206
  ) from e
207
207
 
208
208
  def pause_container(self, container_name: str) -> None:
@@ -214,7 +214,7 @@ class CmdDockerClient(ContainerClient):
214
214
  except subprocess.CalledProcessError as e:
215
215
  self._check_and_raise_no_such_container_error(container_name, error=e)
216
216
  raise ContainerException(
217
- "Docker process returned with errorcode %s" % e.returncode, e.stdout, e.stderr
217
+ f"Docker process returned with errorcode {e.returncode}", e.stdout, e.stderr
218
218
  ) from e
219
219
 
220
220
  def unpause_container(self, container_name: str) -> None:
@@ -226,7 +226,7 @@ class CmdDockerClient(ContainerClient):
226
226
  except subprocess.CalledProcessError as e:
227
227
  self._check_and_raise_no_such_container_error(container_name, error=e)
228
228
  raise ContainerException(
229
- "Docker process returned with errorcode %s" % e.returncode, e.stdout, e.stderr
229
+ f"Docker process returned with errorcode {e.returncode}", e.stdout, e.stderr
230
230
  ) from e
231
231
 
232
232
  def remove_image(self, image: str, force: bool = True) -> None:
@@ -243,7 +243,7 @@ class CmdDockerClient(ContainerClient):
243
243
  if any(msg in to_str(e.stdout) for msg in error_messages):
244
244
  raise NoSuchImage(image, stdout=e.stdout, stderr=e.stderr)
245
245
  raise ContainerException(
246
- "Docker process returned with errorcode %s" % e.returncode, e.stdout, e.stderr
246
+ f"Docker process returned with errorcode {e.returncode}", e.stdout, e.stderr
247
247
  ) from e
248
248
 
249
249
  def commit(
@@ -262,26 +262,34 @@ class CmdDockerClient(ContainerClient):
262
262
  except subprocess.CalledProcessError as e:
263
263
  self._check_and_raise_no_such_container_error(container_name_or_id, error=e)
264
264
  raise ContainerException(
265
- "Docker process returned with errorcode %s" % e.returncode, e.stdout, e.stderr
265
+ f"Docker process returned with errorcode {e.returncode}", e.stdout, e.stderr
266
266
  ) from e
267
267
 
268
- def remove_container(self, container_name: str, force=True, check_existence=False) -> None:
269
- if check_existence and container_name not in self.get_running_container_names():
268
+ def remove_container(
269
+ self, container_name: str, force=True, check_existence=False, volumes=False
270
+ ) -> None:
271
+ if check_existence and container_name not in self.get_all_container_names():
270
272
  return
271
273
  cmd = self._docker_cmd() + ["rm"]
272
274
  if force:
273
275
  cmd.append("-f")
276
+ if volumes:
277
+ cmd.append("--volumes")
274
278
  cmd.append(container_name)
275
279
  LOG.debug("Removing container with cmd %s", cmd)
276
280
  try:
277
- run(cmd)
281
+ output = run(cmd)
282
+ # When the container does not exist, the output could have the error message without any exception
283
+ if isinstance(output, str) and not force:
284
+ self._check_output_and_raise_no_such_container_error(container_name, output=output)
278
285
  except subprocess.CalledProcessError as e:
279
- self._check_and_raise_no_such_container_error(container_name, error=e)
286
+ if not force:
287
+ self._check_and_raise_no_such_container_error(container_name, error=e)
280
288
  raise ContainerException(
281
- "Docker process returned with errorcode %s" % e.returncode, e.stdout, e.stderr
289
+ f"Docker process returned with errorcode {e.returncode}", e.stdout, e.stderr
282
290
  ) from e
283
291
 
284
- 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]:
285
293
  filter = [filter] if isinstance(filter, str) else filter
286
294
  cmd = self._docker_cmd()
287
295
  cmd.append("ps")
@@ -297,7 +305,7 @@ class CmdDockerClient(ContainerClient):
297
305
  cmd_result = run(cmd).strip()
298
306
  except subprocess.CalledProcessError as e:
299
307
  raise ContainerException(
300
- "Docker process returned with errorcode %s" % e.returncode, e.stdout, e.stderr
308
+ f"Docker process returned with errorcode {e.returncode}", e.stdout, e.stderr
301
309
  ) from e
302
310
  container_list = []
303
311
  if cmd_result:
@@ -351,14 +359,14 @@ class CmdDockerClient(ContainerClient):
351
359
  if re.match(".*container .+ does not exist", to_str(e.stdout)):
352
360
  raise NoSuchContainer(container_name, stdout=e.stdout, stderr=e.stderr)
353
361
  raise ContainerException(
354
- "Docker process returned with errorcode %s" % e.returncode, e.stdout, e.stderr
362
+ f"Docker process returned with errorcode {e.returncode}", e.stdout, e.stderr
355
363
  ) from e
356
364
 
357
365
  def pull_image(
358
366
  self,
359
367
  docker_image: str,
360
- platform: Optional[DockerPlatform] = None,
361
- log_handler: Optional[Callable[[str], None]] = None,
368
+ platform: DockerPlatform | None = None,
369
+ log_handler: Callable[[str], None] | None = None,
362
370
  ) -> None:
363
371
  cmd = self._docker_cmd()
364
372
  docker_image = self.registry_resolver_strategy.resolve(docker_image)
@@ -380,7 +388,7 @@ class CmdDockerClient(ContainerClient):
380
388
  if "Trying to pull" in stdout_str and "access to the resource is denied" in stdout_str:
381
389
  raise NoSuchImage(docker_image, stdout=e.stdout, stderr=e.stderr)
382
390
  raise ContainerException(
383
- "Docker process returned with errorcode %s" % e.returncode, e.stdout, e.stderr
391
+ f"Docker process returned with errorcode {e.returncode}", e.stdout, e.stderr
384
392
  ) from e
385
393
 
386
394
  def push_image(self, docker_image: str) -> None:
@@ -412,7 +420,7 @@ class CmdDockerClient(ContainerClient):
412
420
  dockerfile_path: str,
413
421
  image_name: str,
414
422
  context_path: str = None,
415
- platform: Optional[DockerPlatform] = None,
423
+ platform: DockerPlatform | None = None,
416
424
  ):
417
425
  cmd = self._docker_cmd()
418
426
  dockerfile_path = Util.resolve_dockerfile_path(dockerfile_path)
@@ -474,7 +482,7 @@ class CmdDockerClient(ContainerClient):
474
482
  return ""
475
483
  self._check_and_raise_no_such_container_error(container_name_or_id, error=e)
476
484
  raise ContainerException(
477
- "Docker process returned with errorcode %s" % e.returncode, e.stdout, e.stderr
485
+ f"Docker process returned with errorcode {e.returncode}", e.stdout, e.stderr
478
486
  ) from e
479
487
 
480
488
  def stream_container_logs(self, container_name_or_id: str) -> CancellableStream:
@@ -489,7 +497,7 @@ class CmdDockerClient(ContainerClient):
489
497
 
490
498
  return CancellableProcessStream(process)
491
499
 
492
- 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]:
493
501
  cmd = self._docker_cmd()
494
502
  cmd += ["inspect", "--format", "{{json .}}", object_name_or_id]
495
503
  try:
@@ -499,7 +507,7 @@ class CmdDockerClient(ContainerClient):
499
507
  if "no such object" in to_str(e.stdout).lower():
500
508
  raise NoSuchObject(object_name_or_id, stdout=e.stdout, stderr=e.stderr)
501
509
  raise ContainerException(
502
- "Docker process returned with errorcode %s" % e.returncode, e.stdout, e.stderr
510
+ f"Docker process returned with errorcode {e.returncode}", e.stdout, e.stderr
503
511
  ) from e
504
512
  object_data = json.loads(cmd_result.strip())
505
513
  if isinstance(object_data, list):
@@ -516,7 +524,7 @@ class CmdDockerClient(ContainerClient):
516
524
  )
517
525
  return object_data
518
526
 
519
- 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]:
520
528
  try:
521
529
  return self._inspect_object(container_name_or_id)
522
530
  except NoSuchObject as e:
@@ -527,7 +535,7 @@ class CmdDockerClient(ContainerClient):
527
535
  image_name: str,
528
536
  pull: bool = True,
529
537
  strip_wellknown_repo_prefixes: bool = True,
530
- ) -> dict[str, Union[dict, list, str]]:
538
+ ) -> dict[str, dict | list | str]:
531
539
  image_name = self.registry_resolver_strategy.resolve(image_name)
532
540
  try:
533
541
  result = self._inspect_object(image_name)
@@ -552,7 +560,7 @@ class CmdDockerClient(ContainerClient):
552
560
  return run(cmd).strip()
553
561
  except subprocess.CalledProcessError as e:
554
562
  raise ContainerException(
555
- "Docker process returned with errorcode %s" % e.returncode, e.stdout, e.stderr
563
+ f"Docker process returned with errorcode {e.returncode}", e.stdout, e.stderr
556
564
  ) from e
557
565
 
558
566
  def delete_network(self, network_name: str) -> None:
@@ -566,10 +574,10 @@ class CmdDockerClient(ContainerClient):
566
574
  raise NoSuchNetwork(network_name=network_name)
567
575
  else:
568
576
  raise ContainerException(
569
- "Docker process returned with errorcode %s" % e.returncode, e.stdout, e.stderr
577
+ f"Docker process returned with errorcode {e.returncode}", e.stdout, e.stderr
570
578
  ) from e
571
579
 
572
- def inspect_network(self, network_name: str) -> dict[str, Union[dict, str]]:
580
+ def inspect_network(self, network_name: str) -> dict[str, dict | str]:
573
581
  try:
574
582
  return self._inspect_object(network_name)
575
583
  except NoSuchObject as e:
@@ -579,7 +587,7 @@ class CmdDockerClient(ContainerClient):
579
587
  self,
580
588
  network_name: str,
581
589
  container_name_or_id: str,
582
- aliases: Optional[list] = None,
590
+ aliases: list | None = None,
583
591
  link_local_ips: list[str] = None,
584
592
  ) -> None:
585
593
  LOG.debug(
@@ -603,7 +611,7 @@ class CmdDockerClient(ContainerClient):
603
611
  raise NoSuchNetwork(network_name=network_name)
604
612
  self._check_and_raise_no_such_container_error(container_name_or_id, error=e)
605
613
  raise ContainerException(
606
- "Docker process returned with errorcode %s" % e.returncode, e.stdout, e.stderr
614
+ f"Docker process returned with errorcode {e.returncode}", e.stdout, e.stderr
607
615
  ) from e
608
616
 
609
617
  def disconnect_container_from_network(
@@ -621,7 +629,7 @@ class CmdDockerClient(ContainerClient):
621
629
  raise NoSuchNetwork(network_name=network_name)
622
630
  self._check_and_raise_no_such_container_error(container_name_or_id, error=e)
623
631
  raise ContainerException(
624
- "Docker process returned with errorcode %s" % e.returncode, e.stdout, e.stderr
632
+ f"Docker process returned with errorcode {e.returncode}", e.stdout, e.stderr
625
633
  ) from e
626
634
 
627
635
  def get_container_ip(self, container_name_or_id: str) -> str:
@@ -641,10 +649,10 @@ class CmdDockerClient(ContainerClient):
641
649
  if "no such object" in to_str(e.stdout).lower():
642
650
  raise NoSuchContainer(container_name_or_id, stdout=e.stdout, stderr=e.stderr)
643
651
  raise ContainerException(
644
- "Docker process returned with errorcode %s" % e.returncode, e.stdout, e.stderr
652
+ f"Docker process returned with errorcode {e.returncode}", e.stdout, e.stderr
645
653
  ) from e
646
654
 
647
- 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:
648
656
  cmd = self._docker_cmd()
649
657
  # TODO specify password via stdin
650
658
  cmd += ["login", "-u", username, "-p", password]
@@ -654,7 +662,7 @@ class CmdDockerClient(ContainerClient):
654
662
  run(cmd)
655
663
  except subprocess.CalledProcessError as e:
656
664
  raise ContainerException(
657
- "Docker process returned with errorcode %s" % e.returncode, e.stdout, e.stderr
665
+ f"Docker process returned with errorcode {e.returncode}", e.stdout, e.stderr
658
666
  ) from e
659
667
 
660
668
  @functools.cache
@@ -680,7 +688,7 @@ class CmdDockerClient(ContainerClient):
680
688
  if any(msg in to_str(e.stdout) for msg in error_messages):
681
689
  raise NoSuchImage(image_name, stdout=e.stdout, stderr=e.stderr)
682
690
  raise ContainerException(
683
- "Docker process returned with errorcode %s" % e.returncode, e.stdout, e.stderr
691
+ f"Docker process returned with errorcode {e.returncode}", e.stdout, e.stderr
684
692
  ) from e
685
693
  finally:
686
694
  Util.rm_env_vars_file(env_file)
@@ -701,13 +709,13 @@ class CmdDockerClient(ContainerClient):
701
709
  def exec_in_container(
702
710
  self,
703
711
  container_name_or_id: str,
704
- command: Union[list[str], str],
712
+ command: list[str] | str,
705
713
  interactive=False,
706
714
  detach=False,
707
- env_vars: Optional[dict[str, Optional[str]]] = None,
708
- stdin: Optional[bytes] = None,
709
- user: Optional[str] = None,
710
- 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,
711
719
  ) -> tuple[bytes, bytes]:
712
720
  env_file = None
713
721
  cmd = self._docker_cmd()
@@ -737,7 +745,7 @@ class CmdDockerClient(ContainerClient):
737
745
  stdin=None,
738
746
  interactive: bool = False,
739
747
  attach: bool = False,
740
- flags: Optional[str] = None,
748
+ flags: str | None = None,
741
749
  ) -> tuple[bytes, bytes]:
742
750
  cmd = self._docker_cmd() + ["start"]
743
751
  if flags:
@@ -787,7 +795,7 @@ class CmdDockerClient(ContainerClient):
787
795
  if any(msg.lower() in to_str(e.stderr).lower() for msg in error_messages):
788
796
  raise NoSuchContainer(container_name, stdout=e.stdout, stderr=e.stderr)
789
797
  raise ContainerException(
790
- "Docker process returned with errorcode %s" % e.returncode, e.stdout, e.stderr
798
+ f"Docker process returned with errorcode {e.returncode}", e.stdout, e.stderr
791
799
  ) from e
792
800
 
793
801
  def _build_run_create_cmd(
@@ -795,31 +803,31 @@ class CmdDockerClient(ContainerClient):
795
803
  action: str,
796
804
  image_name: str,
797
805
  *,
798
- name: Optional[str] = None,
799
- entrypoint: Optional[Union[list[str], str]] = None,
806
+ name: str | None = None,
807
+ entrypoint: list[str] | str | None = None,
800
808
  remove: bool = False,
801
809
  interactive: bool = False,
802
810
  tty: bool = False,
803
811
  detach: bool = False,
804
- command: Optional[Union[list[str], str]] = None,
805
- volumes: Optional[list[SimpleVolumeBind]] = None,
806
- ports: Optional[PortMappings] = None,
807
- exposed_ports: Optional[list[str]] = None,
808
- env_vars: Optional[dict[str, str]] = None,
809
- user: Optional[str] = None,
810
- cap_add: Optional[list[str]] = None,
811
- cap_drop: Optional[list[str]] = None,
812
- security_opt: Optional[list[str]] = None,
813
- network: Optional[str] = None,
814
- dns: Optional[Union[str, list[str]]] = None,
815
- additional_flags: Optional[str] = None,
816
- workdir: Optional[str] = None,
817
- privileged: Optional[bool] = None,
818
- labels: Optional[dict[str, str]] = None,
819
- platform: Optional[DockerPlatform] = None,
820
- ulimits: Optional[list[Ulimit]] = None,
821
- init: Optional[bool] = None,
822
- 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,
823
831
  ) -> tuple[list[str], str]:
824
832
  env_file = None
825
833
  cmd = self._docker_cmd() + [action]
@@ -892,7 +900,7 @@ class CmdDockerClient(ContainerClient):
892
900
  return cmd, env_file
893
901
 
894
902
  @staticmethod
895
- def _map_to_volume_param(volume: Union[SimpleVolumeBind, BindMount, VolumeDirMount]) -> str:
903
+ def _map_to_volume_param(volume: SimpleVolumeBind | BindMount | VolumeDirMount) -> str:
896
904
  """
897
905
  Maps the mount volume, to a parameter for the -v docker cli argument.
898
906
 
@@ -915,14 +923,22 @@ class CmdDockerClient(ContainerClient):
915
923
  Check the given client invocation error and raise a `NoSuchContainer` exception if it
916
924
  represents a `no such container` exception from Docker or Podman.
917
925
  """
926
+ self._check_output_and_raise_no_such_container_error(
927
+ container_name_or_id, str(error.stdout), error=str(error.stderr)
928
+ )
918
929
 
919
- # consider different error messages for Docker/Podman
920
- error_messages = ("No such container", "no container with name or ID")
921
- process_stdout_lower = to_str(error.stdout).lower()
922
- if any(msg.lower() in process_stdout_lower for msg in error_messages):
923
- raise NoSuchContainer(container_name_or_id, stdout=error.stdout, stderr=error.stderr)
930
+ def _check_output_and_raise_no_such_container_error(
931
+ self, container_name_or_id: str, output: str, error: str | None = None
932
+ ):
933
+ """
934
+ Check the given client invocation output and raise a `NoSuchContainer` exception if it
935
+ represents a `no such container` exception from Docker or Podman.
936
+ """
937
+ possible_not_found_messages = ("No such container", "no container with name or ID")
938
+ if any(msg.lower() in output.lower() for msg in possible_not_found_messages):
939
+ raise NoSuchContainer(container_name_or_id, stdout=output, stderr=error)
924
940
 
925
- 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]:
926
942
  """
927
943
  Transforms the container labels returned by the docker command from the key-value pair format to a dict
928
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:
@@ -264,21 +265,23 @@ class SdkDockerClient(ContainerClient):
264
265
  except APIError as e:
265
266
  raise ContainerException() from e
266
267
 
267
- def remove_container(self, container_name: str, force=True, check_existence=False) -> None:
268
- LOG.debug("Removing container: %s", container_name)
269
- if check_existence and container_name not in self.get_running_container_names():
268
+ def remove_container(
269
+ self, container_name: str, force=True, check_existence=False, volumes=False
270
+ ) -> None:
271
+ LOG.debug("Removing container: %s, with volumes: %s", container_name, volumes)
272
+ if check_existence and container_name not in self.get_all_container_names():
270
273
  LOG.debug("Aborting removing due to check_existence check")
271
274
  return
272
275
  try:
273
276
  container = self.client().containers.get(container_name)
274
- container.remove(force=force)
277
+ container.remove(force=force, v=volumes)
275
278
  except NotFound:
276
279
  if not force:
277
280
  raise NoSuchContainer(container_name)
278
281
  except APIError as e:
279
282
  raise ContainerException() from e
280
283
 
281
- 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]:
282
285
  if filter:
283
286
  filter = [filter] if isinstance(filter, str) else filter
284
287
  filter = dict([f.split("=", 1) for f in filter])
@@ -337,14 +340,14 @@ class SdkDockerClient(ContainerClient):
337
340
  def pull_image(
338
341
  self,
339
342
  docker_image: str,
340
- platform: Optional[DockerPlatform] = None,
341
- log_handler: Optional[Callable[[str], None]] = None,
343
+ platform: DockerPlatform | None = None,
344
+ log_handler: Callable[[str], None] | None = None,
342
345
  ) -> None:
343
346
  LOG.debug("Pulling Docker image: %s", docker_image)
344
347
  # some path in the docker image string indicates a custom repository
345
348
 
346
349
  docker_image = self.registry_resolver_strategy.resolve(docker_image)
347
- kwargs: dict[str, Union[str, bool]] = {"platform": platform}
350
+ kwargs: dict[str, str | bool] = {"platform": platform}
348
351
  try:
349
352
  if log_handler:
350
353
  # Use a lower-level API, as the 'stream' argument is not available in the higher-level `pull`-API
@@ -389,7 +392,7 @@ class SdkDockerClient(ContainerClient):
389
392
  dockerfile_path: str,
390
393
  image_name: str,
391
394
  context_path: str = None,
392
- platform: Optional[DockerPlatform] = None,
395
+ platform: DockerPlatform | None = None,
393
396
  ):
394
397
  try:
395
398
  dockerfile_path = Util.resolve_dockerfile_path(dockerfile_path)
@@ -466,7 +469,7 @@ class SdkDockerClient(ContainerClient):
466
469
  except APIError as e:
467
470
  raise ContainerException() from e
468
471
 
469
- 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]:
470
473
  try:
471
474
  return self.client().containers.get(container_name_or_id).attrs
472
475
  except NotFound:
@@ -479,7 +482,7 @@ class SdkDockerClient(ContainerClient):
479
482
  image_name: str,
480
483
  pull: bool = True,
481
484
  strip_wellknown_repo_prefixes: bool = True,
482
- ) -> dict[str, Union[dict, list, str]]:
485
+ ) -> dict[str, dict | list | str]:
483
486
  image_name = self.registry_resolver_strategy.resolve(image_name)
484
487
  try:
485
488
  result = self.client().images.get(image_name).attrs
@@ -513,7 +516,7 @@ class SdkDockerClient(ContainerClient):
513
516
  except APIError as e:
514
517
  raise ContainerException() from e
515
518
 
516
- def inspect_network(self, network_name: str) -> dict[str, Union[dict, str]]:
519
+ def inspect_network(self, network_name: str) -> dict[str, dict | str]:
517
520
  try:
518
521
  return self.client().networks.get(network_name).attrs
519
522
  except NotFound:
@@ -525,7 +528,7 @@ class SdkDockerClient(ContainerClient):
525
528
  self,
526
529
  network_name: str,
527
530
  container_name_or_id: str,
528
- aliases: Optional[list] = None,
531
+ aliases: list | None = None,
529
532
  link_local_ips: list[str] = None,
530
533
  ) -> None:
531
534
  LOG.debug(
@@ -619,7 +622,7 @@ class SdkDockerClient(ContainerClient):
619
622
  stdin=None,
620
623
  interactive: bool = False,
621
624
  attach: bool = False,
622
- flags: Optional[str] = None,
625
+ flags: str | None = None,
623
626
  ) -> tuple[bytes, bytes]:
624
627
  LOG.debug("Starting container %s", container_name_or_id)
625
628
  try:
@@ -668,7 +671,7 @@ class SdkDockerClient(ContainerClient):
668
671
  sock.sendall(to_bytes(stdin))
669
672
  sock.shutdown(socket.SHUT_WR)
670
673
  stdout, stderr = self._read_from_sock(sock, False)
671
- except socket.timeout:
674
+ except TimeoutError:
672
675
  LOG.debug(
673
676
  "Socket timeout when talking to the I/O streams of Docker container '%s'",
674
677
  container_name_or_id,
@@ -701,31 +704,31 @@ class SdkDockerClient(ContainerClient):
701
704
  self,
702
705
  image_name: str,
703
706
  *,
704
- name: Optional[str] = None,
705
- entrypoint: Optional[Union[list[str], str]] = None,
707
+ name: str | None = None,
708
+ entrypoint: list[str] | str | None = None,
706
709
  remove: bool = False,
707
710
  interactive: bool = False,
708
711
  tty: bool = False,
709
712
  detach: bool = False,
710
- command: Optional[Union[list[str], str]] = None,
711
- volumes: Optional[list[SimpleVolumeBind]] = None,
712
- ports: Optional[PortMappings] = None,
713
- exposed_ports: Optional[list[str]] = None,
714
- env_vars: Optional[dict[str, str]] = None,
715
- user: Optional[str] = None,
716
- cap_add: Optional[list[str]] = None,
717
- cap_drop: Optional[list[str]] = None,
718
- security_opt: Optional[list[str]] = None,
719
- network: Optional[str] = None,
720
- dns: Optional[Union[str, list[str]]] = None,
721
- additional_flags: Optional[str] = None,
722
- workdir: Optional[str] = None,
723
- privileged: Optional[bool] = None,
724
- labels: Optional[dict[str, str]] = None,
725
- platform: Optional[DockerPlatform] = None,
726
- ulimits: Optional[list[Ulimit]] = None,
727
- init: Optional[bool] = None,
728
- 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,
729
732
  ) -> str:
730
733
  LOG.debug("Creating container with attributes: %s", locals())
731
734
  extra_hosts = None
@@ -832,31 +835,31 @@ class SdkDockerClient(ContainerClient):
832
835
  image_name: str,
833
836
  stdin=None,
834
837
  *,
835
- name: Optional[str] = None,
836
- entrypoint: Optional[str] = None,
838
+ name: str | None = None,
839
+ entrypoint: str | None = None,
837
840
  remove: bool = False,
838
841
  interactive: bool = False,
839
842
  tty: bool = False,
840
843
  detach: bool = False,
841
- command: Optional[Union[list[str], str]] = None,
842
- volumes: Optional[list[SimpleVolumeBind]] = None,
843
- ports: Optional[PortMappings] = None,
844
- exposed_ports: Optional[list[str]] = None,
845
- env_vars: Optional[dict[str, str]] = None,
846
- user: Optional[str] = None,
847
- cap_add: Optional[list[str]] = None,
848
- cap_drop: Optional[list[str]] = None,
849
- security_opt: Optional[list[str]] = None,
850
- network: Optional[str] = None,
851
- dns: Optional[str] = None,
852
- additional_flags: Optional[str] = None,
853
- workdir: Optional[str] = None,
854
- labels: Optional[dict[str, str]] = None,
855
- platform: Optional[DockerPlatform] = None,
856
- privileged: Optional[bool] = None,
857
- ulimits: Optional[list[Ulimit]] = None,
858
- init: Optional[bool] = None,
859
- 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,
860
863
  ) -> tuple[bytes, bytes]:
861
864
  LOG.debug("Running container with image: %s", image_name)
862
865
  container = None
@@ -903,13 +906,13 @@ class SdkDockerClient(ContainerClient):
903
906
  def exec_in_container(
904
907
  self,
905
908
  container_name_or_id: str,
906
- command: Union[list[str], str],
909
+ command: list[str] | str,
907
910
  interactive=False,
908
911
  detach=False,
909
- env_vars: Optional[dict[str, Optional[str]]] = None,
910
- stdin: Optional[bytes] = None,
911
- user: Optional[str] = None,
912
- 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,
913
916
  ) -> tuple[bytes, bytes]:
914
917
  LOG.debug("Executing command in container %s: %s", container_name_or_id, command)
915
918
  try:
@@ -936,7 +939,7 @@ class SdkDockerClient(ContainerClient):
936
939
  sock.shutdown(socket.SHUT_WR)
937
940
  stdout, stderr = self._read_from_sock(sock, tty)
938
941
  return stdout, stderr
939
- except socket.timeout:
942
+ except TimeoutError:
940
943
  pass
941
944
  else:
942
945
  if detach:
@@ -957,7 +960,7 @@ class SdkDockerClient(ContainerClient):
957
960
  except APIError as e:
958
961
  raise ContainerException() from e
959
962
 
960
- 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:
961
964
  LOG.debug("Docker login for %s", username)
962
965
  try:
963
966
  self.client().login(username, password=password, registry=registry, reauth=True)