From c7bcc898bdd8bc127e3de96f52dc4c9f131383c9 Mon Sep 17 00:00:00 2001 From: Marc Schmitzer Date: Thu, 9 Oct 2025 11:32:00 +0200 Subject: [PATCH 1/2] fix(redis): Use wait strategy instead of deprecated decorator Part of fixing #874. --- .../redis/testcontainers/redis/__init__.py | 23 +++++++++---------- 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/modules/redis/testcontainers/redis/__init__.py b/modules/redis/testcontainers/redis/__init__.py index 7a4d46613..24895b328 100644 --- a/modules/redis/testcontainers/redis/__init__.py +++ b/modules/redis/testcontainers/redis/__init__.py @@ -17,7 +17,7 @@ from redis.asyncio import Redis as asyncRedis from testcontainers.core.container import DockerContainer from testcontainers.core.utils import raise_for_deprecated_parameter -from testcontainers.core.waiting_utils import wait_container_is_ready +from testcontainers.core.waiting_utils import WaitStrategy, WaitStrategyTarget class RedisContainer(DockerContainer): @@ -36,19 +36,13 @@ class RedisContainer(DockerContainer): def __init__(self, image: str = "redis:latest", port: int = 6379, password: Optional[str] = None, **kwargs) -> None: raise_for_deprecated_parameter(kwargs, "port_to_expose", "port") - super().__init__(image, **kwargs) + super().__init__(image, _wait_strategy=PingWaitStrategy(), **kwargs) self.port = port self.password = password self.with_exposed_ports(self.port) if self.password: self.with_command(f"redis-server --requirepass {self.password}") - @wait_container_is_ready(redis.exceptions.ConnectionError) - def _connect(self) -> None: - client = self.get_client() - if not client.ping(): - raise redis.exceptions.ConnectionError("Could not connect to Redis") - def get_client(self, **kwargs) -> redis.Redis: """ Get a redis client. @@ -66,10 +60,15 @@ def get_client(self, **kwargs) -> redis.Redis: **kwargs, ) - def start(self) -> "RedisContainer": - super().start() - self._connect() - return self + +class PingWaitStrategy(WaitStrategy): + def __init__(self) -> None: + super().__init__() + self.with_transient_exceptions(redis.exceptions.ConnectionError) + + def wait_until_ready(self, container: WaitStrategyTarget) -> None: + if not self._poll(lambda: container.get_client().ping()): + raise redis.exceptions.ConnectionError("Could not connect to Redis") class AsyncRedisContainer(RedisContainer): From a9d5bc947fd40d33712473b3f4ea51d92b9d210c Mon Sep 17 00:00:00 2001 From: Marc Schmitzer Date: Tue, 4 Nov 2025 11:31:07 +0100 Subject: [PATCH 2/2] test(redis): Add test for wait strategy --- modules/redis/tests/test_redis.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/modules/redis/tests/test_redis.py b/modules/redis/tests/test_redis.py index bd8e244c5..01be35f14 100644 --- a/modules/redis/tests/test_redis.py +++ b/modules/redis/tests/test_redis.py @@ -2,6 +2,7 @@ from testcontainers.redis import RedisContainer, AsyncRedisContainer import pytest +import redis def test_docker_run_redis(): @@ -24,6 +25,16 @@ def test_docker_run_redis_with_password(): assert client.get("hello") == "world" +def test_docker_run_start_fails(monkeypatch: pytest.MonkeyPatch): + # Patch config to speed up the test. + monkeypatch.setattr("testcontainers.core.config.testcontainers_config.max_tries", 0.3) + monkeypatch.setattr("testcontainers.core.config.testcontainers_config.sleep_time", 0.02) + # Use a bogus image to make the startup check fail. + config = RedisContainer(image="hello-world") + with pytest.raises(redis.exceptions.ConnectionError, match="Could not connect"): + config.start() + + pytest.mark.usefixtures("anyio_backend")