1+ import types
2+ import warnings
3+ from collections .abc import Mapping
14from dataclasses import dataclass , field
25from enum import Enum
36from logging import warning
47from os import environ
58from os .path import exists
69from pathlib import Path
7- from typing import Optional , Union
10+ from typing import Final , Optional , Union
811
912import docker
1013
@@ -30,28 +33,27 @@ def get_docker_socket() -> str:
3033
3134 Using the docker api ensure we handle rootless docker properly
3235 """
33- if socket_path := environ .get ("TESTCONTAINERS_DOCKER_SOCKET_OVERRIDE" ):
36+ if socket_path := environ .get ("TESTCONTAINERS_DOCKER_SOCKET_OVERRIDE" , "" ):
3437 return socket_path
3538
36- client = docker .from_env ()
3739 try :
40+ client = docker .from_env ()
3841 socket_path = client .api .get_adapter (client .api .base_url ).socket_path
3942 # return the normalized path as string
4043 return str (Path (socket_path ).absolute ())
41- except AttributeError :
44+ except Exception :
4245 return "/var/run/docker.sock"
4346
4447
45- MAX_TRIES = int (environ .get ("TC_MAX_TRIES" , 120 ))
46- SLEEP_TIME = int (environ .get ("TC_POOLING_INTERVAL" , 1 ))
47- TIMEOUT = MAX_TRIES * SLEEP_TIME
48+ def get_bool_env (name : str ) -> bool :
49+ """
50+ Get environment variable named `name` and convert it to bool.
51+
52+ Defaults to False.
53+ """
54+ value = environ .get (name , "" )
55+ return value .lower () in ("yes" , "true" , "t" , "y" , "1" )
4856
49- RYUK_IMAGE : str = environ .get ("RYUK_CONTAINER_IMAGE" , "testcontainers/ryuk:0.8.1" )
50- RYUK_PRIVILEGED : bool = environ .get ("TESTCONTAINERS_RYUK_PRIVILEGED" , "false" ) == "true"
51- RYUK_DISABLED : bool = environ .get ("TESTCONTAINERS_RYUK_DISABLED" , "false" ) == "true"
52- RYUK_DOCKER_SOCKET : str = get_docker_socket ()
53- RYUK_RECONNECTION_TIMEOUT : str = environ .get ("RYUK_RECONNECTION_TIMEOUT" , "10s" )
54- TC_HOST_OVERRIDE : Optional [str ] = environ .get ("TC_HOST" , environ .get ("TESTCONTAINERS_HOST_OVERRIDE" ))
5557
5658TC_FILE = ".testcontainers.properties"
5759TC_GLOBAL = Path .home () / TC_FILE
@@ -94,16 +96,16 @@ def read_tc_properties() -> dict[str, str]:
9496
9597@dataclass
9698class TestcontainersConfiguration :
97- max_tries : int = MAX_TRIES
98- sleep_time : int = SLEEP_TIME
99- ryuk_image : str = RYUK_IMAGE
100- ryuk_privileged : bool = RYUK_PRIVILEGED
101- ryuk_disabled : bool = RYUK_DISABLED
102- ryuk_docker_socket : str = RYUK_DOCKER_SOCKET
103- ryuk_reconnection_timeout : str = RYUK_RECONNECTION_TIMEOUT
99+ max_tries : int = int ( environ . get ( "TC_MAX_TRIES" , "120" ))
100+ sleep_time : int = int ( environ . get ( "TC_POOLING_INTERVAL" , "1" ))
101+ ryuk_image : str = environ . get ( "RYUK_CONTAINER_IMAGE" , "testcontainers/ryuk:0.8.1" )
102+ ryuk_privileged : bool = get_bool_env ( "TESTCONTAINERS_RYUK_PRIVILEGED" )
103+ ryuk_disabled : bool = get_bool_env ( "TESTCONTAINERS_RYUK_DISABLED" )
104+ _ryuk_docker_socket : str = ""
105+ ryuk_reconnection_timeout : str = environ . get ( " RYUK_RECONNECTION_TIMEOUT" , "10s" )
104106 tc_properties : dict [str , str ] = field (default_factory = read_tc_properties )
105107 _docker_auth_config : Optional [str ] = field (default_factory = lambda : environ .get ("DOCKER_AUTH_CONFIG" ))
106- tc_host_override : Optional [str ] = TC_HOST_OVERRIDE
108+ tc_host_override : Optional [str ] = environ . get ( "TC_HOST" , environ . get ( "TESTCONTAINERS_HOST_OVERRIDE" ))
107109 connection_mode_override : Optional [ConnectionMode ] = field (default_factory = get_user_overwritten_connection_mode )
108110
109111 """
@@ -131,19 +133,54 @@ def tc_properties_get_tc_host(self) -> Union[str, None]:
131133 def timeout (self ) -> int :
132134 return self .max_tries * self .sleep_time
133135
136+ @property
137+ def ryuk_docker_socket (self ) -> str :
138+ if not self ._ryuk_docker_socket :
139+ self .ryuk_docker_socket = get_docker_socket ()
140+ return self ._ryuk_docker_socket
134141
135- testcontainers_config = TestcontainersConfiguration ()
142+ @ryuk_docker_socket .setter
143+ def ryuk_docker_socket (self , value : str ) -> None :
144+ self ._ryuk_docker_socket = value
145+
146+
147+ testcontainers_config : Final = TestcontainersConfiguration ()
136148
137149__all__ = [
138- # Legacy things that are deprecated:
139- "MAX_TRIES" ,
140- "RYUK_DISABLED" ,
141- "RYUK_DOCKER_SOCKET" ,
142- "RYUK_IMAGE" ,
143- "RYUK_PRIVILEGED" ,
144- "RYUK_RECONNECTION_TIMEOUT" ,
145- "SLEEP_TIME" ,
146- "TIMEOUT" ,
147150 # Public API of this module:
148151 "testcontainers_config" ,
149152]
153+
154+ _deprecated_attribute_mapping : Final [Mapping [str , str ]] = types .MappingProxyType (
155+ {
156+ "MAX_TRIES" : "max_tries" ,
157+ "RYUK_DISABLED" : "ryuk_disabled" ,
158+ "RYUK_DOCKER_SOCKET" : "ryuk_docker_socket" ,
159+ "RYUK_IMAGE" : "ryuk_image" ,
160+ "RYUK_PRIVILEGED" : "ryuk_privileged" ,
161+ "RYUK_RECONNECTION_TIMEOUT" : "ryuk_reconnection_timeout" ,
162+ "SLEEP_TIME" : "sleep_time" ,
163+ "TIMEOUT" : "timeout" ,
164+ }
165+ )
166+
167+
168+ def __dir__ () -> list [str ]:
169+ return __all__ + list (_deprecated_attribute_mapping .keys ())
170+
171+
172+ def __getattr__ (name : str ) -> object :
173+ """
174+ Allow getting deprecated legacy settings.
175+ """
176+ module = f"{ __name__ !r} "
177+
178+ if name in _deprecated_attribute_mapping :
179+ attrib = _deprecated_attribute_mapping [name ]
180+ warnings .warn (
181+ f"{ module } .{ name } is deprecated. Use { module } .testcontainers_config.{ attrib } instead." ,
182+ DeprecationWarning ,
183+ stacklevel = 2 ,
184+ )
185+ return getattr (testcontainers_config , attrib )
186+ raise AttributeError (f"module { module } has no attribute { name !r} " )
0 commit comments