Skip to content

Commit 20ac61c

Browse files
committed
Add type annotations for compose.
1 parent 053d07f commit 20ac61c

File tree

1 file changed

+52
-79
lines changed

1 file changed

+52
-79
lines changed

compose/testcontainers/compose/__init__.py

Lines changed: 52 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
import requests
99
import subprocess
10+
from typing import Iterable, List, Optional, Tuple, Union
1011

1112
from testcontainers.core.waiting_utils import wait_container_is_ready
1213
from testcontainers.core.exceptions import NoSuchPortExposed
@@ -16,18 +17,12 @@ class DockerCompose:
1617
"""
1718
Manage docker compose environments.
1819
19-
Parameters
20-
----------
21-
filepath: str
22-
The relative directory containing the docker compose configuration file
23-
compose_file_name: str
24-
The file name of the docker compose configuration file
25-
pull: bool
26-
Attempts to pull images before launching environment
27-
build: bool
28-
Whether to build images referenced in the configuration file
29-
env_file: str
30-
Path to an env file containing environment variables to pass to docker compose
20+
Args:
21+
filepath: Relative directory containing the docker compose configuration file.
22+
compose_file_name: File name of the docker compose configuration file.
23+
pull: Pull images before launching environment.
24+
build: Build images referenced in the configuration file.
25+
env_file: Path to an env file containing environment variables to pass to docker compose.
3126
3227
Example
3328
-------
@@ -67,37 +62,33 @@ class DockerCompose:
6762
expose:
6863
- "5555"
6964
"""
70-
7165
def __init__(
7266
self,
73-
filepath,
74-
compose_file_name="docker-compose.yml",
75-
pull=False,
76-
build=False,
77-
env_file=None):
67+
filepath: str,
68+
compose_file_name: Union[str, Iterable] = "docker-compose.yml",
69+
pull: bool = False,
70+
build: bool = False,
71+
env_file: Optional[str] = None) -> None:
7872
self.filepath = filepath
79-
self.compose_file_names = compose_file_name if isinstance(
80-
compose_file_name, (list, tuple)
81-
) else [compose_file_name]
73+
self.compose_file_names = [compose_file_name] if isinstance(compose_file_name, str) else \
74+
list(compose_file_name)
8275
self.pull = pull
8376
self.build = build
8477
self.env_file = env_file
8578

86-
def __enter__(self):
79+
def __enter__(self) -> "DockerCompose":
8780
self.start()
8881
return self
8982

90-
def __exit__(self, exc_type, exc_val, exc_tb):
83+
def __exit__(self, exc_type, exc_val, exc_tb) -> None:
9184
self.stop()
9285

93-
def docker_compose_command(self):
86+
def docker_compose_command(self) -> List[str]:
9487
"""
9588
Returns command parts used for the docker compose commands
9689
97-
Returns
98-
-------
99-
list[str]
100-
The docker compose command parts
90+
Returns:
91+
cmd: Docker compose command parts.
10192
"""
10293
docker_compose_cmd = ['docker-compose']
10394
for file in self.compose_file_names:
@@ -106,7 +97,7 @@ def docker_compose_command(self):
10697
docker_compose_cmd += ['--env-file', self.env_file]
10798
return docker_compose_cmd
10899

109-
def start(self):
100+
def start(self) -> None:
110101
"""
111102
Starts the docker compose environment.
112103
"""
@@ -120,21 +111,20 @@ def start(self):
120111

121112
self._call_command(cmd=up_cmd)
122113

123-
def stop(self):
114+
def stop(self) -> None:
124115
"""
125116
Stops the docker compose environment.
126117
"""
127118
down_cmd = self.docker_compose_command() + ['down', '-v']
128119
self._call_command(cmd=down_cmd)
129120

130-
def get_logs(self):
121+
def get_logs(self) -> Tuple[str, str]:
131122
"""
132123
Returns all log output from stdout and stderr
133124
134-
Returns
135-
-------
136-
tuple[bytes, bytes]
137-
stdout, stderr
125+
Returns:
126+
stdout: Standard output stream.
127+
stderr: Standard error stream.
138128
"""
139129
logs_cmd = self.docker_compose_command() + ["logs"]
140130
result = subprocess.run(
@@ -145,21 +135,17 @@ def get_logs(self):
145135
)
146136
return result.stdout, result.stderr
147137

