Skip to content

Commit 087b18b

Browse files
Merge branch 'main' into mypy_update_report
2 parents 108721a + a0d4317 commit 087b18b

File tree

37 files changed

+987
-98
lines changed

37 files changed

+987
-98
lines changed
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
{
2-
".": "4.8.2"
2+
".": "4.10.0"
33
}

.github/CONTRIBUTING.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,13 @@ You need to have the following tools available to you:
3333
- Run `make install` to get `poetry` to install all dependencies and set up `pre-commit`
3434
- **Recommended**: Run `make` or `make help` to see other commands available to you.
3535
- After this, you should have a working virtual environment and proceed with writing code with your favourite IDE
36-
- **TIP**: You can run `make core/tests` or `make module/<my-module>/tests` to run the tests specifically for that to speed up feedback cycles
36+
- **TIP**: You can run `make core/tests` or `make modules/<my-module>/tests` to run the tests specifically for that to speed up feedback cycles
3737
- You can also run `make lint` to run the `pre-commit` for the entire codebase.
3838

3939

4040
## Adding new containers
4141

42-
We have an [issue template](.github/ISSUE_TEMPLATE/new-container.md) for adding new containers, please refer to that for more information.
42+
We have an [issue template](./ISSUE_TEMPLATE/new-container.md) for adding new containers, please refer to that for more information.
4343
Once you've talked to the maintainers (we do our best to reply!) then you can proceed with contributing the new container.
4444

4545
> [!WARNING]

.github/workflows/ci-community.yml

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,20 +19,20 @@ jobs:
1919
- name: Checkout contents
2020
uses: actions/checkout@v4
2121
with:
22-
fetch-depth: 0 # recommended by tj-actions/changed-files
22+
fetch-depth: 0
2323
- name: Get changed files
2424
id: changed-files
25-
uses: tj-actions/changed-files@v42
25+
uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3
2626
with:
27-
path: "./modules"
28-
diff_relative: true
29-
dir_names: true
30-
dir_names_exclude_current_dir: true
31-
json: true
27+
base: ${{ github.ref }}
28+
list-files: 'json'
29+
filters: |
30+
modules:
31+
- 'modules/**'
3232
- name: Compute modules from files
3333
id: compute-changes
3434
run: |
35-
modules=$(echo "${{ steps.changed-files.outputs.all_changed_files }}" | jq '.[] | split("/") | first' | jq -s -c '. | unique')
35+
modules=$(echo "${{ toJson(steps.changed-files.outputs.modules_files) }}" | jq '.[] | split("/") | nth(1)' | jq -s -c '. | unique')
3636
echo "computed_modules=$modules"
3737
echo "computed_modules=$modules" >> $GITHUB_OUTPUT
3838
outputs:

.readthedocs.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,6 @@ build:
1414
# https://github.com/readthedocs/readthedocs.org/issues/4912#issuecomment-1143587902s
1515
jobs:
1616
post_install:
17-
- pip install poetry==1.7.1 # match version from poetry.lock
17+
- pip install poetry==2.1.2 # match version from poetry.lock
1818
- poetry config virtualenvs.create false
1919
- poetry install --all-extras

CHANGELOG.md

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,66 @@
11
# Changelog
22

