Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 6 additions & 10 deletions core/testcontainers/compose/compose.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from subprocess import CalledProcessError, CompletedProcess
from subprocess import run as subprocess_run
from types import TracebackType
from typing import Any, Callable, Literal, Optional, TypeVar, Union, cast
from typing import Any, Callable, Literal, Optional, Self, TypeVar, Union, cast

from testcontainers.core.docker_client import DockerClient, get_docker_host_hostname
from testcontainers.core.exceptions import ContainerIsNotRunning, NoSuchPortExposed
Expand All @@ -32,7 +32,7 @@ class PublishedPortModel:
PublishedPort: Optional[int] = None
Protocol: Optional[str] = None

def normalize(self) -> "PublishedPortModel":
def normalize(self) -> Self:
url = self.URL

# For SSH-based DOCKER_HOST, local addresses (0.0.0.0, 127.0.0.1, localhost, ::, ::1)
Expand Down Expand Up @@ -137,7 +137,7 @@ def get_logs(self) -> tuple[bytes, bytes]:
stdout, stderr = self._docker_compose.get_logs(self.Service)
return stdout.encode(), stderr.encode()

def get_wrapped_container(self) -> "ComposeContainer":
def get_wrapped_container(self) -> Self:
"""Get the underlying container object for compatibility."""
return self

Expand Down Expand Up @@ -251,7 +251,7 @@ def __post_init__(self) -> None:
if isinstance(self.env_file, str):
self.env_file = [self.env_file]

def __enter__(self) -> "DockerCompose":
def __enter__(self) -> Self:
try:
self.start()
return self
Expand Down Expand Up @@ -288,7 +288,7 @@ def compose_command_property(self) -> list[str]:
docker_compose_cmd += ["--env-file", env_file]
return docker_compose_cmd

def waiting_for(self, strategies: dict[str, WaitStrategy]) -> "DockerCompose":
def waiting_for(self, strategies: dict[str, WaitStrategy]) -> Self:
"""
Set wait strategies for specific services.

Expand Down Expand Up @@ -557,7 +557,7 @@ def get_service_host_and_port(
publisher = self.get_container(service_name).get_publisher(by_port=port).normalize()
return publisher.URL, publisher.PublishedPort

def wait_for(self, url: str) -> "DockerCompose":
def wait_for(self, url: str) -> Self:
"""
Waits for a response from a given URL. This is typically used to block until a service in
the environment has started and is responding. Note that it does not assert any sort of
Expand Down Expand Up @@ -605,10 +605,6 @@ def wait_for(self, url: str) -> "DockerCompose":

time.sleep(1)

with urlopen(url) as response:
response.read()
return self

def _get_docker_client(self) -> DockerClient:
"""Get Docker client instance."""
if self._docker_client is None:
Expand Down
12 changes: 6 additions & 6 deletions core/testcontainers/core/container.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@
from os import PathLike
from socket import socket
from types import TracebackType
from typing import TYPE_CHECKING, Any, Optional, TypedDict, Union, cast
from typing import TYPE_CHECKING, Any, Optional, Self, TypedDict, Union, cast

import docker.errors
from docker import version
from docker.models.containers import ExecResult
from docker.types import EndpointConfig
from dotenv import dotenv_values
from typing_extensions import Self, assert_never
from typing_extensions import assert_never

