44import subprocess
55import time
66import timeit
7+ from typing import Any , Dict , Iterable , Iterator , List , Tuple , Union
78
89import attr
9-
1010import pytest
11+ from _pytest .config import Config
12+ from _pytest .fixtures import FixtureRequest
1113
1214
1315@pytest .fixture
14- def container_scope_fixture (request ) :
16+ def container_scope_fixture (request : FixtureRequest ) -> Any :
1517 return request .config .getoption ("--container-scope" )
1618
17- def containers_scope (fixture_name , config ):
19+
20+ def containers_scope (fixture_name : str , config : Config ) -> Any : # pylint: disable=unused-argument
1821 return config .getoption ("--container-scope" , "session" )
1922
20- def execute (command , success_codes = (0 ,)):
23+
24+ def execute (command : str , success_codes : Iterable [int ] = (0 ,)) -> Union [bytes , Any ]:
2125 """Run a shell command."""
2226 try :
2327 output = subprocess .check_output (command , stderr = subprocess .STDOUT , shell = True )
@@ -29,14 +33,12 @@ def execute(command, success_codes=(0,)):
2933
3034 if status not in success_codes :
3135 raise Exception (
32- 'Command {} returned {}: """{}""".' .format (
33- command , status , output .decode ("utf-8" )
34- )
36+ 'Command {} returned {}: """{}""".' .format (command , status , output .decode ("utf-8" ))
3537 )
3638 return output
3739
3840
39- def get_docker_ip ():
41+ def get_docker_ip () -> Union [ str , Any ] :
4042 # When talking to the Docker daemon via a UNIX socket, route all TCP
4143 # traffic to docker containers via the TCP loopback interface.
4244 docker_host = os .environ .get ("DOCKER_HOST" , "" ).strip ()
@@ -50,19 +52,18 @@ def get_docker_ip():
5052
5153
5254@pytest .fixture (scope = containers_scope )
53- def docker_ip ():
55+ def docker_ip () -> Union [ str , Any ] :
5456 """Determine the IP address for TCP connections to Docker containers."""
5557
5658 return get_docker_ip ()
5759
5860
5961@attr .s (frozen = True )
6062class Services :
63+ _docker_compose : Any = attr .ib ()
64+ _services : Dict [Any , Dict [Any , Any ]] = attr .ib (init = False , default = attr .Factory (dict ))
6165
62- _docker_compose = attr .ib ()
63- _services = attr .ib (init = False , default = attr .Factory (dict ))
64-
65- def port_for (self , service , container_port ):
66+ def port_for (self , service : str , container_port : int ) -> int :
6667 """Return the "host" port for `service` and `container_port`.
6768
6869 E.g. If the service is defined like this:
@@ -78,16 +79,14 @@ def port_for(self, service, container_port):
7879 """
7980
8081 # Lookup in the cache.
81- cache = self ._services .get (service , {}).get (container_port , None )
82+ cache : int = self ._services .get (service , {}).get (container_port , None )
8283 if cache is not None :
8384 return cache
8485
8586 output = self ._docker_compose .execute ("port %s %d" % (service , container_port ))
8687 endpoint = output .strip ().decode ("utf-8" )
8788 if not endpoint :
88- raise ValueError (
89- 'Could not detect port for "%s:%d".' % (service , container_port )
90- )
89+ raise ValueError ('Could not detect port for "%s:%d".' % (service , container_port ))
9190
9291 # This handles messy output that might contain warnings or other text
9392 if len (endpoint .split ("\n " )) > 1 :
@@ -101,7 +100,13 @@ def port_for(self, service, container_port):
101100
102101 return match
103102
104- def wait_until_responsive (self , check , timeout , pause , clock = timeit .default_timer ):
103+ def wait_until_responsive (
104+ self ,
105+ check : Any ,
106+ timeout : float ,
107+ pause : float ,
108+ clock : Any = timeit .default_timer ,
109+ ) -> None :
105110 """Wait until a service is responsive."""
106111
107112 ref = clock ()
@@ -115,20 +120,19 @@ def wait_until_responsive(self, check, timeout, pause, clock=timeit.default_time
115120 raise Exception ("Timeout reached while waiting on service!" )
116121
117122
118- def str_to_list (arg ) :
123+ def str_to_list (arg : Union [ str , List [ Any ], Tuple [ Any ]]) -> Union [ List [ Any ], Tuple [ Any ]] :
119124 if isinstance (arg , (list , tuple )):
120125 return arg
121126 return [arg ]
122127
123128
124129@attr .s (frozen = True )
125130class DockerComposeExecutor :
131+ _compose_command : str = attr .ib ()
132+ _compose_files : Any = attr .ib (converter = str_to_list )
133+ _compose_project_name : str = attr .ib ()
126134
127- _compose_command = attr .ib ()
128- _compose_files = attr .ib (converter = str_to_list )
129- _compose_project_name = attr .ib ()
130-
131- def execute (self , subcommand ):
135+ def execute (self , subcommand : str ) -> Union [bytes , Any ]:
132136 command = self ._compose_command
133137 for compose_file in self ._compose_files :
134138 command += ' -f "{}"' .format (compose_file )
@@ -137,7 +141,7 @@ def execute(self, subcommand):
137141
138142
139143@pytest .fixture (scope = containers_scope )
140- def docker_compose_command ():
144+ def docker_compose_command () -> str :
141145 """Docker Compose command to use, it could be either `docker compose`
142146 for Docker Compose V2 or `docker-compose` for Docker Compose
143147 V1."""
@@ -146,40 +150,40 @@ def docker_compose_command():
146150
147151
148152@pytest .fixture (scope = containers_scope )
149- def docker_compose_file (pytestconfig ) :
153+ def docker_compose_file (pytestconfig : Any ) -> str :
150154 """Get an absolute path to the `docker-compose.yml` file. Override this
151155 fixture in your tests if you need a custom location."""
152156
153157 return os .path .join (str (pytestconfig .rootdir ), "tests" , "docker-compose.yml" )
154158
155159
156160@pytest .fixture (scope = containers_scope )
157- def docker_compose_project_name ():
161+ def docker_compose_project_name () -> str :
158162 """Generate a project name using the current process PID. Override this
159163 fixture in your tests if you need a particular project name."""
160164
161165 return "pytest{}" .format (os .getpid ())
162166
163167
164- def get_cleanup_command ():
168+ def get_cleanup_command () -> Union [ List [ str ], str ] :
165169 return ["down -v" ]
166170
167171
168172@pytest .fixture (scope = containers_scope )
169- def docker_cleanup ():
173+ def docker_cleanup () -> Union [ List [ str ], str ] :
170174 """Get the docker_compose command to be executed for test clean-up actions.
171175 Override this fixture in your tests if you need to change clean-up actions.
172176 Returning anything that would evaluate to False will skip this command."""
173177
174178 return get_cleanup_command ()
175179
176180
177- def get_setup_command ():
181+ def get_setup_command () -> Union [ List [ str ], str ] :
178182 return ["up --build -d" ]
179183
180184
181185@pytest .fixture (scope = containers_scope )
182- def docker_setup ():
186+ def docker_setup () -> Union [ List [ str ], str ] :
183187 """Get the docker_compose command to be executed for test setup actions.
184188 Override this fixture in your tests if you need to change setup actions.
185189 Returning anything that would evaluate to False will skip this command."""
@@ -189,12 +193,12 @@ def docker_setup():
189193
190194@contextlib .contextmanager
191195def get_docker_services (
192- docker_compose_command ,
193- docker_compose_file ,
194- docker_compose_project_name ,
195- docker_setup ,
196- docker_cleanup ,
197- ):
196+ docker_compose_command : str ,
197+ docker_compose_file : str ,
198+ docker_compose_project_name : str ,
199+ docker_setup : Union [ List [ str ], str ] ,
200+ docker_cleanup : Union [ List [ str ], str ] ,
201+ ) -> Iterator [ Services ] :
198202 docker_compose = DockerComposeExecutor (
199203 docker_compose_command , docker_compose_file , docker_compose_project_name
200204 )
@@ -222,12 +226,12 @@ def get_docker_services(
222226
223227@pytest .fixture (scope = containers_scope )
224228def docker_services (
225- docker_compose_command ,
226- docker_compose_file ,
227- docker_compose_project_name ,
228- docker_setup ,
229- docker_cleanup ,
230- ):
229+ docker_compose_command : str ,
230+ docker_compose_file : str ,
231+ docker_compose_project_name : str ,
232+ docker_setup : str ,
233+ docker_cleanup : str ,
234+ ) -> Iterator [ Services ] :
231235 """Start all services from a docker compose file (`docker-compose up`).
232236 After test are finished, shutdown all services (`docker-compose down`)."""
233237
0 commit comments