|
1 | | -# SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. |
| 1 | +# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. |
2 | 2 | # SPDX-License-Identifier: Apache-2.0 |
3 | 3 |
|
4 | | -# This helper is factored out so spawned child processes only import this |
5 | | -# lightweight module. That avoids re-importing the test module (and |
6 | | -# repeating its potentially expensive setup) in every child process. |
| 4 | +from __future__ import annotations |
7 | 5 |
|
8 | | -import json |
9 | | -import os |
| 6 | +import subprocess |
10 | 7 | import sys |
11 | | -import traceback |
| 8 | +import tempfile |
| 9 | +from pathlib import Path |
12 | 10 |
|
| 11 | +from cuda.pathfinder._testing.load_nvidia_dynamic_lib_subprocess import DYNAMIC_LIB_NOT_FOUND_MARKER |
13 | 12 |
|
14 | | -def build_child_process_failed_for_libname_message(libname, result): |
| 13 | +LOAD_NVIDIA_DYNAMIC_LIB_SUBPROCESS_MODULE = "cuda.pathfinder._testing.load_nvidia_dynamic_lib_subprocess" |
| 14 | +# Launch the child from a neutral directory so `python -m cuda.pathfinder...` |
| 15 | +# resolves the installed package instead of the source checkout. In CI the |
| 16 | +# checkout does not contain the generated `_version.py` file. |
| 17 | +LOAD_NVIDIA_DYNAMIC_LIB_SUBPROCESS_CWD = Path(tempfile.gettempdir()) |
| 18 | +PROCESS_TIMED_OUT = -9 |
| 19 | + |
| 20 | + |
| 21 | +def build_child_process_failed_for_libname_message(libname: str, result: subprocess.CompletedProcess[str]) -> str: |
15 | 22 | return ( |
16 | 23 | f"Child process failed for {libname=!r} with exit code {result.returncode}\n" |
17 | 24 | f"--- stdout-from-child-process ---\n{result.stdout}<end-of-stdout-from-child-process>\n" |
18 | 25 | f"--- stderr-from-child-process ---\n{result.stderr}<end-of-stderr-from-child-process>\n" |
19 | 26 | ) |
20 | 27 |
|
21 | 28 |
|
22 | | -def validate_abs_path(abs_path): |
23 | | - assert abs_path, f"empty path: {abs_path=!r}" |
24 | | - assert os.path.isabs(abs_path), f"not absolute: {abs_path=!r}" |
25 | | - assert os.path.isfile(abs_path), f"not a file: {abs_path=!r}" |
| 29 | +def child_process_reported_dynamic_lib_not_found(result: subprocess.CompletedProcess[str]) -> bool: |
| 30 | + return result.stdout.startswith(DYNAMIC_LIB_NOT_FOUND_MARKER) |
26 | 31 |
|
27 | 32 |
|
28 | | -def child_process_func(libname): |
29 | | - from cuda.pathfinder import DynamicLibNotFoundError, load_nvidia_dynamic_lib |
30 | | - from cuda.pathfinder._dynamic_libs.load_nvidia_dynamic_lib import _load_lib_no_cache |
31 | | - from cuda.pathfinder._dynamic_libs.supported_nvidia_libs import ( |
32 | | - SUPPORTED_LINUX_SONAMES, |
33 | | - SUPPORTED_WINDOWS_DLLS, |
34 | | - ) |
35 | | - from cuda.pathfinder._utils.platform_aware import IS_WINDOWS |
36 | | - |
| 33 | +def run_load_nvidia_dynamic_lib_in_subprocess( |
| 34 | + libname: str, |
| 35 | + *, |
| 36 | + timeout: float, |
| 37 | +) -> subprocess.CompletedProcess[str]: |
| 38 | + command = [sys.executable, "-m", LOAD_NVIDIA_DYNAMIC_LIB_SUBPROCESS_MODULE, libname] |
37 | 39 | try: |
38 | | - loaded_dl_fresh = load_nvidia_dynamic_lib(libname) |
39 | | - except DynamicLibNotFoundError: |
40 | | - print("CHILD_LOAD_NVIDIA_DYNAMIC_LIB_HELPER_DYNAMIC_LIB_NOT_FOUND_ERROR:") |
41 | | - traceback.print_exc(file=sys.stdout) |
42 | | - return |
43 | | - if loaded_dl_fresh.was_already_loaded_from_elsewhere: |
44 | | - raise RuntimeError("loaded_dl_fresh.was_already_loaded_from_elsewhere") |
45 | | - validate_abs_path(loaded_dl_fresh.abs_path) |
46 | | - assert loaded_dl_fresh.found_via is not None |
47 | | - |
48 | | - loaded_dl_from_cache = load_nvidia_dynamic_lib(libname) |
49 | | - if loaded_dl_from_cache is not loaded_dl_fresh: |
50 | | - raise RuntimeError("loaded_dl_from_cache is not loaded_dl_fresh") |
51 | | - |
52 | | - loaded_dl_no_cache = _load_lib_no_cache(libname) |
53 | | - # check_if_already_loaded_from_elsewhere relies on these: |
54 | | - supported_libs = SUPPORTED_WINDOWS_DLLS if IS_WINDOWS else SUPPORTED_LINUX_SONAMES |
55 | | - if not loaded_dl_no_cache.was_already_loaded_from_elsewhere and libname in supported_libs: |
56 | | - raise RuntimeError("not loaded_dl_no_cache.was_already_loaded_from_elsewhere") |
57 | | - if not os.path.samefile(loaded_dl_no_cache.abs_path, loaded_dl_fresh.abs_path): |
58 | | - raise RuntimeError(f"not os.path.samefile({loaded_dl_no_cache.abs_path=!r}, {loaded_dl_fresh.abs_path=!r})") |
59 | | - validate_abs_path(loaded_dl_no_cache.abs_path) |
60 | | - |
61 | | - print(json.dumps(loaded_dl_fresh.abs_path)) |
| 40 | + return subprocess.run( # noqa: S603 - trusted argv: current interpreter + internal test helper module |
| 41 | + command, |
| 42 | + capture_output=True, |
| 43 | + text=True, |
| 44 | + timeout=timeout, |
| 45 | + check=False, |
| 46 | + cwd=LOAD_NVIDIA_DYNAMIC_LIB_SUBPROCESS_CWD, |
| 47 | + ) |
| 48 | + except subprocess.TimeoutExpired: |
| 49 | + return subprocess.CompletedProcess( |
| 50 | + args=command, |
| 51 | + returncode=PROCESS_TIMED_OUT, |
| 52 | + stdout="", |
| 53 | + stderr=f"Process timed out after {timeout} seconds and was terminated.", |
| 54 | + ) |
0 commit comments