from testcontainers.core.config import ConnectionMode
from testcontainers.core.config import testcontainers_config as c
Expand Down Expand Up @@ -390,14 +390,14 @@ class Reaper:
:meta private:
"""

_instance: "Optional[Reaper]" = None
_instance: Optional[Self] = None
_container: Optional[DockerContainer] = None
_socket: Optional[socket] = None

@classmethod
def get_instance(cls) -> "Reaper":
def get_instance(cls) -> Self:
if not Reaper._instance:
Reaper._instance = Reaper._create_instance()
Reaper._create_instance()

return Reaper._instance

Expand All @@ -416,7 +416,7 @@ def delete_instance(cls) -> None:
Reaper._instance = None

@classmethod
def _create_instance(cls) -> "Reaper":
def _create_instance(cls) -> Self:
logger.debug(f"Creating new Reaper for session: {SESSION_ID}")

Reaper._container = (
Expand Down
2 changes: 1 addition & 1 deletion core/testcontainers/core/docker_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,7 @@ def client_networks_create(self, name: str, param: dict[str, Any]) -> "DockerNet
labels = create_labels("", param.get("labels"))
return self.client.networks.create(name, **{**param, "labels": labels})

def get_container_inspect_info(self, container_id: str) -> "ContainerInspectInfo":
def get_container_inspect_info(self, container_id: str) -> ContainerInspectInfo:
"""Get container inspect information with fresh data."""
container = self.client.containers.get(container_id)
return ContainerInspectInfo.from_dict(container.attrs)
Expand Down
4 changes: 2 additions & 2 deletions core/testcontainers/core/generic.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from typing import Any, Optional
from typing import Any, Optional, Self
from urllib.parse import quote

from testcontainers.core.container import DockerContainer
Expand Down Expand Up @@ -74,7 +74,7 @@ def _create_connection_url(
url = f"{url}/{dbname}"
return url

def start(self) -> "DbContainer":
def start(self) -> Self:
self._configure()
super().start()
self._transfer_seed()
Expand Down
4 changes: 1 addition & 3 deletions core/testcontainers/core/image.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
from os import PathLike
from types import TracebackType
from typing import TYPE_CHECKING, Any, Optional, Union

from typing_extensions import Self
from typing import TYPE_CHECKING, Any, Optional, Self, Union

from testcontainers.core.docker_client import DockerClient
from testcontainers.core.utils import setup_logger
Expand Down
4 changes: 2 additions & 2 deletions core/testcontainers/core/inspect.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
"""Docker Engine API data structures for container inspect responses."""

from dataclasses import dataclass, fields, is_dataclass
from typing import Any, Optional, TypeVar
from typing import Any, Optional, Self, TypeVar

_IPT = TypeVar("_IPT")

Expand Down Expand Up @@ -523,7 +523,7 @@ class ContainerInspectInfo:
NetworkSettings: Optional[ContainerNetworkSettings] = None

@classmethod
def from_dict(cls, data: dict[str, Any]) -> "ContainerInspectInfo":
def from_dict(cls, data: dict[str, Any]) -> Self:
"""Create from docker inspect JSON."""
return cls(
Id=data.get("Id"),
Expand Down
6 changes: 3 additions & 3 deletions core/testcontainers/core/network.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
# under the License.
import uuid
from types import TracebackType
from typing import TYPE_CHECKING, Any, Optional
from typing import TYPE_CHECKING, Any, Optional, Self

from testcontainers.core.docker_client import DockerClient

Expand Down Expand Up @@ -52,11 +52,11 @@ def connect(self, container_id: str, network_aliases: Optional[list[str]] = None
def remove(self) -> None:
self._unwrap_network.remove()

def create(self) -> "Network":
def create(self) -> Self:
self._network = self._docker.client_networks_create(self.name, self._docker_network_kw)
return self

def __enter__(self) -> "Network":
def __enter__(self) -> Self:
return self.create()

def __exit__(
Expand Down
22 changes: 10 additions & 12 deletions core/testcontainers/core/wait_strategies.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,10 @@
import time
from datetime import timedelta
from pathlib import Path
from typing import TYPE_CHECKING, Any, Callable, Optional, Union, cast
from typing import TYPE_CHECKING, Any, Callable, Optional, Self, Union, cast
from urllib.error import HTTPError, URLError
from urllib.request import Request, urlopen

from typing_extensions import Self

from testcontainers.compose import DockerCompose
from testcontainers.core.utils import setup_logger

Expand Down Expand Up @@ -193,7 +191,7 @@ def __init__(self, port: int, path: Optional[str] = "/") -> None:
self._insecure_tls = False

@classmethod
def from_url(cls, url: str) -> "HttpWaitStrategy":
def from_url(cls, url: str) -> Self:
"""
Create an HttpWaitStrategy from a URL string.

Expand All @@ -219,7 +217,7 @@ def from_url(cls, url: str) -> "HttpWaitStrategy":

return strategy

def for_status_code(self, code: int) -> "HttpWaitStrategy":
def for_status_code(self, code: int) -> Self:
"""
Add an acceptable status code.

Expand All @@ -232,7 +230,7 @@ def for_status_code(self, code: int) -> "HttpWaitStrategy":
self._status_codes.add(code)
return self

def for_status_code_matching(self, predicate: Callable[[int], bool]) -> "HttpWaitStrategy":
def for_status_code_matching(self, predicate: Callable[[int], bool]) -> Self:
"""
Set a predicate to match status codes against.

Expand All @@ -245,7 +243,7 @@ def for_status_code_matching(self, predicate: Callable[[int], bool]) -> "HttpWai
self._status_code_predicate = predicate
return self

def for_response_predicate(self, predicate: Callable[[str], bool]) -> "HttpWaitStrategy":
def for_response_predicate(self, predicate: Callable[[str], bool]) -> Self:
"""
Set a predicate to match response body against.

Expand All @@ -258,7 +256,7 @@ def for_response_predicate(self, predicate: Callable[[str], bool]) -> "HttpWaitS
self._response_predicate = predicate
return self

def using_tls(self, insecure: bool = False) -> "HttpWaitStrategy":
def using_tls(self, insecure: bool = False) -> Self:
"""
Use HTTPS instead of HTTP.

