Skip to content

Commit 62f3453

Browse files
fix remaining mypy issues
1 parent 4e7e787 commit 62f3453

19 files changed

+230
-127
lines changed
Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,15 @@
11
# flake8: noqa: F401
22
from testcontainers.compose.compose import (
33
ComposeContainer,
4-
ContainerIsNotRunning,
54
DockerCompose,
6-
NoSuchPortExposed,
7-
PublishedPort,
5+
PublishedPortModel,
86
)
7+
from testcontainers.core.exceptions import ContainerIsNotRunning, NoSuchPortExposed
8+
9+
__all__ = [
10+
"ComposeContainer",
11+
"ContainerIsNotRunning",
12+
"DockerCompose",
13+
"NoSuchPortExposed",
14+
"PublishedPortModel",
15+
]

core/testcontainers/compose/compose.py

Lines changed: 37 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from re import split
88
from subprocess import CompletedProcess
99
from subprocess import run as subprocess_run
10+
from types import TracebackType
1011
from typing import Any, Callable, Literal, Optional, TypeVar, Union, cast
1112
from urllib.error import HTTPError, URLError
1213
from urllib.request import urlopen
@@ -18,7 +19,7 @@
1819
_WARNINGS = {"DOCKER_COMPOSE_GET_CONFIG": "get_config is experimental, see testcontainers/testcontainers-python#669"}
1920

2021

21-
def _ignore_properties(cls: type[_IPT], dict_: any) -> _IPT:
22+
def _ignore_properties(cls: type[_IPT], dict_: Any) -> _IPT:
2223
"""omits extra fields like @JsonIgnoreProperties(ignoreUnknown = true)
2324
2425
https://gist.github.com/alexanderankin/2a4549ac03554a31bef6eaaf2eaf7fd5"""
@@ -30,23 +31,23 @@ def _ignore_properties(cls: type[_IPT], dict_: any) -> _IPT:
3031

3132

3233
@dataclass
33-
class PublishedPort:
34+
class PublishedPortModel:
3435
"""
3536
Class that represents the response we get from compose when inquiring status
3637
via `DockerCompose.get_running_containers()`.
3738
"""
3839

3940
URL: Optional[str] = None
40-
TargetPort: Optional[str] = None
41-
PublishedPort: Optional[str] = None
41+
TargetPort: Optional[int] = None
42+
PublishedPort: Optional[int] = None
4243
Protocol: Optional[str] = None
4344

44-
def normalize(self):
45+
def normalize(self) -> "PublishedPortModel":
4546
url_not_usable = system() == "Windows" and self.URL == "0.0.0.0"
4647
if url_not_usable:
4748
self_dict = asdict(self)
4849
self_dict.update({"URL": "127.0.0.1"})
49-
return PublishedPort(**self_dict)
50+
return PublishedPortModel(**self_dict)
5051
return self
5152

5253

@@ -75,19 +76,19 @@ class ComposeContainer:
7576
Service: Optional[str] = None
7677
State: Optional[str] = None
7778
Health: Optional[str] = None
78-
ExitCode: Optional[str] = None
79-
Publishers: list[PublishedPort] = field(default_factory=list)
79+
ExitCode: Optional[int] = None
80+
Publishers: list[PublishedPortModel] = field(default_factory=list)
8081

81-
def __post_init__(self):
82+
def __post_init__(self) -> None:
8283
if self.Publishers:
83-
self.Publishers = [_ignore_properties(PublishedPort, p) for p in self.Publishers]
84+
self.Publishers = [_ignore_properties(PublishedPortModel, p) for p in self.Publishers]
8485

8586
def get_publisher(
8687
self,
8788
by_port: Optional[int] = None,
8889
by_host: Optional[str] = None,
89-
prefer_ip_version: Literal["IPV4", "IPv6"] = "IPv4",
90-
) -> PublishedPort:
90+
prefer_ip_version: Literal["IPv4", "IPv6"] = "IPv4",
91+
) -> PublishedPortModel:
9192
remaining_publishers = self.Publishers
9293

9394
remaining_publishers = [r for r in remaining_publishers if self._matches_protocol(prefer_ip_version, r)]
@@ -109,8 +110,9 @@ def get_publisher(
109110
)
110111

111112
@staticmethod
112-
def _matches_protocol(prefer_ip_version, r):
113-
return (":" in r.URL) is (prefer_ip_version == "IPv6")
113+
def _matches_protocol(prefer_ip_version: str, r: PublishedPortModel) -> bool:
114+
r_url = r.URL
115+
return (r_url is not None and ":" in r_url) is (prefer_ip_version == "IPv6")
114116

115117

