diff --git a/cuda_pathfinder/cuda/pathfinder/__init__.py b/cuda_pathfinder/cuda/pathfinder/__init__.py index a0ba8e4f21..b598829cc7 100644 --- a/cuda_pathfinder/cuda/pathfinder/__init__.py +++ b/cuda_pathfinder/cuda/pathfinder/__init__.py @@ -7,7 +7,13 @@ find_nvidia_binary_utility as find_nvidia_binary_utility, ) from cuda.pathfinder._binaries.supported_nvidia_binaries import SUPPORTED_BINARIES as _SUPPORTED_BINARIES +from cuda.pathfinder._dynamic_libs.load_dl_common import ( + DynamicLibNotAvailableError as DynamicLibNotAvailableError, +) from cuda.pathfinder._dynamic_libs.load_dl_common import DynamicLibNotFoundError as DynamicLibNotFoundError +from cuda.pathfinder._dynamic_libs.load_dl_common import ( + DynamicLibUnknownError as DynamicLibUnknownError, +) from cuda.pathfinder._dynamic_libs.load_dl_common import LoadedDL as LoadedDL from cuda.pathfinder._dynamic_libs.load_nvidia_dynamic_lib import load_nvidia_dynamic_lib as load_nvidia_dynamic_lib from cuda.pathfinder._dynamic_libs.supported_nvidia_libs import ( diff --git a/cuda_pathfinder/cuda/pathfinder/_dynamic_libs/load_dl_common.py b/cuda_pathfinder/cuda/pathfinder/_dynamic_libs/load_dl_common.py index 91e6284a00..1204a23c15 100644 --- a/cuda_pathfinder/cuda/pathfinder/_dynamic_libs/load_dl_common.py +++ b/cuda_pathfinder/cuda/pathfinder/_dynamic_libs/load_dl_common.py @@ -11,6 +11,14 @@ class DynamicLibNotFoundError(RuntimeError): pass +class DynamicLibNotAvailableError(DynamicLibNotFoundError): + pass + + +class DynamicLibUnknownError(DynamicLibNotFoundError): + pass + + @dataclass class LoadedDL: abs_path: str | None 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 3e22d62b30..9df1c5de23 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 @@ -11,7 +11,13 @@ _FindNvidiaDynamicLib, derive_ctk_root, ) -from cuda.pathfinder._dynamic_libs.load_dl_common import DynamicLibNotFoundError, LoadedDL, load_dependencies +from cuda.pathfinder._dynamic_libs.load_dl_common import ( + DynamicLibNotAvailableError, + DynamicLibNotFoundError, + DynamicLibUnknownError, + LoadedDL, + load_dependencies, +) from cuda.pathfinder._dynamic_libs.supported_nvidia_libs import ( _CTK_ROOT_CANARY_ANCHOR_LIBNAMES, _CTK_ROOT_CANARY_DISCOVERABLE_LIBNAMES, @@ -41,6 +47,8 @@ _ALL_SUPPORTED_LIBNAMES: frozenset[str] = frozenset( (SUPPORTED_WINDOWS_DLLS if IS_WINDOWS else SUPPORTED_LINUX_SONAMES).keys() ) +_ALL_KNOWN_LIBNAMES: frozenset[str] = frozenset(SUPPORTED_LINUX_SONAMES) | frozenset(SUPPORTED_WINDOWS_DLLS) +_PLATFORM_NAME = "Windows" if IS_WINDOWS else "Linux" # Driver libraries: shipped with the NVIDIA display driver, always on the # system linker path. These skip all CTK search steps (site-packages, @@ -205,7 +213,9 @@ def load_nvidia_dynamic_lib(libname: str) -> LoadedDL: https://github.com/NVIDIA/cuda-python/issues/1011 Raises: - ValueError: If ``libname`` is not a recognized library name. + DynamicLibUnknownError: If ``libname`` is not a recognized library name. + DynamicLibNotAvailableError: If ``libname`` is recognized but not + supported on this platform. DynamicLibNotFoundError: If the library cannot be found or loaded. RuntimeError: If Python is not 64-bit. @@ -278,6 +288,11 @@ def load_nvidia_dynamic_lib(libname: str) -> LoadedDL: f" Currently running: {pointer_size_bits}-bit Python" f" {sys.version_info.major}.{sys.version_info.minor}" ) + if libname not in _ALL_KNOWN_LIBNAMES: + raise DynamicLibUnknownError(f"Unknown library name: {libname!r}. Known names: {sorted(_ALL_KNOWN_LIBNAMES)}") if libname not in _ALL_SUPPORTED_LIBNAMES: - raise ValueError(f"Unsupported library name: {libname!r}. Supported names: {sorted(_ALL_SUPPORTED_LIBNAMES)}") + raise DynamicLibNotAvailableError( + f"Library name {libname!r} is known but not available on {_PLATFORM_NAME}. " + f"Supported names on {_PLATFORM_NAME}: {sorted(_ALL_SUPPORTED_LIBNAMES)}" + ) return _load_lib_no_cache(libname) diff --git a/cuda_pathfinder/docs/nv-versions.json b/cuda_pathfinder/docs/nv-versions.json index 69f16c3c40..a8498094b5 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.4.0", + "url": "https://nvidia.github.io/cuda-python/cuda-pathfinder/1.4.0/" + }, { "version": "1.3.5", "url": "https://nvidia.github.io/cuda-python/cuda-pathfinder/1.3.5/" diff --git a/cuda_pathfinder/docs/source/api.rst b/cuda_pathfinder/docs/source/api.rst index 19ade040b4..52a4ff5010 100644 --- a/cuda_pathfinder/docs/source/api.rst +++ b/cuda_pathfinder/docs/source/api.rst @@ -16,6 +16,8 @@ locating NVIDIA C/C++ header directories, and finding CUDA binary utilities. load_nvidia_dynamic_lib LoadedDL DynamicLibNotFoundError + DynamicLibUnknownError + DynamicLibNotAvailableError SUPPORTED_HEADERS_CTK SUPPORTED_HEADERS_NON_CTK diff --git a/cuda_pathfinder/docs/source/release/1.3.5-notes.rst b/cuda_pathfinder/docs/source/release/1.3.5-notes.rst index acf66adf04..9db2dbf4fe 100644 --- a/cuda_pathfinder/docs/source/release/1.3.5-notes.rst +++ b/cuda_pathfinder/docs/source/release/1.3.5-notes.rst @@ -8,12 +8,27 @@ Released on Feb 23, 2026 +.. warning:: + + ``cuda-pathfinder==1.3.5`` introduced a short-lived backward-incompatible + exception regression in ``load_nvidia_dynamic_lib()`` (unsupported + library names incorrectly raising ``ValueError``). + + This bug was corrected in ``cuda-pathfinder>=1.4.0``, restoring backward-compatible + ``DynamicLibNotFoundError`` subclass behavior while preserving more specific + error signaling. + + See `Issue #1684 `_ and + `PR #1688 `_. + Highlights ---------- * Add support for loading NVIDIA driver libraries (``"cuda"``, ``"nvml"``) via ``load_nvidia_dynamic_lib()``, and reject unsupported library names - with ``ValueError``. + with ``ValueError`` (**EDIT:** this behavior was + `a regression `_ + and was corrected in ``cuda-pathfinder>=1.4.0``). (`PR #1602 `_) * Add bitcode library discovery helpers and public API support, including diff --git a/cuda_pathfinder/docs/source/release/1.4.0-notes.rst b/cuda_pathfinder/docs/source/release/1.4.0-notes.rst new file mode 100644 index 0000000000..bcfd202b7d --- /dev/null +++ b/cuda_pathfinder/docs/source/release/1.4.0-notes.rst @@ -0,0 +1,23 @@ +.. SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +.. SPDX-License-Identifier: Apache-2.0 + +.. py:currentmodule:: cuda.pathfinder + +``cuda-pathfinder`` 1.4.0 Release notes +======================================= + +Released on Feb 25, 2026 + +Highlights +---------- + +* Add CTK root canary probing for non-standard-path libraries in + ``load_nvidia_dynamic_lib()`` (notably ``nvvm``), including spawned child + process isolation for the canary probe. + (`PR #1595 `_) + +* Restore backward-compatible exception behavior for + ``load_nvidia_dynamic_lib()`` argument validation by replacing the short-lived + ``ValueError`` introduced in ``1.3.5`` with ``DynamicLibNotFoundError`` + subclasses: ``DynamicLibUnknownError`` and ``DynamicLibNotAvailableError``. + (`PR #1688 `_) diff --git a/cuda_pathfinder/tests/test_load_nvidia_dynamic_lib.py b/cuda_pathfinder/tests/test_load_nvidia_dynamic_lib.py index 05c00f81ed..6de2bff097 100644 --- a/cuda_pathfinder/tests/test_load_nvidia_dynamic_lib.py +++ b/cuda_pathfinder/tests/test_load_nvidia_dynamic_lib.py @@ -9,7 +9,8 @@ from child_load_nvidia_dynamic_lib_helper import build_child_process_failed_for_libname_message, child_process_func from local_helpers import have_distribution -from cuda.pathfinder import load_nvidia_dynamic_lib +from cuda.pathfinder import DynamicLibNotAvailableError, DynamicLibUnknownError, load_nvidia_dynamic_lib +from cuda.pathfinder._dynamic_libs import load_nvidia_dynamic_lib as load_nvidia_dynamic_lib_module from cuda.pathfinder._dynamic_libs import supported_nvidia_libs from cuda.pathfinder._utils.platform_aware import IS_WINDOWS, quote_for_shell from cuda.pathfinder._utils.spawned_process_runner import run_in_spawned_child_process @@ -69,11 +70,23 @@ def test_runtime_error_on_non_64bit_python(mocker): load_nvidia_dynamic_lib("cudart") -def test_unsupported_libname_raises_value_error(): - with pytest.raises(ValueError, match=r"Unsupported library name: 'not_a_real_lib'.*cudart"): +def test_unknown_libname_raises_dynamic_lib_unknown_error(): + with pytest.raises(DynamicLibUnknownError, match=r"Unknown library name: 'not_a_real_lib'.*cudart"): load_nvidia_dynamic_lib("not_a_real_lib") +def test_known_but_platform_unavailable_libname_raises_dynamic_lib_not_available_error(monkeypatch): + load_nvidia_dynamic_lib.cache_clear() + monkeypatch.setattr(load_nvidia_dynamic_lib_module, "_ALL_KNOWN_LIBNAMES", frozenset(("known_but_unavailable",))) + monkeypatch.setattr(load_nvidia_dynamic_lib_module, "_ALL_SUPPORTED_LIBNAMES", frozenset()) + monkeypatch.setattr(load_nvidia_dynamic_lib_module, "_PLATFORM_NAME", "TestOS") + with pytest.raises( + DynamicLibNotAvailableError, + match=r"known_but_unavailable.*not available on TestOS", + ): + load_nvidia_dynamic_lib("known_but_unavailable") + + IMPORTLIB_METADATA_DISTRIBUTIONS_NAMES = { "cufftMp": r"^nvidia-cufftmp-.*$", "mathdx": r"^nvidia-libmathdx-.*$",