Expand All @@ -272,7 +270,7 @@ def using_tls(self, insecure: bool = False) -> "HttpWaitStrategy":
self._insecure_tls = insecure
return self

def with_header(self, name: str, value: str) -> "HttpWaitStrategy":
def with_header(self, name: str, value: str) -> Self:
"""
Add a header to the request.

Expand All @@ -286,7 +284,7 @@ def with_header(self, name: str, value: str) -> "HttpWaitStrategy":
self._headers[name] = value
return self

def with_basic_credentials(self, username: str, password: str) -> "HttpWaitStrategy":
def with_basic_credentials(self, username: str, password: str) -> Self:
"""
Add basic auth credentials.

Expand All @@ -300,7 +298,7 @@ def with_basic_credentials(self, username: str, password: str) -> "HttpWaitStrat
self._basic_auth = (username, password)
return self

def with_method(self, method: str) -> "HttpWaitStrategy":
def with_method(self, method: str) -> Self:
"""
Set the HTTP method to use.

Expand All @@ -313,7 +311,7 @@ def with_method(self, method: str) -> "HttpWaitStrategy":
self._method = method.upper()
return self

def with_body(self, body: str) -> "HttpWaitStrategy":
def with_body(self, body: str) -> Self:
"""
Set the request body.

Expand Down
6 changes: 3 additions & 3 deletions core/testcontainers/socat/socat.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
import random
import socket
import string
from typing import Any, Optional
from typing import Any, Optional, Self

from testcontainers.core.container import DockerContainer
from testcontainers.core.waiting_utils import wait_container_is_ready
Expand Down Expand Up @@ -46,7 +46,7 @@ def __init__(

super().__init__(image=image, **kwargs)

def with_target(self, exposed_port: int, host: str, internal_port: Optional[int] = None) -> "SocatContainer":
def with_target(self, exposed_port: int, host: str, internal_port: Optional[int] = None) -> Self:
"""
Add a target to forward connections from the exposed port to the given host and port.

Expand Down Expand Up @@ -77,7 +77,7 @@ def _configure(self) -> None:

self.with_command(f'-c "{command}"')

def start(self) -> "SocatContainer":
def start(self) -> Self:
super().start()
self._connect()
return self
Expand Down
4 changes: 2 additions & 2 deletions modules/azurite/testcontainers/azurite/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
# under the License.
import enum
import os
from typing import Optional
from typing import Optional, Self

from testcontainers.core.container import DockerContainer
from testcontainers.core.utils import raise_for_deprecated_parameter
Expand Down Expand Up @@ -217,7 +217,7 @@ def __get_external_connection_string(self) -> str:

return connection_string

def start(self) -> "AzuriteContainer":
def start(self) -> Self:
super().start()
self._connect()
return self
Expand Down
4 changes: 2 additions & 2 deletions modules/chroma/testcontainers/chroma/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, Self

from requests import ConnectionError, get

Expand Down Expand Up @@ -73,7 +73,7 @@ def _healthcheck(self) -> None:
response: Response = get(url)
response.raise_for_status()

def start(self) -> "ChromaContainer":
def start(self) -> Self:
"""This method starts the Chroma container and runs the healthcheck
to verify that the container is ready to use."""
super().start()
Expand Down
4 changes: 2 additions & 2 deletions modules/generic/testcontainers/generic/server.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import Union
from typing import Union, Self

import httpx

Expand Down Expand Up @@ -53,7 +53,7 @@ def _create_connection_url(self) -> str:
url = f"http://{host}:{exposed_port}"
return url

def start(self) -> "ServerContainer":
def start(self) -> Self:
super().start()
self._connect()
return self
Expand Down
4 changes: 2 additions & 2 deletions modules/generic/testcontainers/generic/sql.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import logging
from typing import Any, Optional
from typing import Any, Optional, Self
from urllib.parse import quote, urlencode

from testcontainers.core.container import DockerContainer
Expand Down Expand Up @@ -83,7 +83,7 @@ def _create_connection_url(

return url

def start(self) -> "SqlContainer":
def start(self) -> Self:
"""
Start the database container and perform initialization.

Expand Down
4 changes: 2 additions & 2 deletions modules/influxdb/testcontainers/influxdb.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
so you won't have to install dependencies that you do not need
"""

from typing import Optional
from typing import Optional, Self

from requests import get
from requests.exceptions import ConnectionError, ReadTimeout
Expand Down Expand Up @@ -90,7 +90,7 @@ def get_influxdb_version(self) -> str:

return self._health_check().get("version")

def start(self) -> "InfluxDbContainer":
def start(self) -> Self:
"""
Spawns a container of the InfluxDB Docker image, ready to be used.
"""
Expand Down
Loading