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 , cast
10+ from typing import Final , Optional , Union , cast
811
912import docker
1013
@@ -30,29 +33,28 @@ 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 socket_path = cast ("str" , socket_path )
4043 # return the normalized path as string
4144 return str (Path (socket_path ).absolute ())
42- except AttributeError :
45+ except Exception :
4346 return "/var/run/docker.sock"
4447
4548
46- MAX_TRIES = int (environ .get ("TC_MAX_TRIES" , 120 ))
47- SLEEP_TIME = int (environ .get ("TC_POOLING_INTERVAL" , 1 ))
48- TIMEOUT = MAX_TRIES * SLEEP_TIME
49+ def get_bool_env (name : str ) -> bool :
50+ """
51+ Get environment variable named `name` and convert it to bool.
52+
53+ Defaults to False.
54+ """
55+ value = environ .get (name , "" )
56+ return value .lower () in ("yes" , "true" , "t" , "y" , "1" )
4957
50- RYUK_IMAGE : str = environ .get ("RYUK_CONTAINER_IMAGE" , "testcontainers/ryuk:0.8.1" )
51- RYUK_PRIVILEGED : bool = environ .get ("TESTCONTAINERS_RYUK_PRIVILEGED" , "false" ) == "true"
52- RYUK_DISABLED : bool = environ .get ("TESTCONTAINERS_RYUK_DISABLED" , "false" ) == "true"
53- RYUK_DOCKER_SOCKET : str = get_docker_socket ()
54- RYUK_RECONNECTION_TIMEOUT : str = environ .get ("RYUK_RECONNECTION_TIMEOUT" , "10s" )
55- TC_HOST_OVERRIDE : Optional [str ] = environ .get ("TC_HOST" , environ .get ("TESTCONTAINERS_HOST_OVERRIDE" ))
5658
5759TC_FILE = ".testcontainers.properties"
5860TC_GLOBAL = Path .home () / TC_FILE
@@ -95,16 +97,16 @@ def read_tc_properties() -> dict[str, str]:
9597
9698@dataclass
9799class TestcontainersConfiguration :
98- max_tries : int = MAX_TRIES
99- sleep_time : int = SLEEP_TIME
100- ryuk_image : str = RYUK_IMAGE
101- ryuk_privileged : bool = RYUK_PRIVILEGED
102- ryuk_disabled : bool = RYUK_DISABLED
103- ryuk_docker_socket : str = RYUK_DOCKER_SOCKET
104- ryuk_reconnection_timeout : str = RYUK_RECONNECTION_TIMEOUT
100+ max_tries : int = int ( environ . get ( "TC_MAX_TRIES" , "120" ))
101+ sleep_time : int = int ( environ . get ( "TC_POOLING_INTERVAL" , "1" ))
102+ ryuk_image : str = environ . get ( "RYUK_CONTAINER_IMAGE" , "testcontainers/ryuk:0.8.1" )
103+ ryuk_privileged : bool = get_bool_env ( "TESTCONTAINERS_RYUK_PRIVILEGED" )
104+ ryuk_disabled : bool = get_bool_env ( "TESTCONTAINERS_RYUK_DISABLED" )
105+ _ryuk_docker_socket : str = ""
106+ ryuk_reconnection_timeout : str = environ . get ( " RYUK_RECONNECTION_TIMEOUT" , "10s" )
105107 tc_properties : dict [str , str ] = field (default_factory = read_tc_properties )
106108 _docker_auth_config : Optional [str ] = field (default_factory = lambda : environ .get ("DOCKER_AUTH_CONFIG" ))
107- tc_host_override : Optional [str ] = TC_HOST_OVERRIDE
109+ tc_host_override : Optional [str ] = environ . get ( "TC_HOST" , environ . get ( "TESTCONTAINERS_HOST_OVERRIDE" ))
108110 connection_mode_override : Optional [ConnectionMode ] = field (default_factory = get_user_overwritten_connection_mode )
109111
110112 """
@@ -132,20 +134,55 @@ def tc_properties_get_tc_host(self) -> Union[str, None]:
132134 def timeout (self ) -> int :
133135 return self .max_tries * self .sleep_time
134136
137+ @property
138+ def ryuk_docker_socket (self ) -> str :
139+ if not self ._ryuk_docker_socket :
140+ self .ryuk_docker_socket = get_docker_socket ()
141+ return self ._ryuk_docker_socket
135142
136- testcontainers_config = TestcontainersConfiguration ()
143+ @ryuk_docker_socket .setter
144+ def ryuk_docker_socket (self , value : str ) -> None :
145+ self ._ryuk_docker_socket = value
146+
147+
148+ testcontainers_config : Final = TestcontainersConfiguration ()
137149
138150__all__ = [
139- # Legacy things that are deprecated:
140- "MAX_TRIES" ,
141- "RYUK_DISABLED" ,
142- "RYUK_DOCKER_SOCKET" ,
143- "RYUK_IMAGE" ,
144- "RYUK_PRIVILEGED" ,
145- "RYUK_RECONNECTION_TIMEOUT" ,
146- "SLEEP_TIME" ,
147- "TIMEOUT" ,
148151 # Public API of this module:
149152 "ConnectionMode" ,
150153 "testcontainers_config" ,
151154]
155+
156+ _deprecated_attribute_mapping : Final [Mapping [str , str ]] = types .MappingProxyType (
157+ {
158+ "MAX_TRIES" : "max_tries" ,
159+ "RYUK_DISABLED" : "ryuk_disabled" ,
160+ "RYUK_DOCKER_SOCKET" : "ryuk_docker_socket" ,
161+ "RYUK_IMAGE" : "ryuk_image" ,
162+ "RYUK_PRIVILEGED" : "ryuk_privileged" ,
163+ "RYUK_RECONNECTION_TIMEOUT" : "ryuk_reconnection_timeout" ,
164+ "SLEEP_TIME" : "sleep_time" ,
165+ "TIMEOUT" : "timeout" ,
166+ }
167+ )
168+
169+
170+ def __dir__ () -> list [str ]:
171+ return __all__ + list (_deprecated_attribute_mapping .keys ())
172+
173+
174+ def __getattr__ (name : str ) -> object :
175+ """
176+ Allow getting deprecated legacy settings.
177+ """
178+ module = f"{ __name__ !r} "
179+
180+ if name in _deprecated_attribute_mapping :
181+ attrib = _deprecated_attribute_mapping [name ]
182+ warnings .warn (
183+ f"{ module } .{ name } is deprecated. Use { module } .testcontainers_config.{ attrib } instead." ,
184+ DeprecationWarning ,
185+ stacklevel = 2 ,
186+ )
187+ return getattr (testcontainers_config , attrib )
188+ raise AttributeError (f"module { module } has no attribute { name !r} " )
0 commit comments