3+
## [4.10.0](https://github.com/testcontainers/testcontainers-python/compare/testcontainers-v4.9.2...testcontainers-v4.10.0) (2025-04-02)
4+
5+
6+
### Features
7+
8+
* Add SocatContainer ([#795](https://github.com/testcontainers/testcontainers-python/issues/795)) ([2f9139c](https://github.com/testcontainers/testcontainers-python/commit/2f9139ca3ea9fba36325373b63635a5f539a3003))
9+
10+
11+
### Bug Fixes
12+
13+
* **ollama:** make device request a list ([#799](https://github.com/testcontainers/testcontainers-python/issues/799)) ([9497a45](https://github.com/testcontainers/testcontainers-python/commit/9497a45c39d13761aa3dd30dd5605676cbbe4b46))
14+
* **security:** Update track-modules job ([#787](https://github.com/testcontainers/testcontainers-python/issues/787)) ([f979525](https://github.com/testcontainers/testcontainers-python/commit/f97952505eba089f9cbbc979f8091dafbf520669))
15+
16+
## [4.9.2](https://github.com/testcontainers/testcontainers-python/compare/testcontainers-v4.9.1...testcontainers-v4.9.2) (2025-02-26)
17+
18+
19+
### Bug Fixes
20+
21+
* Change env var disabling OpenSearch security plugin ([#773](https://github.com/testcontainers/testcontainers-python/issues/773)) ([2620d7f](https://github.com/testcontainers/testcontainers-python/commit/2620d7fb1157caa18c3bef4bf2f9b3b79cd2f075))
22+
* **core:** create_label test ([#771](https://github.com/testcontainers/testcontainers-python/issues/771)) ([7517297](https://github.com/testcontainers/testcontainers-python/commit/751729722a013b46f67c09b4318b1b3d92b98008))
23+
* **core:** multiple container start invocations with custom labels ([#769](https://github.com/testcontainers/testcontainers-python/issues/769)) ([3e783a8](https://github.com/testcontainers/testcontainers-python/commit/3e783a80aa11b9c87201404a895d922624f0d451))
24+
* **keycloak:** Fixed Keycloak testcontainer for latest version v26.1.0 ([#766](https://github.com/testcontainers/testcontainers-python/issues/766)) ([b1642e9](https://github.com/testcontainers/testcontainers-python/commit/b1642e98c4d349564c4365782d1b58c9810b719a))
25+
* **scylla:** scylla get cluster method ([#778](https://github.com/testcontainers/testcontainers-python/issues/778)) ([46913c1](https://github.com/testcontainers/testcontainers-python/commit/46913c18a8b6f37bf8dc193828148926b6fc56a8))
26+
27+
28+
### Documentation
29+
30+
* Fixed typo in CONTRIBUTING.md ([#767](https://github.com/testcontainers/testcontainers-python/issues/767)) ([f0bb0f5](https://github.com/testcontainers/testcontainers-python/commit/f0bb0f54bea83885698bd137e24c397498709362))
31+
32+
## [4.9.1](https://github.com/testcontainers/testcontainers-python/compare/testcontainers-v4.9.0...testcontainers-v4.9.1) (2025-01-21)
33+
34+
35+
### Bug Fixes
36+
37+
* milvus healthcheck: use correct requests errors ([#759](https://github.com/testcontainers/testcontainers-python/issues/759)) ([78b137c](https://github.com/testcontainers/testcontainers-python/commit/78b137cfe53fc81eb8d5d858e98610fb6a8792ad))
38+
* **mysql:** add dialect parameter instead of hardcoded mysql dialect ([#739](https://github.com/testcontainers/testcontainers-python/issues/739)) ([8d77bd3](https://github.com/testcontainers/testcontainers-python/commit/8d77bd3541e1c5e73c7ed5d5bd3c0d7bb617f5c0))
39+
* **tests:** replace dind-test direct docker usage with sdk ([#750](https://github.com/testcontainers/testcontainers-python/issues/750)) ([ace2a7d](https://github.com/testcontainers/testcontainers-python/commit/ace2a7d143fb80576ddc0859a9106aa8652f2356))
40+
41+
## [4.9.0](https://github.com/testcontainers/testcontainers-python/compare/testcontainers-v4.8.2...testcontainers-v4.9.0) (2024-11-26)
42+
43+
44+
### Features
45+
46+
* **compose:** support for setting profiles ([#738](https://github.com/testcontainers/testcontainers-python/issues/738)) ([3e00e71](https://github.com/testcontainers/testcontainers-python/commit/3e00e71da4d2b5e7fd30315468d4e54c86ba6150))
47+
* **core:** Support working with env files ([#737](https://github.com/testcontainers/testcontainers-python/issues/737)) ([932ee30](https://github.com/testcontainers/testcontainers-python/commit/932ee307955e3591a63f194aee8e2f6d8e2f6bf9))
48+
49+
50+
### Bug Fixes
51+
52+
* allow running all tests ([#721](https://github.com/testcontainers/testcontainers-python/issues/721)) ([f958cf9](https://github.com/testcontainers/testcontainers-python/commit/f958cf9fe62a5f3ee2dc255713ec8b16de6a767d))
53+
* **core:** Avoid hanging upon bad docker host connection ([#742](https://github.com/testcontainers/testcontainers-python/issues/742)) ([4ced198](https://github.com/testcontainers/testcontainers-python/commit/4ced1983162914fe511a6e714f136b670e1dbdfb))
54+
* **core:** running testcontainer inside container ([#714](https://github.com/testcontainers/testcontainers-python/issues/714)) ([85a6666](https://github.com/testcontainers/testcontainers-python/commit/85a66667c23d76e87aecc6761bbb01429adb3dee))
55+
* **generic:** Also catch URLError waiting for ServerContainer ([#743](https://github.com/testcontainers/testcontainers-python/issues/743)) ([24e354f](https://github.com/testcontainers/testcontainers-python/commit/24e354f3bfa5912eaf7877da9442a885d7872f1a))
56+
* update wait_for_logs to not throw on 'created', and an optimization ([#719](https://github.com/testcontainers/testcontainers-python/issues/719)) ([271ca9a](https://github.com/testcontainers/testcontainers-python/commit/271ca9a0fef2e5f2b216457bfee44318e93990bf))
57+
* Vault health check ([#734](https://github.com/testcontainers/testcontainers-python/issues/734)) ([79434d6](https://github.com/testcontainers/testcontainers-python/commit/79434d6744b2918493884cf8fbf27aeadf78ecfd))
58+
59+
60+
### Documentation
61+
62+
* Documentation fix for ServerContainer ([#671](https://github.com/testcontainers/testcontainers-python/issues/671)) ([0303d47](https://github.com/testcontainers/testcontainers-python/commit/0303d47d7173e1c4ec1a4f565efee9b2fe694928))
63+
364
## [4.8.2](https://github.com/testcontainers/testcontainers-python/compare/testcontainers-v4.8.1...testcontainers-v4.8.2) (2024-09-27)
465

566

conf.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,4 +161,9 @@
161161
intersphinx_mapping = {
162162
"python": ("https://docs.python.org/3", None),
163163
"selenium": ("https://seleniumhq.github.io/selenium/docs/api/py/", None),
164+
"typing_extensions": ("https://typing-extensions.readthedocs.io/en/latest/", None),
164165
}
166+
167+
nitpick_ignore = [
168+
("py:class", "typing_extensions.Self"),
169+
]

core/README.rst

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,15 @@ Testcontainers Core
44
:code:`testcontainers-core` is the core functionality for spinning up Docker containers in test environments.
55

66
.. autoclass:: testcontainers.core.container.DockerContainer
7+
:members: with_bind_ports, with_exposed_ports
8+
9+
.. note::
10+
When using `with_bind_ports` or `with_exposed_ports`
11+
you can specify the port in the following formats: :code:`{private_port}/{protocol}`
12+
13+
e.g. `8080/tcp` or `8125/udp` or just `8080` (default protocol is tcp)
14+
15+
For legacy reasons, the port can be an *integer*
716

817
.. autoclass:: testcontainers.core.image.DockerImage
918

core/testcontainers/compose/compose.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,8 @@ class DockerCompose:
139139
The list of services to use from this DockerCompose.
140140
client_args:
141141
arguments to pass to docker.from_env()
142+
docker_command_path:
143+
The docker compose command to run.
142144
143145
Example:
144146
@@ -171,6 +173,7 @@ class DockerCompose:
171173
env_file: Optional[str] = None
172174
services: Optional[list[str]] = None
173175
docker_command_path: Optional[str] = None
176+
profiles: Optional[list[str]] = None
174177

175178
def __post_init__(self):
176179
if isinstance(self.compose_file_name, str):
@@ -194,10 +197,12 @@ def docker_compose_command(self) -> list[str]:
194197

195198
@cached_property
196199
def compose_command_property(self) -> list[str]:
197-
docker_compose_cmd = [self.docker_command_path or "docker", "compose"]
200+
docker_compose_cmd = [self.docker_command_path] if self.docker_command_path else ["docker", "compose"]
198201
if self.compose_file_name:
199202
for file in self.compose_file_name:
200203
docker_compose_cmd += ["-f", file]
204+
if self.profiles:
205+
docker_compose_cmd += [item for profile in self.profiles for item in ["--profile", profile]]
201206
if self.env_file:
202207
docker_compose_cmd += ["--env-file", self.env_file]
203208
return docker_compose_cmd

core/testcontainers/core/config.py

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
from pathlib import Path
77
from typing import Optional, Union
88

9+
import docker
10+
911

1012
class ConnectionMode(Enum):
1113
bridge_ip = "bridge_ip"
@@ -24,14 +26,32 @@ def use_mapped_port(self) -> bool:
2426
return True
2527

2628

29+
def get_docker_socket() -> str:
30+
"""
31+
Determine the docker socket, prefer value given by env variable
32+
33+
Using the docker api ensure we handle rootless docker properly
34+
"""
35+
if socket_path := environ.get("TESTCONTAINERS_DOCKER_SOCKET_OVERRIDE"):
36+
return socket_path
37+
38+
client = docker.from_env()
39+
try:
40+
socket_path = client.api.get_adapter(client.api.base_url).socket_path
41+
# return the normalized path as string
42+
return str(Path(socket_path).absolute())
43+
except AttributeError:
44+
return "/var/run/docker.sock"
45+
46+
2747
MAX_TRIES = int(environ.get("TC_MAX_TRIES", 120))
2848
SLEEP_TIME = int(environ.get("TC_POOLING_INTERVAL", 1))
2949
TIMEOUT = MAX_TRIES * SLEEP_TIME
3050

3151
RYUK_IMAGE: str = environ.get("RYUK_CONTAINER_IMAGE", "testcontainers/ryuk:0.8.1")
3252
RYUK_PRIVILEGED: bool = environ.get("TESTCONTAINERS_RYUK_PRIVILEGED", "false") == "true"
3353
RYUK_DISABLED: bool = environ.get("TESTCONTAINERS_RYUK_DISABLED", "false") == "true"
34-
RYUK_DOCKER_SOCKET: str = environ.get("TESTCONTAINERS_DOCKER_SOCKET_OVERRIDE", "/var/run/docker.sock")
54+
RYUK_DOCKER_SOCKET: str = get_docker_socket()
3555
RYUK_RECONNECTION_TIMEOUT: str = environ.get("RYUK_RECONNECTION_TIMEOUT", "10s")
3656
TC_HOST_OVERRIDE: Optional[str] = environ.get("TC_HOST", environ.get("TESTCONTAINERS_HOST_OVERRIDE"))
3757

@@ -86,7 +106,7 @@ class TestcontainersConfiguration:
86106
tc_properties: dict[str, str] = field(default_factory=read_tc_properties)
87107
_docker_auth_config: Optional[str] = field(default_factory=lambda: environ.get("DOCKER_AUTH_CONFIG"))
88108
tc_host_override: Optional[str] = TC_HOST_OVERRIDE
89-
connection_mode_override: Optional[ConnectionMode] = None
109+
connection_mode_override: Optional[ConnectionMode] = field(default_factory=get_user_overwritten_connection_mode)
90110

91111
"""
92112
https://github.com/testcontainers/testcontainers-go/blob/dd76d1e39c654433a3d80429690d07abcec04424/docker.go#L644

core/testcontainers/core/container.py

Lines changed: 46 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,18 @@
11
import contextlib
2+
from os import PathLike
23
from socket import socket
34
from typing import TYPE_CHECKING, Optional, Union
45

56
import docker.errors
67
from docker import version
78
from docker.types import EndpointConfig
9+
from dotenv import dotenv_values
810
from typing_extensions import Self, assert_never
911

1012
from testcontainers.core.config import ConnectionMode
1113
from testcontainers.core.config import testcontainers_config as c
1214
from testcontainers.core.docker_client import DockerClient
13-
from testcontainers.core.exceptions import ContainerStartException
15+
from testcontainers.core.exceptions import ContainerConnectException, ContainerStartException
1416
from testcontainers.core.labels import LABEL_SESSION_ID, SESSION_ID
1517
from testcontainers.core.network import Network
1618
from testcontainers.core.utils import is_arm, setup_logger
@@ -57,11 +59,44 @@ def with_env(self, key: str, value: str) -> Self:
5759
self.env[key] = value
5860
return self
5961

60-
def with_bind_ports(self, container: int, host: Optional[int] = None) -> Self:
62+
def with_env_file(self, env_file: Union[str, PathLike]) -> Self:
63+
env_values = dotenv_values(env_file)
64+
for key, value in env_values.items():
65+
self.with_env(key, value)
66+
return self
67+
68+
def with_bind_ports(self, container: Union[str, int], host: Optional[Union[str, int]] = None) -> Self:
69+
"""
70+
Bind container port to host port
71+
72+
:param container: container port
73+
:param host: host port
74+
75+
:doctest:
76+
77+
>>> from testcontainers.core.container import DockerContainer
78+
>>> container = DockerContainer("nginx")
79+
>>> container = container.with_bind_ports("8080/tcp", 8080)
80+
>>> container = container.with_bind_ports("8081/tcp", 8081)
81+
82+
"""
6183
self.ports[container] = host
6284
return self
6385

64-
def with_exposed_ports(self, *ports: int) -> Self:
86+
def with_exposed_ports(self, *ports: Union[str, int]) -> Self:
87+
"""
88+
Expose ports from the container without binding them to the host.
89+
90+
:param ports: ports to expose
91+
92+
:doctest:
93+
94+
>>> from testcontainers.core.container import DockerContainer
95+
>>> container = DockerContainer("nginx")
96+
>>> container = container.with_exposed_ports("8080/tcp", "8081/tcp")
97+
98+
"""
99+
65100
for port in ports:
66101
self.ports[port] = None
67102
return self
@@ -147,7 +182,7 @@ def get_exposed_port(self, port: int) -> int:
147182
return self.get_docker_client().port(self._container.id, port)
148183
return port
149184

150-
def with_command(self, command: str) -> Self:
185+
def with_command(self, command: Union[str, list[str]]) -> Self:
151186
self._command = command
152187
return self
153188

@@ -220,15 +255,21 @@ def _create_instance(cls) -> "Reaper":
220255
.with_env("RYUK_RECONNECTION_TIMEOUT", c.ryuk_reconnection_timeout)
221256
.start()
222257
)
223-
wait_for_logs(Reaper._container, r".* Started!")
258+
wait_for_logs(Reaper._container, r".* Started!", timeout=20, raise_on_exit=True)
224259

225260
container_host = Reaper._container.get_container_host_ip()
226261
container_port = int(Reaper._container.get_exposed_port(8080))
227262

263+
if not container_host or not container_port:
264+
raise ContainerConnectException(
265+
f"Could not obtain network details for {Reaper._container._container.id}. Host: {container_host} Port: {container_port}"
266+
)
267+
228268
last_connection_exception: Optional[Exception] = None
229269
for _ in range(50):
230270
try:
231271
Reaper._socket = socket()
272+
Reaper._socket.settimeout(1)
232273
Reaper._socket.connect((container_host, container_port))
233274
last_connection_exception = None
234275
break

0 commit comments

Comments
 (0)