116118
@dataclass
@@ -164,7 +166,7 @@ class DockerCompose:
164166
image: "hello-world"
165167
"""
166168

167-
context: Union[str, PathLike]
169+
context: Union[str, PathLike[str]]
168170
compose_file_name: Optional[Union[str, list[str]]] = None
169171
pull: bool = False
170172
build: bool = False
@@ -175,15 +177,17 @@ class DockerCompose:
175177
docker_command_path: Optional[str] = None
176178
profiles: Optional[list[str]] = None
177179

178-
def __post_init__(self):
180+
def __post_init__(self) -> None:
179181
if isinstance(self.compose_file_name, str):
180182
self.compose_file_name = [self.compose_file_name]
181183

182184
def __enter__(self) -> "DockerCompose":
183185
self.start()
184186
return self
185187

186-
def __exit__(self, exc_type, exc_val, exc_tb) -> None:
188+
def __exit__(
189+
self, exc_type: Optional[type[BaseException]], exc_val: Optional[BaseException], exc_tb: Optional[TracebackType]
190+
) -> None:
187191
self.stop(not self.keep_volumes)
188192

189193
def docker_compose_command(self) -> list[str]:
@@ -235,7 +239,7 @@ def start(self) -> None:
235239

236240
self._run_command(cmd=up_cmd)
237241

238-
def stop(self, down=True) -> None:
242+
def stop(self, down: bool = True) -> None:
239243
"""
240244
Stops the docker compose environment.
241245
"""
@@ -295,7 +299,7 @@ def get_config(
295299
cmd_output = self._run_command(cmd=config_cmd).stdout
296300
return cast(dict[str, Any], loads(cmd_output)) # noqa: TC006
297301

298-
def get_containers(self, include_all=False) -> list[ComposeContainer]:
302+
def get_containers(self, include_all: bool = False) -> list[ComposeContainer]:
299303
"""
300304
Fetch information about running containers via `docker compose ps --format json`.
301305
Available only in V2 of compose.
@@ -370,17 +374,18 @@ def exec_in_container(
370374
"""
371375
if not service_name:
372376
service_name = self.get_container().Service
373-
exec_cmd = [*self.compose_command_property, "exec", "-T", service_name, *command]
377+
assert service_name
378+
exec_cmd: list[str] = [*self.compose_command_property, "exec", "-T", service_name, *command]
374379
result = self._run_command(cmd=exec_cmd)
375380

376-
return (result.stdout.decode("utf-8"), result.stderr.decode("utf-8"), result.returncode)
381+
return result.stdout.decode("utf-8"), result.stderr.decode("utf-8"), result.returncode
377382

378383
def _run_command(
379384
self,
380385
cmd: Union[str, list[str]],
381386
context: Optional[str] = None,
382387
) -> CompletedProcess[bytes]:
383-
context = context or self.context
388+
context = context or str(self.context)
384389
return subprocess_run(
385390
cmd,
386391
capture_output=True,
@@ -392,7 +397,7 @@ def get_service_port(
392397
self,
393398
service_name: Optional[str] = None,
394399
port: Optional[int] = None,
395-
):
400+
) -> Optional[int]:
396401
"""
397402
Returns the mapped port for one of the services.
398403
@@ -408,13 +413,14 @@ def get_service_port(
408413
str:
409414
The mapped port on the host
410415
"""
411-
return self.get_container(service_name).get_publisher(by_port=port).normalize().PublishedPort
416+
normalize: PublishedPortModel = self.get_container(service_name).get_publisher(by_port=port).normalize()
417+
return normalize.PublishedPort
412418

413419
def get_service_host(
414420
self,
415421
service_name: Optional[str] = None,
416422
port: Optional[int] = None,
417-
):
423+
) -> Optional[str]:
418424
"""
419425
Returns the host for one of the services.
420426
@@ -430,13 +436,17 @@ def get_service_host(
430436
str:
431437
The hostname for the service
432438
"""
433-
return self.get_container(service_name).get_publisher(by_port=port).normalize().URL
439+
container: ComposeContainer = self.get_container(service_name)
440+
publisher: PublishedPortModel = container.get_publisher(by_port=port)
441+
normalize: PublishedPortModel = publisher.normalize()
442+
url: Optional[str] = normalize.URL
443+
return url
434444

435445
def get_service_host_and_port(
436446
self,
437447
service_name: Optional[str] = None,
438448
port: Optional[int] = None,
439-
):
449+
) -> tuple[Optional[str], Optional[int]]:
440450
publisher = self.get_container(service_name).get_publisher(by_port=port).normalize()
441451
return publisher.URL, publisher.PublishedPort
442452

core/testcontainers/core/config.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
from os import environ
55
from os.path import exists
66
from pathlib import Path
7-
from typing import Optional, Union
7+
from typing import Optional, Union, cast
88

99
import docker
1010

@@ -36,6 +36,7 @@ def get_docker_socket() -> str:
3636
client = docker.from_env()
3737
try:
3838
socket_path = client.api.get_adapter(client.api.base_url).socket_path
39+
socket_path = cast("str", socket_path)
3940
# return the normalized path as string
4041
return str(Path(socket_path).absolute())
4142
except AttributeError:
@@ -145,5 +146,6 @@ def timeout(self) -> int:
145146
"SLEEP_TIME",
146147
"TIMEOUT",
147148
# Public API of this module:
149+
"ConnectionMode",
148150
"testcontainers_config",
149151
]

0 commit comments

Comments
 (0)