11from __future__ import annotations
22
3- from typing import TYPE_CHECKING
3+ import os
4+ from typing import Iterable , TYPE_CHECKING
45
56import testcontainers .core .config
67import testcontainers .core .container
1112if TYPE_CHECKING :
1213 from pytest import ExitCode , Session , Parser , Metafunc
1314
15+ SECURITY_OPTION_ROOTLESS = "name=rootless"
16+ TESTCONTAINERS_DOCKER_SOCKET_OVERRIDE = "TESTCONTAINERS_DOCKER_SOCKET_OVERRIDE"
17+
1418SHUTDOWN_RYUK = False
1519
1620# NOTE: Configure Testcontainers through `testcontainers.core.config` and not through env variables.
2630testcontainers .core .config .testcontainers_config .ryuk_privileged = True
2731
2832
33+ # https://docs.pytest.org/en/latest/reference/reference.html#pytest.hookspec.pytest_addoption
2934def pytest_addoption (parser : Parser ) -> None :
3035 parser .addoption ("--image" , action = "append" , default = [],
3136 help = "Image to use, can be specified multiple times" )
3237
3338
39+ # https://docs.pytest.org/en/latest/reference/reference.html#pytest.hookspec.pytest_generate_tests
3440def pytest_generate_tests (metafunc : Metafunc ) -> None :
3541 if image .__name__ in metafunc .fixturenames :
3642 metafunc .parametrize (image .__name__ , metafunc .config .getoption ("--image" ))
@@ -43,17 +49,42 @@ def image(request):
4349 yield request .param
4450
4551
52+ # https://docs.pytest.org/en/latest/reference/reference.html#pytest.hookspec.pytest_sessionstart
4653def pytest_sessionstart (session : Session ) -> None :
4754 # first preflight check: ping the Docker API
4855 client = testcontainers .core .docker_client .DockerClient ()
4956 assert client .client .ping (), "Failed to connect to Docker"
5057
58+ # determine the local socket path
59+ # NOTE: this will not work for remote docker, but we will cross the bridge when we come to it
60+ socket_path = the_one (adapter .socket_path for adapter in client .client .api .adapters .values ())
61+
62+ # set that socket path for ryuk's use, unless user overrode that
63+ if TESTCONTAINERS_DOCKER_SOCKET_OVERRIDE not in os .environ :
64+ testcontainers .core .config .testcontainers_config .ryuk_docker_socket = socket_path
65+
5166 # second preflight check: start the Reaper container
52- assert testcontainers .core .container .Reaper .get_instance () is not None , "Failed to start Reaper container"
67+ if not testcontainers .core .config .testcontainers_config .ryuk_disabled :
68+ assert testcontainers .core .container .Reaper .get_instance () is not None , "Failed to start Reaper container"
5369
5470
5571# https://docs.pytest.org/en/latest/reference/reference.html#pytest.hookspec.pytest_sessionfinish
5672def pytest_sessionfinish (session : Session , exitstatus : int | ExitCode ) -> None :
5773 # resolves a shutdown resource leak warning that would be otherwise reported
5874 if SHUTDOWN_RYUK :
5975 testcontainers .core .container .Reaper .delete_instance ()
76+
77+
78+ # https://docs.python.org/3/library/functions.html#iter
79+ def the_one [T ](iterable : Iterable [T ]) -> T :
80+ """Checks that there is exactly one element in the iterable, and returns it."""
81+ it = iter (iterable )
82+ try :
83+ v = next (it )
84+ except StopIteration :
85+ raise ValueError ("No elements in iterable" )
86+ try :
87+ next (it )
88+ except StopIteration :
89+ return v
90+ raise ValueError ("More than one element in iterable" )
0 commit comments