Skip to content

Commit 0221ee2

Browse files
feat: DockerContainer initializer to accept its private members as kwargs (#809)
Re submitting what is the end result of the iterations in #238 submitted originally by @vikhal. Simply enabling the initializer of `DockerContainer` to accept its private members as kwargs. --------- Co-authored-by: David Ankin <[email protected]>
2 parents 2143e2b + 632e5f4 commit 0221ee2

File tree

15 files changed

+3146
-1997
lines changed

15 files changed

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

.github/workflows/ci-core.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Contrinuous Integration for the core package
1+
# Continuous Integration for the core package
22

33
name: core
44

@@ -25,6 +25,8 @@ jobs:
2525
run: poetry install --all-extras
2626
- name: Run twine check
2727
run: poetry build && poetry run twine check dist/*.tar.gz
28+
- name: Set up Docker
29+
uses: docker/setup-docker-action@v4
2830
- name: Run tests
2931
run: make core/tests
3032
- name: Rename coverage file

CHANGELOG.md

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

3+
## [4.11.0](https://github.com/testcontainers/testcontainers-python/compare/testcontainers-v4.10.0...testcontainers-v4.11.0) (2025-06-15)
4+
5+
6+
### Features
7+
8+
* **core:** Protocol support for container port bind and expose ([#690](https://github.com/testcontainers/testcontainers-python/issues/690)) ([a0d4317](https://github.com/testcontainers/testcontainers-python/commit/a0d4317643005dde4f344eccbfc56c062e83bf05))
9+
* DockerContainer initializer to accept its private members as kwargs ([#809](https://github.com/testcontainers/testcontainers-python/issues/809)) ([e7feb53](https://github.com/testcontainers/testcontainers-python/commit/e7feb53fe532b6d32d5d0c5a5d517249f8e7de50))
10+
11+
12+
### Bug Fixes
13+
14+
* **compose:** use provided docker command instead of default ([#785](https://github.com/testcontainers/testcontainers-python/issues/785)) ([0ae704a](https://github.com/testcontainers/testcontainers-python/commit/0ae704a24de440b715d5f3c11eaa4f18ccd437b5))
15+
* **core:** Add kwargs to image build ([#708](https://github.com/testcontainers/testcontainers-python/issues/708)) ([cc02f94](https://github.com/testcontainers/testcontainers-python/commit/cc02f9444b41efa62836b21210b07aee1da94d0b))
16+
* **core:** change with_command type to include list of strings ([#789](https://github.com/testcontainers/testcontainers-python/issues/789)) ([f7c29cb](https://github.com/testcontainers/testcontainers-python/commit/f7c29cb913e4d42d535783c3aa0f3566d4e543bf))
17+
* **core:** Determine docker socket for rootless docker ([#779](https://github.com/testcontainers/testcontainers-python/issues/779)) ([6817582](https://github.com/testcontainers/testcontainers-python/commit/6817582bf67ed36448b69019ab897c50ae80e7e1))
18+
* **core:** Typing in docker_client ([#702](https://github.com/testcontainers/testcontainers-python/issues/702)) ([e8bf224](https://github.com/testcontainers/testcontainers-python/commit/e8bf2244c7210e31b34e5fecf2602fdd1b8c0834))
19+
* **core:** Typing in generic + network ([#700](https://github.com/testcontainers/testcontainers-python/issues/700)) ([2061912](https://github.com/testcontainers/testcontainers-python/commit/2061912e67705be801136f349f372f542a1f262f))
20+
* **core:** Typing in version ([#701](https://github.com/testcontainers/testcontainers-python/issues/701)) ([9dc2a02](https://github.com/testcontainers/testcontainers-python/commit/9dc2a02ca9b9ffbaacfd7de79ec9f78175758ec0))
21+
* **core:** wait in test core registry ([#812](https://github.com/testcontainers/testcontainers-python/issues/812)) ([b574c0e](https://github.com/testcontainers/testcontainers-python/commit/b574c0e0a11d57c8c56aef448292f8c2fc233078))
22+
* **modules:** fix cosmosdb failure ([#827](https://github.com/testcontainers/testcontainers-python/issues/827)) ([dafcbed](https://github.com/testcontainers/testcontainers-python/commit/dafcbed7608e857bebcdd0b4638bec27abadc693))
23+
* **modules:** update chroma version ([#826](https://github.com/testcontainers/testcontainers-python/issues/826)) ([b7d41dd](https://github.com/testcontainers/testcontainers-python/commit/b7d41ddc5742dd380b6e01c712a02b044a64cbb3))
24+
* **rabbitmq:** correct pika pypi reference ([#817](https://github.com/testcontainers/testcontainers-python/issues/817)) ([e90d308](https://github.com/testcontainers/testcontainers-python/commit/e90d30826fb7d7cf3cc7db39a86465d448aaa6e0))
25+
* **registry:** module typed ([#811](https://github.com/testcontainers/testcontainers-python/issues/811)) ([6b11268](https://github.com/testcontainers/testcontainers-python/commit/6b1126884c82529a93bd55030374d322dd0870bc))
26+
* use connection mode override function in config ([#775](https://github.com/testcontainers/testcontainers-python/issues/775)) ([ab2a1ab](https://github.com/testcontainers/testcontainers-python/commit/ab2a1abd957ffb35719f673a7674df83287f1545)), closes [#774](https://github.com/testcontainers/testcontainers-python/issues/774)
27+
328
## [4.10.0](https://github.com/testcontainers/testcontainers-python/compare/testcontainers-v4.9.2...testcontainers-v4.10.0) (2025-04-02)
429

530

core/testcontainers/core/docker_client.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ def find_host_network(self) -> Optional[str]:
151151
except ipaddress.AddressValueError:
152152
continue
153153
if docker_host in subnet:
154-
return cast(str, network.name)
154+
return cast("str", network.name)
155155
except (ipaddress.AddressValueError, OSError):
156156
pass
157157
return None
@@ -163,7 +163,7 @@ def port(self, container_id: str, port: int) -> str:
163163
port_mappings = self.client.api.port(container_id, port)
164164
if not port_mappings:
165165
raise ConnectionError(f"Port mapping for container {container_id} and port {port} is not available")
166-
return cast(str, port_mappings[0]["HostPort"])
166+
return cast("str", port_mappings[0]["HostPort"])
167167

168168
def get_container(self, container_id: str) -> dict[str, Any]:
169169
"""
@@ -172,7 +172,7 @@ def get_container(self, container_id: str) -> dict[str, Any]:
172172
containers = self.client.api.containers(filters={"id": container_id})
173173
if not containers:
174174
raise RuntimeError(f"Could not get container with id {container_id}")
175-
return cast(dict[str, Any], containers[0])
175+
return cast("dict[str, Any]", containers[0])
176176

177177
def bridge_ip(self, container_id: str) -> str:
178178
"""
@@ -241,7 +241,7 @@ def host(self) -> str:
241241
hostname = url.hostname
242242
if not hostname or (hostname == "localnpipe" and utils.is_windows()):
243243
return "localhost"
244-
return cast(str, url.hostname)
244+
return cast("str", url.hostname)
245245
if utils.inside_container() and ("unix" in url.scheme or "npipe" in url.scheme):
246246
ip_address = utils.default_gateway_ip()
247247
if ip_address:
@@ -257,7 +257,7 @@ def login(self, auth_config: DockerAuthInfo) -> None:
257257

258258
def client_networks_create(self, name: str, param: dict[str, Any]) -> dict[str, Any]:
259259
labels = create_labels("", param.get("labels"))
260-
return cast(dict[str, Any], self.client.networks.create(name, **{**param, "labels": labels}))
260+
return cast("dict[str, Any]", self.client.networks.create(name, **{**param, "labels": labels}))
261261

262262

263263
def get_docker_host() -> Optional[str]:

core/tests/test_core_registry.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
from testcontainers.core.config import testcontainers_config
1616
from testcontainers.core.container import DockerContainer
1717
from testcontainers.core.docker_client import DockerClient
18-
from testcontainers.core.waiting_utils import wait_container_is_ready
18+
from testcontainers.core.waiting_utils import wait_for_logs
1919

2020
from testcontainers.registry import DockerRegistryContainer
2121
from testcontainers.core.utils import is_mac
@@ -43,22 +43,22 @@ def test_missing_on_private_registry(monkeypatch):
4343
with pytest.raises(NotFound):
4444
# Test a container with image from private registry
4545
with DockerContainer(f"{registry_url}/{image}:{tag}") as test_container:
46-
wait_container_is_ready(test_container)
46+
wait_for_logs(test_container, "Hello from Docker!")
4747

4848

4949
@pytest.mark.skipif(
5050
is_mac(),
5151
reason="Docker Desktop on macOS does not support local insecure registries over HTTP without modifying daemon settings",
5252
)
5353
@pytest.mark.parametrize(
54-
"image,tag,username,password",
54+
"image,tag,username,password,expected_output",
5555
[
56-
("nginx", "test", "user", "pass"),
57-
("hello-world", "latest", "new_user", "new_pass"),
58-
("alpine", "3.12", None, None),
56+
("nginx", "test", "user", "pass", "start worker processes"),
57+
("hello-world", "latest", "new_user", "new_pass", "Hello from Docker!"),
58+
("alpine", "3.12", None, None, ""),
5959
],
6060
)
61-
def test_with_private_registry(image, tag, username, password, monkeypatch):
61+
def test_with_private_registry(image, tag, username, password, expected_output, monkeypatch):
6262
client = DockerClient().client
6363

6464
with DockerRegistryContainer(username=username, password=password) as registry:
@@ -85,7 +85,7 @@ def test_with_private_registry(image, tag, username, password, monkeypatch):
8585

8686
# Test a container with image from private registry
8787
with DockerContainer(f"{registry_url}/{image}:{tag}") as test_container:
88-
wait_container_is_ready(test_container)
88+
wait_for_logs(test_container, expected_output)
8989

9090
# cleanup
9191
client.images.remove(f"{registry_url}/{image}:{tag}")

modules/chroma/testcontainers/chroma/__init__.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,13 @@ class ChromaContainer(DockerContainer):
3232

3333
def __init__(
3434
self,
35-
image: str = "chromadb/chroma:latest",
35+
image: str = "chromadb/chroma:1.0.0",
3636
port: int = 8000,
3737
**kwargs,
3838
) -> None:
3939
"""
4040
Args:
41-
image: Docker image to use for the MinIO container.
41+
image: Docker image to use for the ChromaDB container.
4242
port: Port to expose on the container.
4343
access_key: Access key for client connections.
4444
secret_key: Secret key for client connections.
@@ -55,7 +55,7 @@ def get_config(self) -> dict:
5555
including the endpoint.
5656
5757
Returns:
58-
dict: {`endpoint`: str}
58+
dict: {`endpoint`: str, `host`: str, `port`: int}
5959
"""
6060
host_ip = self.get_container_host_ip()
6161
exposed_port = self.get_exposed_port(self.port)
@@ -69,7 +69,7 @@ def get_config(self) -> dict:
6969
def _healthcheck(self) -> None:
7070
"""This is an internal method used to check if the Chroma container
7171
is healthy and ready to receive requests."""
72-
url = f"http://{self.get_config()['endpoint']}/api/v1/heartbeat"
72+
url = f"http://{self.get_config()['endpoint']}/api/v2/heartbeat"
7373
response: Response = get(url)
7474
response.raise_for_status()
7575

modules/chroma/tests/test_chroma.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44

55
def test_docker_run_chroma():
6-
with ChromaContainer(image="chromadb/chroma:0.4.24") as chroma:
6+
with ChromaContainer(image="chromadb/chroma:1.0.0") as chroma:
77
client = chromadb.HttpClient(host=chroma.get_config()["host"], port=chroma.get_config()["port"])
88
col = client.get_or_create_collection("test")
99
assert col.name == "test"

modules/cosmosdb/testcontainers/cosmosdb/mongodb.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ def __init__(
2727
self,
2828
mongodb_version: str,
2929
image: str = os.getenv(
30-
"AZURE_COSMOS_EMULATOR_IMAGE", "mcr.microsoft.com/cosmosdb/linux/azure-cosmos-emulator:mongodb"
30+
"AZURE_COSMOS_EMULATOR_IMAGE", "mcr.microsoft.com/cosmosdb/linux/azure-cosmos-emulator:latest"
3131
),
3232
**other_kwargs,
3333
):

modules/postgres/tests/test_postgres.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,3 +133,21 @@ def test_none_driver_urls():
133133

134134
url = container.get_connection_url(driver=None)
135135
assert url == expected_url
136+
137+
138+
def test_psycopg_versions():
139+
"""Test that both psycopg2 and psycopg (v2 and v3) work with the container."""
140+
141+
postgres_container = PostgresContainer("postgres:16-alpine", driver="psycopg2")
142+
with postgres_container as postgres:
143+
engine = sqlalchemy.create_engine(postgres.get_connection_url())
144+
with engine.begin() as connection:
145+
result = connection.execute(sqlalchemy.text("SELECT 1 as test"))
146+
assert result.scalar() == 1
147+
148+
postgres_container = PostgresContainer("postgres:16-alpine", driver="psycopg")
149+
with postgres_container as postgres:
150+
engine = sqlalchemy.create_engine(postgres.get_connection_url())
151+
with engine.begin() as connection:
152+
result = connection.execute(sqlalchemy.text("SELECT 1 as test"))
153+
assert result.scalar() == 1

modules/qdrant/testcontainers/qdrant/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ class QdrantContainer(DbContainer):
3939

4040
def __init__(
4141
self,
42-
image: str = "qdrant/qdrant:v1.8.3",
42+
image: str = "qdrant/qdrant:v1.13.5",
4343
rest_port: int = 6333,
4444
grpc_port: int = 6334,
4545
api_key: Optional[str] = None,

0 commit comments

Comments
 (0)