|
26 | 26 | import multiprocessing |
27 | 27 | import sys |
28 | 28 |
|
| 29 | +import fasteners |
| 30 | + |
29 | 31 | multiprocessing.set_start_method("spawn", force=True) |
30 | 32 |
|
31 | 33 | from collections.abc import Iterator |
@@ -300,23 +302,38 @@ def with_test_cache(test_files_directory, request): |
300 | 302 | if tmp_cache.exists(): |
301 | 303 | shutil.rmtree(tmp_cache) |
302 | 304 |
|
303 | | -@pytest.fixture(scope="session", autouse=True) |
304 | | -def openml_docker_stack(): |
305 | | - subprocess.run(["docker", "compose", "up", "-d"], check=True) |
| 305 | +def _is_server_responding(): |
| 306 | + """Check if the Docker API is already listening.""" |
| 307 | + try: |
| 308 | + requests.get("http://localhost:9001/api/v2/", timeout=1) |
| 309 | + return True |
| 310 | + except (requests.exceptions.ConnectionError, requests.exceptions.Timeout): |
| 311 | + return False |
| 312 | + |
| 313 | +def _start_docker(): |
| 314 | + """Logic to spin up the containers and wait for initialization.""" |
| 315 | + subprocess.run(["docker", "compose", "up", "-d"], check=True, capture_output=True, text=True) |
306 | 316 | subprocess.run(["docker", "wait", "openml-test-setup-ci"], check=True) |
| 317 | + |
| 318 | +@pytest.fixture(scope="session", autouse=True) |
| 319 | +def openml_docker_stack(tmp_path_factory, worker_id): |
| 320 | + # For local development, single-process runs |
| 321 | + if worker_id == "master": |
| 322 | + _start_docker() |
| 323 | + yield |
| 324 | + subprocess.run(["docker", "compose", "down", "-v"], check=True) |
| 325 | + return |
| 326 | + |
| 327 | + # Case 2: Running in CI with multiple workers (xdist) |
| 328 | + root_tmp_dir = tmp_path_factory.getbasetemp().parent |
| 329 | + lock_file = root_tmp_dir / "docker_setup.lock" |
307 | 330 |
|
308 | | - timeout = 10 |
309 | | - start = time.time() |
310 | | - while time.time() - start < timeout: |
311 | | - try: |
312 | | - requests.get("http://localhost:9001/api/v2/") |
313 | | - break |
314 | | - except requests.exceptions.ConnectionError: |
315 | | - time.sleep(1) |
316 | | - |
| 331 | + lock = fasteners.InterProcessLock(str(lock_file)) |
| 332 | + with lock: |
| 333 | + if not _is_server_responding(): |
| 334 | + _start_docker() |
| 335 | + |
317 | 336 | yield |
318 | | - |
319 | | - subprocess.run(["docker", "compose", "down", "-v"], check=True) |
320 | 337 |
|
321 | 338 | @pytest.fixture |
322 | 339 | def static_cache_dir(): |
|
0 commit comments