148-
def exec_in_container(self, service_name, command):
138+
def exec_in_container(self, service_name: str, command: List[str]) -> Tuple[str, str]:
149139
"""
150140
Executes a command in the container of one of the services.
151141
152-
Parameters
153-
----------
154-
service_name: str
155-
Name of the docker compose service to run the command in
156-
command: list[str]
157-
The command to execute
142+
Args:
143+
service_name: Name of the docker compose service to run the command in.
144+
command: Command to execute.
158145
159-
Returns
160-
-------
161-
tuple[str, str, int]
162-
stdout, stderr, return code
146+
Returns:
147+
stdout: Standard output stream.
148+
stderr: Standard error stream.
163149
"""
164150
exec_cmd = self.docker_compose_command() + ['exec', '-T', service_name] + command
165151
result = subprocess.run(
@@ -170,67 +156,54 @@ def exec_in_container(self, service_name, command):
170156
)
171157
return result.stdout.decode("utf-8"), result.stderr.decode("utf-8"), result.returncode
172158

173-
def get_service_port(self, service_name, port):
159+
def get_service_port(self, service_name: str, port: int) -> int:
174160
"""
175161
Returns the mapped port for one of the services.
176162
177-
Parameters
178-
----------
179-
service_name: str
180-
Name of the docker compose service
181-
port: int
182-
The internal port to get the mapping for
163+
Args:
164+
service_name: Name of the docker compose service.
165+
port: Internal port to get the mapping for.
183166
184-
Returns
185-
-------
186-
str:
187-
The mapped port on the host
167+
Returns:
168+
mapped_port: Mapped port on the host.
188169
"""
189170
return self._get_service_info(service_name, port)[1]
190171

191-
def get_service_host(self, service_name, port):
172+
def get_service_host(self, service_name: str, port: int) -> str:
192173
"""
193174
Returns the host for one of the services.
194175
195-
Parameters
196-
----------
197-
service_name: str
198-
Name of the docker compose service
199-
port: int
200-
The internal port to get the host for
176+
Args:
177+
service_name: Name of the docker compose service.
178+
port: Internal port to get the mapping for.
201179
202-
Returns
203-
-------
204-
str:
205-
The hostname for the service
180+
Returns:
181+
host: Hostname for the service.
206182
"""
207183
return self._get_service_info(service_name, port)[0]
208184

209-
def _get_service_info(self, service, port):
185+
def _get_service_info(self, service: str, port: int) -> List[str]:
210186
port_cmd = self.docker_compose_command() + ["port", service, str(port)]
211187
output = subprocess.check_output(port_cmd, cwd=self.filepath).decode("utf-8")
212188
result = str(output).rstrip().split(":")
213189
if len(result) != 2 or not all(result):
214190
raise NoSuchPortExposed(f"port {port} is not exposed for service {service}")
215191
return result
216192

217-
def _call_command(self, cmd, filepath=None):
193+
def _call_command(self, cmd: Union[str, List[str]], filepath: Optional[str] = None) -> None:
218194
if filepath is None:
219195
filepath = self.filepath
220196
subprocess.call(cmd, cwd=filepath)
221197

222198
@wait_container_is_ready(requests.exceptions.ConnectionError)
223-
def wait_for(self, url):
199+
def wait_for(self, url: str) -> 'DockerCompose':
224200
"""
225-
Waits for a response from a given URL. This is typically used to
226-
block until a service in the environment has started and is responding.
227-
Note that it does not assert any sort of return code, only check that
228-
the connection was successful.
201+
Waits for a response from a given URL. This is typically used to block until a service in
202+
the environment has started and is responding. Note that it does not assert any sort of
203+
return code, only check that the connection was successful.
229204
230-
Parameters
231-
----------
232-
url: str
233-
URL from one of the services in the environment to use to wait on
205+
Args:
206+
url: URL from one of the services in the environment to use to wait on.
234207
"""
235208
requests.get(url)
236209
return self

0 commit comments

Comments
 (0)