From 5174ea2db6327718f73dbffe9a081a1d00a4c921 Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Sat, 20 Sep 2025 11:48:35 -0700 Subject: [PATCH 01/17] Bump pathfinder version to 1.2.4a0 --- cuda_pathfinder/cuda/pathfinder/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cuda_pathfinder/cuda/pathfinder/_version.py b/cuda_pathfinder/cuda/pathfinder/_version.py index 001da9389..d0695c7c8 100644 --- a/cuda_pathfinder/cuda/pathfinder/_version.py +++ b/cuda_pathfinder/cuda/pathfinder/_version.py @@ -1,4 +1,4 @@ # SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. # SPDX-License-Identifier: Apache-2.0 -__version__ = "1.2.3" +__version__ = "1.2.4a0" From 8da27a7f9ed2de5ff9a3c8a521af40c331f0ee94 Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Sat, 20 Sep 2025 11:37:05 -0700 Subject: [PATCH 02/17] Update load_nvidia_dynamic_lib docstring first (to define what we're shooting for; no actual changes). --- .../_dynamic_libs/load_nvidia_dynamic_lib.py | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/cuda_pathfinder/cuda/pathfinder/_dynamic_libs/load_nvidia_dynamic_lib.py b/cuda_pathfinder/cuda/pathfinder/_dynamic_libs/load_nvidia_dynamic_lib.py index 3160333aa..561c608ed 100644 --- a/cuda_pathfinder/cuda/pathfinder/_dynamic_libs/load_nvidia_dynamic_lib.py +++ b/cuda_pathfinder/cuda/pathfinder/_dynamic_libs/load_nvidia_dynamic_lib.py @@ -77,7 +77,13 @@ def load_nvidia_dynamic_lib(libname: str) -> LoadedDL: - Scan installed distributions (``site-packages``) to find libraries shipped in NVIDIA wheels. - 2. **OS default mechanisms / Conda environments** + 2. **Conda environment** + + - Conda installations are discovered via ``CONDA_PREFIX``, which is + predefined in activated conda environments (see + https://docs.conda.io/projects/conda-build/en/stable/user-guide/environment-variables.html). + + 3. **OS default mechanisms** - Fall back to the native loader: @@ -85,14 +91,6 @@ def load_nvidia_dynamic_lib(libname: str) -> LoadedDL: - Windows: ``LoadLibraryW()`` - - Conda installations are commonly discovered via: - - - Linux: ``$ORIGIN/../lib`` in the ``RPATH`` of the ``python`` binary - (note: this can take precedence over ``LD_LIBRARY_PATH`` and - ``/etc/ld.so.conf.d/``). - - - Windows: ``%CONDA_PREFIX%\\Library\\bin`` on the system ``PATH``. - - CUDA Toolkit (CTK) system installs with system config updates are often discovered via: @@ -101,7 +99,7 @@ def load_nvidia_dynamic_lib(libname: str) -> LoadedDL: - Windows: ``C:\\Program Files\\NVIDIA GPU Computing Toolkit\\CUDA\\vX.Y\\bin`` on the system ``PATH``. - 3. **Environment variables** + 4. **Environment variables** - If set, use ``CUDA_HOME`` or ``CUDA_PATH`` (in that order). From 37cb325b96fdb47c5922666622dd5600ef6c3796 Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Sat, 20 Sep 2025 12:48:52 -0700 Subject: [PATCH 03/17] =?UTF-8?q?Rename=20.retry=5Fwith=5Fcuda=5Fhome=5Fpr?= =?UTF-8?q?iority=5Flast()=20=E2=86=92=20.try=5Fwith=5Fcuda=5Fhome()?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cuda/pathfinder/_dynamic_libs/find_nvidia_dynamic_lib.py | 2 +- .../cuda/pathfinder/_dynamic_libs/load_nvidia_dynamic_lib.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cuda_pathfinder/cuda/pathfinder/_dynamic_libs/find_nvidia_dynamic_lib.py b/cuda_pathfinder/cuda/pathfinder/_dynamic_libs/find_nvidia_dynamic_lib.py index d9567207e..bb072d335 100644 --- a/cuda_pathfinder/cuda/pathfinder/_dynamic_libs/find_nvidia_dynamic_lib.py +++ b/cuda_pathfinder/cuda/pathfinder/_dynamic_libs/find_nvidia_dynamic_lib.py @@ -167,7 +167,7 @@ def __init__(self, libname: str): self.attachments, ) - def retry_with_cuda_home_priority_last(self) -> None: + def try_with_cuda_home(self) -> None: cuda_home_lib_dir = _find_lib_dir_using_cuda_home(self.libname) if cuda_home_lib_dir is not None: if IS_WINDOWS: diff --git a/cuda_pathfinder/cuda/pathfinder/_dynamic_libs/load_nvidia_dynamic_lib.py b/cuda_pathfinder/cuda/pathfinder/_dynamic_libs/load_nvidia_dynamic_lib.py index 561c608ed..465e43356 100644 --- a/cuda_pathfinder/cuda/pathfinder/_dynamic_libs/load_nvidia_dynamic_lib.py +++ b/cuda_pathfinder/cuda/pathfinder/_dynamic_libs/load_nvidia_dynamic_lib.py @@ -44,7 +44,7 @@ def _load_lib_no_cache(libname: str) -> LoadedDL: loaded = load_with_system_search(libname) if loaded is not None: return loaded - found.retry_with_cuda_home_priority_last() + found.try_with_cuda_home() found.raise_if_abs_path_is_None() assert found.abs_path is not None # for mypy From 3e5aa1dfe367d176c56a3a1cbb51bc87f6f3c0a3 Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Sat, 20 Sep 2025 12:08:08 -0700 Subject: [PATCH 04/17] Add in ._try_with_conda_prefix() --- .../_dynamic_libs/find_nvidia_dynamic_lib.py | 71 ++++++++++++------- 1 file changed, 47 insertions(+), 24 deletions(-) diff --git a/cuda_pathfinder/cuda/pathfinder/_dynamic_libs/find_nvidia_dynamic_lib.py b/cuda_pathfinder/cuda/pathfinder/_dynamic_libs/find_nvidia_dynamic_lib.py index bb072d335..ccf1438ab 100644 --- a/cuda_pathfinder/cuda/pathfinder/_dynamic_libs/find_nvidia_dynamic_lib.py +++ b/cuda_pathfinder/cuda/pathfinder/_dynamic_libs/find_nvidia_dynamic_lib.py @@ -80,10 +80,7 @@ def _find_dll_using_nvidia_bin_dirs( return None -def _find_lib_dir_using_cuda_home(libname: str) -> Optional[str]: - cuda_home = get_cuda_home_or_path() - if cuda_home is None: - return None +def _find_lib_dir_using_anchor_point(libname: str, anchor_point: str) -> Optional[str]: subdirs_list: tuple[tuple[str, ...], ...] if IS_WINDOWS: if libname == "nvvm": # noqa: SIM108 @@ -106,11 +103,25 @@ def _find_lib_dir_using_cuda_home(libname: str) -> Optional[str]: ) for sub_dirs in subdirs_list: dirname: str # work around bug in mypy - for dirname in find_sub_dirs((cuda_home,), sub_dirs): + for dirname in find_sub_dirs((anchor_point,), sub_dirs): return dirname return None +def _find_lib_dir_using_cuda_home(libname: str) -> Optional[str]: + cuda_home = get_cuda_home_or_path() + if cuda_home is None: + return None + return _find_lib_dir_using_anchor_point(libname, cuda_home) + + +def _find_lib_dir_using_conda_prefix(libname: str) -> Optional[str]: + conda_prefix = os.getenv("CONDA_PREFIX") + if not conda_prefix: + return None + return _find_lib_dir_using_anchor_point(libname, conda_prefix) + + def _find_so_using_lib_dir( lib_dir: str, so_basename: str, error_messages: list[str], attachments: list[str] ) -> Optional[str]: @@ -146,44 +157,56 @@ def __init__(self, libname: str): self.libname = libname self.error_messages: list[str] = [] self.attachments: list[str] = [] - self.abs_path = None + self.abs_path: Optional[str] = None + + self._try_site_packages() + self._try_with_conda_prefix() + def _try_site_packages(self) -> None: if IS_WINDOWS: - self.lib_searched_for = f"{libname}*.dll" + self.lib_searched_for = f"{self.libname}*.dll" if self.abs_path is None: self.abs_path = _find_dll_using_nvidia_bin_dirs( - libname, + self.libname, self.lib_searched_for, self.error_messages, self.attachments, ) else: - self.lib_searched_for = f"lib{libname}.so" + self.lib_searched_for = f"lib{self.libname}.so" if self.abs_path is None: self.abs_path = _find_so_using_nvidia_lib_dirs( - libname, + self.libname, self.lib_searched_for, self.error_messages, self.attachments, ) + def _try_with_conda_prefix(self) -> None: + conda_lib_dir = _find_lib_dir_using_conda_prefix(self.libname) + if conda_lib_dir is not None: + self._find_using_lib_dir(conda_lib_dir) + def try_with_cuda_home(self) -> None: cuda_home_lib_dir = _find_lib_dir_using_cuda_home(self.libname) if cuda_home_lib_dir is not None: - if IS_WINDOWS: - self.abs_path = _find_dll_using_lib_dir( - cuda_home_lib_dir, - self.libname, - self.error_messages, - self.attachments, - ) - else: - self.abs_path = _find_so_using_lib_dir( - cuda_home_lib_dir, - self.lib_searched_for, - self.error_messages, - self.attachments, - ) + self._find_using_lib_dir(cuda_home_lib_dir) + + def _find_using_lib_dir(self, lib_dir: str) -> None: + if IS_WINDOWS: + self.abs_path = _find_dll_using_lib_dir( + lib_dir, + self.libname, + self.error_messages, + self.attachments, + ) + else: + self.abs_path = _find_so_using_lib_dir( + lib_dir, + self.lib_searched_for, + self.error_messages, + self.attachments, + ) def raise_if_abs_path_is_None(self) -> str: # noqa: N802 if self.abs_path: From 5b3c5ebfa4ef20839d9425d748620f6e9394c01d Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Sun, 21 Sep 2025 17:31:53 -0700 Subject: [PATCH 05/17] Fix anchor_point if IS_WINDOWS --- .../cuda/pathfinder/_dynamic_libs/find_nvidia_dynamic_lib.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cuda_pathfinder/cuda/pathfinder/_dynamic_libs/find_nvidia_dynamic_lib.py b/cuda_pathfinder/cuda/pathfinder/_dynamic_libs/find_nvidia_dynamic_lib.py index ccf1438ab..91d32d421 100644 --- a/cuda_pathfinder/cuda/pathfinder/_dynamic_libs/find_nvidia_dynamic_lib.py +++ b/cuda_pathfinder/cuda/pathfinder/_dynamic_libs/find_nvidia_dynamic_lib.py @@ -119,7 +119,9 @@ def _find_lib_dir_using_conda_prefix(libname: str) -> Optional[str]: conda_prefix = os.getenv("CONDA_PREFIX") if not conda_prefix: return None - return _find_lib_dir_using_anchor_point(libname, conda_prefix) + return _find_lib_dir_using_anchor_point( + libname, os.path.join(conda_prefix, "Library") if IS_WINDOWS else conda_prefix + ) def _find_so_using_lib_dir( From 233ad085ce7674af6d4b2315629d9fc7b16c6c9b Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Sun, 21 Sep 2025 20:46:19 -0700 Subject: [PATCH 06/17] Untangle CTK/Conda subdirs_list --- .../_dynamic_libs/find_nvidia_dynamic_lib.py | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/cuda_pathfinder/cuda/pathfinder/_dynamic_libs/find_nvidia_dynamic_lib.py b/cuda_pathfinder/cuda/pathfinder/_dynamic_libs/find_nvidia_dynamic_lib.py index 91d32d421..bb6763bca 100644 --- a/cuda_pathfinder/cuda/pathfinder/_dynamic_libs/find_nvidia_dynamic_lib.py +++ b/cuda_pathfinder/cuda/pathfinder/_dynamic_libs/find_nvidia_dynamic_lib.py @@ -80,7 +80,7 @@ def _find_dll_using_nvidia_bin_dirs( return None -def _find_lib_dir_using_anchor_point(libname: str, anchor_point: str) -> Optional[str]: +def _find_lib_dir_using_anchor_point(libname: str, anchor_point: str, linux_lib_dir: str) -> Optional[str]: subdirs_list: tuple[tuple[str, ...], ...] if IS_WINDOWS: if libname == "nvvm": # noqa: SIM108 @@ -97,10 +97,7 @@ def _find_lib_dir_using_anchor_point(libname: str, anchor_point: str) -> Optiona if libname == "nvvm": # noqa: SIM108 subdirs_list = (("nvvm", "lib64"),) else: - subdirs_list = ( - ("lib64",), # CTK - ("lib",), # Conda - ) + subdirs_list = ((linux_lib_dir,),) for sub_dirs in subdirs_list: dirname: str # work around bug in mypy for dirname in find_sub_dirs((anchor_point,), sub_dirs): @@ -112,7 +109,7 @@ def _find_lib_dir_using_cuda_home(libname: str) -> Optional[str]: cuda_home = get_cuda_home_or_path() if cuda_home is None: return None - return _find_lib_dir_using_anchor_point(libname, cuda_home) + return _find_lib_dir_using_anchor_point(libname, anchor_point=cuda_home, linux_lib_dir="lib64") def _find_lib_dir_using_conda_prefix(libname: str) -> Optional[str]: @@ -120,7 +117,7 @@ def _find_lib_dir_using_conda_prefix(libname: str) -> Optional[str]: if not conda_prefix: return None return _find_lib_dir_using_anchor_point( - libname, os.path.join(conda_prefix, "Library") if IS_WINDOWS else conda_prefix + libname, anchor_point=os.path.join(conda_prefix, "Library") if IS_WINDOWS else conda_prefix, linux_lib_dir="lib" ) From 2642d35dd88af61370c94d5807e04793cdb07a91 Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Sun, 21 Sep 2025 21:04:13 -0700 Subject: [PATCH 07/17] Bug fix (in test code only): logical or/and mixup that leads to silent failures --- cuda_pathfinder/tests/test_load_nvidia_dynamic_lib.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cuda_pathfinder/tests/test_load_nvidia_dynamic_lib.py b/cuda_pathfinder/tests/test_load_nvidia_dynamic_lib.py index 5f35d996d..ce08f29d3 100644 --- a/cuda_pathfinder/tests/test_load_nvidia_dynamic_lib.py +++ b/cuda_pathfinder/tests/test_load_nvidia_dynamic_lib.py @@ -100,7 +100,7 @@ def test_load_nvidia_dynamic_lib(info_summary_append, libname): result = spawned_process_runner.run_in_spawned_child_process(child_process_func, args=(libname,), timeout=timeout) if result.returncode == 0: info_summary_append(f"abs_path={result.stdout.rstrip()}") - elif STRICTNESS == "see_what_works" or "DynamicLibNotFoundError: Failure finding " in result.stderr: + elif STRICTNESS == "see_what_works" and "DynamicLibNotFoundError: Failure finding " in result.stderr: info_summary_append(f"Not found: {libname=!r}") else: raise RuntimeError(build_child_process_failed_for_libname_message(libname, result)) From edff6ecb971c33050e7e2d9816d2385763ca79df Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Sun, 21 Sep 2025 22:02:50 -0700 Subject: [PATCH 08/17] Make interaction between test_load_nvidia_dynamic_lib.py and child_load_nvidia_dynamic_lib_helper.py more robust to make it less likely that bugs like 2642d35dd88af61370c94d5807e04793cdb07a91 slip in. --- .../child_load_nvidia_dynamic_lib_helper.py | 13 ++++++++++--- .../tests/test_load_nvidia_dynamic_lib.py | 18 ++++++++++++++---- 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/cuda_pathfinder/tests/child_load_nvidia_dynamic_lib_helper.py b/cuda_pathfinder/tests/child_load_nvidia_dynamic_lib_helper.py index 4ca905989..fceaa33be 100644 --- a/cuda_pathfinder/tests/child_load_nvidia_dynamic_lib_helper.py +++ b/cuda_pathfinder/tests/child_load_nvidia_dynamic_lib_helper.py @@ -5,8 +5,10 @@ # lightweight module. That avoids re-importing the test module (and # repeating its potentially expensive setup) in every child process. +import json import os import sys +import traceback def build_child_process_failed_for_libname_message(libname, result): @@ -24,7 +26,7 @@ def validate_abs_path(abs_path): def child_process_func(libname): - from cuda.pathfinder import load_nvidia_dynamic_lib + from cuda.pathfinder import DynamicLibNotFoundError, load_nvidia_dynamic_lib from cuda.pathfinder._dynamic_libs.load_nvidia_dynamic_lib import _load_lib_no_cache from cuda.pathfinder._dynamic_libs.supported_nvidia_libs import ( IS_WINDOWS, @@ -32,7 +34,12 @@ def child_process_func(libname): SUPPORTED_WINDOWS_DLLS, ) - loaded_dl_fresh = load_nvidia_dynamic_lib(libname) + try: + loaded_dl_fresh = load_nvidia_dynamic_lib(libname) + except DynamicLibNotFoundError: + sys.stdout.write("CHILD_LOAD_NVIDIA_DYNAMIC_LIB_HELPER_DYNAMIC_LIB_NOT_FOUND_ERROR:\n") + traceback.print_exc(file=sys.stdout) + return if loaded_dl_fresh.was_already_loaded_from_elsewhere: raise RuntimeError("loaded_dl_fresh.was_already_loaded_from_elsewhere") validate_abs_path(loaded_dl_fresh.abs_path) @@ -50,4 +57,4 @@ def child_process_func(libname): raise RuntimeError(f"not os.path.samefile({loaded_dl_no_cache.abs_path=!r}, {loaded_dl_fresh.abs_path=!r})") validate_abs_path(loaded_dl_no_cache.abs_path) - sys.stdout.write(f"{loaded_dl_fresh.abs_path!r}\n") + sys.stdout.write(json.dumps(loaded_dl_fresh.abs_path) + "\n") diff --git a/cuda_pathfinder/tests/test_load_nvidia_dynamic_lib.py b/cuda_pathfinder/tests/test_load_nvidia_dynamic_lib.py index ce08f29d3..ed735aaa2 100644 --- a/cuda_pathfinder/tests/test_load_nvidia_dynamic_lib.py +++ b/cuda_pathfinder/tests/test_load_nvidia_dynamic_lib.py @@ -2,6 +2,7 @@ # SPDX-License-Identifier: Apache-2.0 import functools +import json import os from unittest.mock import patch @@ -98,9 +99,18 @@ def test_load_nvidia_dynamic_lib(info_summary_append, libname): # interfere across test cases and lead to nondeterministic or platform-specific failures. timeout = 120 if supported_nvidia_libs.IS_WINDOWS else 30 result = spawned_process_runner.run_in_spawned_child_process(child_process_func, args=(libname,), timeout=timeout) - if result.returncode == 0: - info_summary_append(f"abs_path={result.stdout.rstrip()}") - elif STRICTNESS == "see_what_works" and "DynamicLibNotFoundError: Failure finding " in result.stderr: + + def raise_child_process_failed(): + raise RuntimeError(build_child_process_failed_for_libname_message(libname, result)) + + if result.returncode != 0: + raise_child_process_failed() + assert not result.stderr + if result.stdout.startswith("CHILD_LOAD_NVIDIA_DYNAMIC_LIB_HELPER_DYNAMIC_LIB_NOT_FOUND_ERROR:"): + if STRICTNESS == "all_must_work": + raise_child_process_failed() info_summary_append(f"Not found: {libname=!r}") else: - raise RuntimeError(build_child_process_failed_for_libname_message(libname, result)) + abs_path = json.loads(result.stdout.rstrip()) + info_summary_append(f"{abs_path=}") + assert os.path.isfile(abs_path) # double-check the abs_path From 5ba032ce1b265ff8356cb72fc49c199bff7cf445 Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Sun, 21 Sep 2025 22:30:56 -0700 Subject: [PATCH 09/17] Show abs_path suitable for copy-pasting in shell --- cuda_pathfinder/tests/test_load_nvidia_dynamic_lib.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/cuda_pathfinder/tests/test_load_nvidia_dynamic_lib.py b/cuda_pathfinder/tests/test_load_nvidia_dynamic_lib.py index ed735aaa2..ae44ab7f8 100644 --- a/cuda_pathfinder/tests/test_load_nvidia_dynamic_lib.py +++ b/cuda_pathfinder/tests/test_load_nvidia_dynamic_lib.py @@ -4,6 +4,8 @@ import functools import json import os +import shlex +from subprocess import list2cmdline # nosec B404 from unittest.mock import patch import pytest @@ -91,6 +93,12 @@ def _get_libnames_for_test_load_nvidia_dynamic_lib(): return tuple(result) +def _quote_for_shell_copypaste(s): + if os.name == "nt": + return list2cmdline([s]) + return shlex.quote(s) + + @pytest.mark.parametrize("libname", _get_libnames_for_test_load_nvidia_dynamic_lib()) def test_load_nvidia_dynamic_lib(info_summary_append, libname): # We intentionally run each dynamic library operation in a child process @@ -112,5 +120,5 @@ def raise_child_process_failed(): info_summary_append(f"Not found: {libname=!r}") else: abs_path = json.loads(result.stdout.rstrip()) - info_summary_append(f"{abs_path=}") + info_summary_append(f"abs_path={_quote_for_shell_copypaste(abs_path)}") assert os.path.isfile(abs_path) # double-check the abs_path From 894c6b59fb445f6c588a91689c1409e098034739 Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Mon, 22 Sep 2025 14:25:28 -0700 Subject: [PATCH 10/17] Standardize on `os.environ.get` under cuda_pathfinder/ --- .../cuda/pathfinder/_dynamic_libs/find_nvidia_dynamic_lib.py | 2 +- cuda_pathfinder/cuda/pathfinder/_headers/find_nvidia_headers.py | 2 +- cuda_pathfinder/tests/test_find_nvidia_headers.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cuda_pathfinder/cuda/pathfinder/_dynamic_libs/find_nvidia_dynamic_lib.py b/cuda_pathfinder/cuda/pathfinder/_dynamic_libs/find_nvidia_dynamic_lib.py index bb6763bca..0ea574c59 100644 --- a/cuda_pathfinder/cuda/pathfinder/_dynamic_libs/find_nvidia_dynamic_lib.py +++ b/cuda_pathfinder/cuda/pathfinder/_dynamic_libs/find_nvidia_dynamic_lib.py @@ -113,7 +113,7 @@ def _find_lib_dir_using_cuda_home(libname: str) -> Optional[str]: def _find_lib_dir_using_conda_prefix(libname: str) -> Optional[str]: - conda_prefix = os.getenv("CONDA_PREFIX") + conda_prefix = os.environ.get("CONDA_PREFIX") if not conda_prefix: return None return _find_lib_dir_using_anchor_point( diff --git a/cuda_pathfinder/cuda/pathfinder/_headers/find_nvidia_headers.py b/cuda_pathfinder/cuda/pathfinder/_headers/find_nvidia_headers.py index f97f12c06..61fa2fc28 100644 --- a/cuda_pathfinder/cuda/pathfinder/_headers/find_nvidia_headers.py +++ b/cuda_pathfinder/cuda/pathfinder/_headers/find_nvidia_headers.py @@ -90,7 +90,7 @@ def _find_ctk_header_directory(libname: str) -> Optional[str]: if _joined_isfile(hdr_dir, h_basename): return hdr_dir - conda_prefix = os.getenv("CONDA_PREFIX") + conda_prefix = os.environ.get("CONDA_PREFIX") if conda_prefix: # noqa: SIM102 if result := _find_based_on_conda_layout(libname, h_basename, conda_prefix): return result diff --git a/cuda_pathfinder/tests/test_find_nvidia_headers.py b/cuda_pathfinder/tests/test_find_nvidia_headers.py index da0f0e01e..d2b9c59eb 100644 --- a/cuda_pathfinder/tests/test_find_nvidia_headers.py +++ b/cuda_pathfinder/tests/test_find_nvidia_headers.py @@ -59,7 +59,7 @@ def test_find_libname_nvshmem(info_summary_append): if have_nvidia_nvshmem_package(): hdr_dir_parts = hdr_dir.split(os.path.sep) assert "site-packages" in hdr_dir_parts - elif conda_prefix := os.getenv("CONDA_PREFIX"): + elif conda_prefix := os.environ.get("CONDA_PREFIX"): assert hdr_dir.startswith(conda_prefix) else: assert hdr_dir.startswith("/usr/include/nvshmem_") From bd5e2ae58d69c3058542d2648ccbc933ec3d37d5 Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Mon, 22 Sep 2025 14:57:14 -0700 Subject: [PATCH 11/17] =?UTF-8?q?predefined=20=E2=86=92=20defined=20automa?= =?UTF-8?q?tically?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cuda/pathfinder/_dynamic_libs/load_nvidia_dynamic_lib.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cuda_pathfinder/cuda/pathfinder/_dynamic_libs/load_nvidia_dynamic_lib.py b/cuda_pathfinder/cuda/pathfinder/_dynamic_libs/load_nvidia_dynamic_lib.py index 465e43356..64bef4076 100644 --- a/cuda_pathfinder/cuda/pathfinder/_dynamic_libs/load_nvidia_dynamic_lib.py +++ b/cuda_pathfinder/cuda/pathfinder/_dynamic_libs/load_nvidia_dynamic_lib.py @@ -80,7 +80,7 @@ def load_nvidia_dynamic_lib(libname: str) -> LoadedDL: 2. **Conda environment** - Conda installations are discovered via ``CONDA_PREFIX``, which is - predefined in activated conda environments (see + defined automatically in activated conda environments (see https://docs.conda.io/projects/conda-build/en/stable/user-guide/environment-variables.html). 3. **OS default mechanisms** From ce2ca30c8060ff2c42432f554c5d9888a6fa046b Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Mon, 22 Sep 2025 15:26:25 -0700 Subject: [PATCH 12/17] Move quote_for_shell() to new _utils/platform_aware.py --- .../cuda/pathfinder/_utils/platform_aware.py | 18 ++++++++++++++++++ .../tests/test_load_nvidia_dynamic_lib.py | 11 ++--------- 2 files changed, 20 insertions(+), 9 deletions(-) create mode 100644 cuda_pathfinder/cuda/pathfinder/_utils/platform_aware.py diff --git a/cuda_pathfinder/cuda/pathfinder/_utils/platform_aware.py b/cuda_pathfinder/cuda/pathfinder/_utils/platform_aware.py new file mode 100644 index 000000000..196a0df7f --- /dev/null +++ b/cuda_pathfinder/cuda/pathfinder/_utils/platform_aware.py @@ -0,0 +1,18 @@ +# SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +import sys + +IS_WINDOWS = sys.platform == "win32" + + +def quote_for_shell(s: str) -> str: + if IS_WINDOWS: + # This is a relatively heavy import; keep pathfinder if possible. + from subprocess import list2cmdline # nosec B404 + + return list2cmdline([s]) + else: + import shlex + + return shlex.quote(s) diff --git a/cuda_pathfinder/tests/test_load_nvidia_dynamic_lib.py b/cuda_pathfinder/tests/test_load_nvidia_dynamic_lib.py index ae44ab7f8..c04b091e7 100644 --- a/cuda_pathfinder/tests/test_load_nvidia_dynamic_lib.py +++ b/cuda_pathfinder/tests/test_load_nvidia_dynamic_lib.py @@ -4,8 +4,6 @@ import functools import json import os -import shlex -from subprocess import list2cmdline # nosec B404 from unittest.mock import patch import pytest @@ -16,6 +14,7 @@ from cuda.pathfinder._dynamic_libs import supported_nvidia_libs from cuda.pathfinder._utils.find_site_packages_dll import find_all_dll_files_via_metadata from cuda.pathfinder._utils.find_site_packages_so import find_all_so_files_via_metadata +from cuda.pathfinder._utils.platform_aware import quote_for_shell STRICTNESS = os.environ.get("CUDA_PATHFINDER_TEST_LOAD_NVIDIA_DYNAMIC_LIB_STRICTNESS", "see_what_works") assert STRICTNESS in ("see_what_works", "all_must_work") @@ -93,12 +92,6 @@ def _get_libnames_for_test_load_nvidia_dynamic_lib(): return tuple(result) -def _quote_for_shell_copypaste(s): - if os.name == "nt": - return list2cmdline([s]) - return shlex.quote(s) - - @pytest.mark.parametrize("libname", _get_libnames_for_test_load_nvidia_dynamic_lib()) def test_load_nvidia_dynamic_lib(info_summary_append, libname): # We intentionally run each dynamic library operation in a child process @@ -120,5 +113,5 @@ def raise_child_process_failed(): info_summary_append(f"Not found: {libname=!r}") else: abs_path = json.loads(result.stdout.rstrip()) - info_summary_append(f"abs_path={_quote_for_shell_copypaste(abs_path)}") + info_summary_append(f"abs_path={quote_for_shell(abs_path)}") assert os.path.isfile(abs_path) # double-check the abs_path From 9e0b4a8172d06418b6d7d2103a060a41d8f94841 Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Mon, 22 Sep 2025 15:33:40 -0700 Subject: [PATCH 13/17] Universally use cuda.pathfinder._utils.platform_aware.IS_WINDOWS --- .../pathfinder/_dynamic_libs/find_nvidia_dynamic_lib.py | 2 +- .../pathfinder/_dynamic_libs/load_nvidia_dynamic_lib.py | 2 +- .../cuda/pathfinder/_dynamic_libs/supported_nvidia_libs.py | 4 +--- .../cuda/pathfinder/_headers/find_nvidia_headers.py | 2 +- .../cuda/pathfinder/_headers/supported_nvidia_headers.py | 3 +-- .../tests/child_load_nvidia_dynamic_lib_helper.py | 2 +- cuda_pathfinder/tests/test_find_nvidia_headers.py | 2 +- cuda_pathfinder/tests/test_load_nvidia_dynamic_lib.py | 6 +++--- 8 files changed, 10 insertions(+), 13 deletions(-) diff --git a/cuda_pathfinder/cuda/pathfinder/_dynamic_libs/find_nvidia_dynamic_lib.py b/cuda_pathfinder/cuda/pathfinder/_dynamic_libs/find_nvidia_dynamic_lib.py index 0ea574c59..d0d22dabf 100644 --- a/cuda_pathfinder/cuda/pathfinder/_dynamic_libs/find_nvidia_dynamic_lib.py +++ b/cuda_pathfinder/cuda/pathfinder/_dynamic_libs/find_nvidia_dynamic_lib.py @@ -9,13 +9,13 @@ from cuda.pathfinder._dynamic_libs.load_dl_common import DynamicLibNotFoundError from cuda.pathfinder._dynamic_libs.supported_nvidia_libs import ( - IS_WINDOWS, SITE_PACKAGES_LIBDIRS_LINUX, SITE_PACKAGES_LIBDIRS_WINDOWS, is_suppressed_dll_file, ) from cuda.pathfinder._utils.env_vars import get_cuda_home_or_path from cuda.pathfinder._utils.find_sub_dirs import find_sub_dirs, find_sub_dirs_all_sitepackages +from cuda.pathfinder._utils.platform_aware import IS_WINDOWS def _no_such_file_in_sub_dirs( diff --git a/cuda_pathfinder/cuda/pathfinder/_dynamic_libs/load_nvidia_dynamic_lib.py b/cuda_pathfinder/cuda/pathfinder/_dynamic_libs/load_nvidia_dynamic_lib.py index 64bef4076..40cfd60f8 100644 --- a/cuda_pathfinder/cuda/pathfinder/_dynamic_libs/load_nvidia_dynamic_lib.py +++ b/cuda_pathfinder/cuda/pathfinder/_dynamic_libs/load_nvidia_dynamic_lib.py @@ -7,7 +7,7 @@ from cuda.pathfinder._dynamic_libs.find_nvidia_dynamic_lib import _FindNvidiaDynamicLib from cuda.pathfinder._dynamic_libs.load_dl_common import LoadedDL, load_dependencies -from cuda.pathfinder._dynamic_libs.supported_nvidia_libs import IS_WINDOWS +from cuda.pathfinder._utils.platform_aware import IS_WINDOWS if IS_WINDOWS: from cuda.pathfinder._dynamic_libs.load_dl_windows import ( diff --git a/cuda_pathfinder/cuda/pathfinder/_dynamic_libs/supported_nvidia_libs.py b/cuda_pathfinder/cuda/pathfinder/_dynamic_libs/supported_nvidia_libs.py index 4b1eb5ce6..7933e909a 100644 --- a/cuda_pathfinder/cuda/pathfinder/_dynamic_libs/supported_nvidia_libs.py +++ b/cuda_pathfinder/cuda/pathfinder/_dynamic_libs/supported_nvidia_libs.py @@ -7,9 +7,7 @@ # SUPPORTED_WINDOWS_DLLS # SUPPORTED_LINUX_SONAMES -import sys - -IS_WINDOWS = sys.platform == "win32" +from cuda.pathfinder._utils.platform_aware import IS_WINDOWS SUPPORTED_LIBNAMES_COMMON = ( # Core CUDA Runtime and Compiler diff --git a/cuda_pathfinder/cuda/pathfinder/_headers/find_nvidia_headers.py b/cuda_pathfinder/cuda/pathfinder/_headers/find_nvidia_headers.py index 61fa2fc28..535d4b800 100644 --- a/cuda_pathfinder/cuda/pathfinder/_headers/find_nvidia_headers.py +++ b/cuda_pathfinder/cuda/pathfinder/_headers/find_nvidia_headers.py @@ -7,9 +7,9 @@ from typing import Optional from cuda.pathfinder._headers import supported_nvidia_headers -from cuda.pathfinder._headers.supported_nvidia_headers import IS_WINDOWS from cuda.pathfinder._utils.env_vars import get_cuda_home_or_path from cuda.pathfinder._utils.find_sub_dirs import find_sub_dirs_all_sitepackages +from cuda.pathfinder._utils.platform_aware import IS_WINDOWS def _abs_norm(path: Optional[str]) -> Optional[str]: diff --git a/cuda_pathfinder/cuda/pathfinder/_headers/supported_nvidia_headers.py b/cuda_pathfinder/cuda/pathfinder/_headers/supported_nvidia_headers.py index afd9067de..1b2855c0c 100644 --- a/cuda_pathfinder/cuda/pathfinder/_headers/supported_nvidia_headers.py +++ b/cuda_pathfinder/cuda/pathfinder/_headers/supported_nvidia_headers.py @@ -1,10 +1,9 @@ # SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. # SPDX-License-Identifier: Apache-2.0 -import sys from typing import Final -IS_WINDOWS = sys.platform == "win32" +from cuda.pathfinder._utils.platform_aware import IS_WINDOWS SUPPORTED_HEADERS_CTK_COMMON = { "cccl": "cuda/std/version", diff --git a/cuda_pathfinder/tests/child_load_nvidia_dynamic_lib_helper.py b/cuda_pathfinder/tests/child_load_nvidia_dynamic_lib_helper.py index fceaa33be..0f7322631 100644 --- a/cuda_pathfinder/tests/child_load_nvidia_dynamic_lib_helper.py +++ b/cuda_pathfinder/tests/child_load_nvidia_dynamic_lib_helper.py @@ -29,10 +29,10 @@ def child_process_func(libname): from cuda.pathfinder import DynamicLibNotFoundError, load_nvidia_dynamic_lib from cuda.pathfinder._dynamic_libs.load_nvidia_dynamic_lib import _load_lib_no_cache from cuda.pathfinder._dynamic_libs.supported_nvidia_libs import ( - IS_WINDOWS, SUPPORTED_LINUX_SONAMES, SUPPORTED_WINDOWS_DLLS, ) + from cuda.pathfinder._utils.platform_aware import IS_WINDOWS try: loaded_dl_fresh = load_nvidia_dynamic_lib(libname) diff --git a/cuda_pathfinder/tests/test_find_nvidia_headers.py b/cuda_pathfinder/tests/test_find_nvidia_headers.py index d2b9c59eb..cdea0cd28 100644 --- a/cuda_pathfinder/tests/test_find_nvidia_headers.py +++ b/cuda_pathfinder/tests/test_find_nvidia_headers.py @@ -22,11 +22,11 @@ from cuda.pathfinder import find_nvidia_header_directory from cuda.pathfinder._headers.supported_nvidia_headers import ( - IS_WINDOWS, SUPPORTED_HEADERS_CTK, SUPPORTED_HEADERS_CTK_ALL, SUPPORTED_SITE_PACKAGE_HEADER_DIRS_CTK, ) +from cuda.pathfinder._utils.platform_aware import IS_WINDOWS STRICTNESS = os.environ.get("CUDA_PATHFINDER_TEST_FIND_NVIDIA_HEADERS_STRICTNESS", "see_what_works") assert STRICTNESS in ("see_what_works", "all_must_work") diff --git a/cuda_pathfinder/tests/test_load_nvidia_dynamic_lib.py b/cuda_pathfinder/tests/test_load_nvidia_dynamic_lib.py index c04b091e7..b50c644a6 100644 --- a/cuda_pathfinder/tests/test_load_nvidia_dynamic_lib.py +++ b/cuda_pathfinder/tests/test_load_nvidia_dynamic_lib.py @@ -14,7 +14,7 @@ from cuda.pathfinder._dynamic_libs import supported_nvidia_libs from cuda.pathfinder._utils.find_site_packages_dll import find_all_dll_files_via_metadata from cuda.pathfinder._utils.find_site_packages_so import find_all_so_files_via_metadata -from cuda.pathfinder._utils.platform_aware import quote_for_shell +from cuda.pathfinder._utils.platform_aware import IS_WINDOWS, quote_for_shell STRICTNESS = os.environ.get("CUDA_PATHFINDER_TEST_LOAD_NVIDIA_DYNAMIC_LIB_STRICTNESS", "see_what_works") assert STRICTNESS in ("see_what_works", "all_must_work") @@ -74,7 +74,7 @@ def test_runtime_error_on_non_64bit_python(): @functools.cache def _get_libnames_for_test_load_nvidia_dynamic_lib(): result = list(SUPPORTED_NVIDIA_LIBNAMES) - if supported_nvidia_libs.IS_WINDOWS: + if IS_WINDOWS: spld_other = supported_nvidia_libs.SITE_PACKAGES_LIBDIRS_WINDOWS_OTHER all_dyn_libs = find_all_dll_files_via_metadata() for libname in spld_other: @@ -98,7 +98,7 @@ def test_load_nvidia_dynamic_lib(info_summary_append, libname): # to ensure isolation of global dynamic linking state (e.g., dlopen handles). # Without child processes, loading/unloading libraries during testing could # interfere across test cases and lead to nondeterministic or platform-specific failures. - timeout = 120 if supported_nvidia_libs.IS_WINDOWS else 30 + timeout = 120 if IS_WINDOWS else 30 result = spawned_process_runner.run_in_spawned_child_process(child_process_func, args=(libname,), timeout=timeout) def raise_child_process_failed(): From 5fd4801045ee9d643efab6260a57d442d64ab9fc Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Mon, 22 Sep 2025 21:47:09 -0700 Subject: [PATCH 14/17] Bump pathfinder version to 1.3.0 (for release) and add release notes. --- cuda_pathfinder/cuda/pathfinder/_version.py | 2 +- cuda_pathfinder/docs/nv-versions.json | 4 ++++ cuda_pathfinder/docs/source/release.rst | 1 + .../docs/source/release/1.3.0-notes.rst | 17 +++++++++++++++++ 4 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 cuda_pathfinder/docs/source/release/1.3.0-notes.rst diff --git a/cuda_pathfinder/cuda/pathfinder/_version.py b/cuda_pathfinder/cuda/pathfinder/_version.py index d0695c7c8..9fca58d64 100644 --- a/cuda_pathfinder/cuda/pathfinder/_version.py +++ b/cuda_pathfinder/cuda/pathfinder/_version.py @@ -1,4 +1,4 @@ # SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. # SPDX-License-Identifier: Apache-2.0 -__version__ = "1.2.4a0" +__version__ = "1.3.0" diff --git a/cuda_pathfinder/docs/nv-versions.json b/cuda_pathfinder/docs/nv-versions.json index 9fcc3f0ab..db8fecd82 100644 --- a/cuda_pathfinder/docs/nv-versions.json +++ b/cuda_pathfinder/docs/nv-versions.json @@ -3,6 +3,10 @@ "version": "latest", "url": "https://nvidia.github.io/cuda-python/cuda-pathfinder/latest/" }, + { + "version": "1.3.0", + "url": "https://nvidia.github.io/cuda-python/cuda-pathfinder/1.3.0/" + }, { "version": "1.2.3", "url": "https://nvidia.github.io/cuda-python/cuda-pathfinder/1.2.3/" diff --git a/cuda_pathfinder/docs/source/release.rst b/cuda_pathfinder/docs/source/release.rst index 62dbf7ad6..b46579557 100644 --- a/cuda_pathfinder/docs/source/release.rst +++ b/cuda_pathfinder/docs/source/release.rst @@ -7,6 +7,7 @@ Release Notes .. toctree:: :maxdepth: 3 + 1.3.0 1.2.3 1.2.2 1.2.1 diff --git a/cuda_pathfinder/docs/source/release/1.3.0-notes.rst b/cuda_pathfinder/docs/source/release/1.3.0-notes.rst new file mode 100644 index 000000000..b5daea6f9 --- /dev/null +++ b/cuda_pathfinder/docs/source/release/1.3.0-notes.rst @@ -0,0 +1,17 @@ +.. SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +.. SPDX-License-Identifier: Apache-2.0 + +.. module:: cuda.pathfinder + +``cuda-pathfinder`` 1.3.0 Release notes +======================================= + +Released on Sep 25, 2025 + + +Highlights +---------- + +* Add a Conda-specific search into ``load_nvidia_dynamic_lib()``. + This makes the search more intuitive and predictable. + (`PR #1003 `_) From 2841734bcf40c4684e4818d5aa0dd574f2417d09 Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Tue, 23 Sep 2025 10:53:44 -0700 Subject: [PATCH 15/17] Replace `find_sub_dirs` with `glob.glob` for simplicity. Functionally equivalent. --- .../_dynamic_libs/find_nvidia_dynamic_lib.py | 35 ++++++++++--------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/cuda_pathfinder/cuda/pathfinder/_dynamic_libs/find_nvidia_dynamic_lib.py b/cuda_pathfinder/cuda/pathfinder/_dynamic_libs/find_nvidia_dynamic_lib.py index d0d22dabf..16af1d380 100644 --- a/cuda_pathfinder/cuda/pathfinder/_dynamic_libs/find_nvidia_dynamic_lib.py +++ b/cuda_pathfinder/cuda/pathfinder/_dynamic_libs/find_nvidia_dynamic_lib.py @@ -14,7 +14,7 @@ is_suppressed_dll_file, ) from cuda.pathfinder._utils.env_vars import get_cuda_home_or_path -from cuda.pathfinder._utils.find_sub_dirs import find_sub_dirs, find_sub_dirs_all_sitepackages +from cuda.pathfinder._utils.find_sub_dirs import find_sub_dirs_all_sitepackages from cuda.pathfinder._utils.platform_aware import IS_WINDOWS @@ -81,27 +81,30 @@ def _find_dll_using_nvidia_bin_dirs( def _find_lib_dir_using_anchor_point(libname: str, anchor_point: str, linux_lib_dir: str) -> Optional[str]: - subdirs_list: tuple[tuple[str, ...], ...] + # Resolve paths for the four cases: + # Windows/Linux x nvvm yes/no if IS_WINDOWS: if libname == "nvvm": # noqa: SIM108 - subdirs_list = ( - ("nvvm", "bin", "*"), # CTK 13 - ("nvvm", "bin"), # CTK 12 - ) + rel_paths = [ + "nvvm/bin/*", # CTK 13 + "nvvm/bin", # CTK 12 + ] else: - subdirs_list = ( - ("bin", "x64"), # CTK 13 - ("bin",), # CTK 12 - ) + rel_paths = [ + "bin/x64", # CTK 13 + "bin", # CTK 12 + ] else: if libname == "nvvm": # noqa: SIM108 - subdirs_list = (("nvvm", "lib64"),) + rel_paths = ["nvvm/lib64"] else: - subdirs_list = ((linux_lib_dir,),) - for sub_dirs in subdirs_list: - dirname: str # work around bug in mypy - for dirname in find_sub_dirs((anchor_point,), sub_dirs): - return dirname + rel_paths = [linux_lib_dir] + + for rel_path in rel_paths: + for dirname in sorted(glob.glob(os.path.join(anchor_point, rel_path))): + if os.path.isdir(dirname): + return dirname + return None From c83fa0ddc4529ef9c909608becd32c523ccfcb12 Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Tue, 23 Sep 2025 12:02:55 -0700 Subject: [PATCH 16/17] =?UTF-8?q?Replace=20sys.stdout.write=20=E2=86=92=20?= =?UTF-8?q?print?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cuda_pathfinder/tests/child_load_nvidia_dynamic_lib_helper.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cuda_pathfinder/tests/child_load_nvidia_dynamic_lib_helper.py b/cuda_pathfinder/tests/child_load_nvidia_dynamic_lib_helper.py index 0f7322631..72ee80c89 100644 --- a/cuda_pathfinder/tests/child_load_nvidia_dynamic_lib_helper.py +++ b/cuda_pathfinder/tests/child_load_nvidia_dynamic_lib_helper.py @@ -37,7 +37,7 @@ def child_process_func(libname): try: loaded_dl_fresh = load_nvidia_dynamic_lib(libname) except DynamicLibNotFoundError: - sys.stdout.write("CHILD_LOAD_NVIDIA_DYNAMIC_LIB_HELPER_DYNAMIC_LIB_NOT_FOUND_ERROR:\n") + print("CHILD_LOAD_NVIDIA_DYNAMIC_LIB_HELPER_DYNAMIC_LIB_NOT_FOUND_ERROR:") # noqa: T201 traceback.print_exc(file=sys.stdout) return if loaded_dl_fresh.was_already_loaded_from_elsewhere: @@ -57,4 +57,4 @@ def child_process_func(libname): raise RuntimeError(f"not os.path.samefile({loaded_dl_no_cache.abs_path=!r}, {loaded_dl_fresh.abs_path=!r})") validate_abs_path(loaded_dl_no_cache.abs_path) - sys.stdout.write(json.dumps(loaded_dl_fresh.abs_path) + "\n") + print(json.dumps(loaded_dl_fresh.abs_path)) # noqa: T201 From 45096260b03292ab371e339b75875afa40064078 Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Wed, 24 Sep 2025 17:11:28 -0700 Subject: [PATCH 17/17] Add issue 1011 notes to the load_nvidia_dynamic_lib() docstring and the 1.3.0 release notes. --- .../_dynamic_libs/load_nvidia_dynamic_lib.py | 12 ++++++++++++ .../docs/source/release/1.3.0-notes.rst | 16 ++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/cuda_pathfinder/cuda/pathfinder/_dynamic_libs/load_nvidia_dynamic_lib.py b/cuda_pathfinder/cuda/pathfinder/_dynamic_libs/load_nvidia_dynamic_lib.py index 40cfd60f8..31d8b1bcf 100644 --- a/cuda_pathfinder/cuda/pathfinder/_dynamic_libs/load_nvidia_dynamic_lib.py +++ b/cuda_pathfinder/cuda/pathfinder/_dynamic_libs/load_nvidia_dynamic_lib.py @@ -62,6 +62,18 @@ def load_nvidia_dynamic_lib(libname: str) -> LoadedDL: Returns: LoadedDL: Object containing the OS library handle and absolute path. + **Important:** + + **Never close the returned handle.** Do **not** call ``dlclose`` (Linux) or + ``FreeLibrary`` (Windows) on the ``LoadedDL._handle_uint``. + + **Why:** the return value is cached (``functools.cache``) and shared across the + process. Closing the handle can unload the module while other code still uses + it, leading to crashes or subtle failures. + + This applies to Linux and Windows. For context, see issue #1011: + https://github.com/NVIDIA/cuda-python/issues/1011 + Raises: DynamicLibNotFoundError: If the library cannot be found or loaded. RuntimeError: If Python is not 64-bit. diff --git a/cuda_pathfinder/docs/source/release/1.3.0-notes.rst b/cuda_pathfinder/docs/source/release/1.3.0-notes.rst index b5daea6f9..d0517eb57 100644 --- a/cuda_pathfinder/docs/source/release/1.3.0-notes.rst +++ b/cuda_pathfinder/docs/source/release/1.3.0-notes.rst @@ -8,6 +8,22 @@ Released on Sep 25, 2025 +Important update to the ``load_nvidia_dynamic_lib`` documentation +----------------------------------------------------------------- + +The documentation for :func:`cuda.pathfinder.load_nvidia_dynamic_lib` now calls out +an important behavior: + +* **Do not close** the OS handle returned (``LoadedDL._handle_uint``). Avoid + ``dlclose`` (Linux) and ``FreeLibrary`` (Windows). + +* **Reason:** the function's result is cached (``functools.cache``) and shared across + the process. Closing the handle can unload the module while other code still holds + a reference, which may cause crashes or latent errors. + +This note is documentation‑only; there is no API change in 1.3.0. See issue #1011 +for background and discussion: +https://github.com/NVIDIA/cuda-python/issues/1011 Highlights ----------