From 9ad6bcde8cca87b07aa4919b161534e324caf64a Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Tue, 30 Sep 2025 16:50:10 -0700 Subject: [PATCH 01/12] _decide_nvjitlink_or_driver(): catch RuntimeError (bug fix), use importlib + ModuleNotFoundError (more selective than ImportError) and produce specific error messages --- cuda_core/cuda/core/experimental/_linker.py | 46 ++++++++++++--------- 1 file changed, 27 insertions(+), 19 deletions(-) diff --git a/cuda_core/cuda/core/experimental/_linker.py b/cuda_core/cuda/core/experimental/_linker.py index cef778c9aa..39672c993c 100644 --- a/cuda_core/cuda/core/experimental/_linker.py +++ b/cuda_core/cuda/core/experimental/_linker.py @@ -5,6 +5,8 @@ from __future__ import annotations import ctypes +import importlib +import sys import weakref from contextlib import contextmanager from dataclasses import dataclass @@ -37,28 +39,34 @@ def _decide_nvjitlink_or_driver() -> bool: _driver_ver = handle_return(driver.cuDriverGetVersion()) _driver_ver = (_driver_ver // 1000, (_driver_ver % 1000) // 10) + + def libname(): + return "nvJitLink*.dll" if sys.platform == "win32" else "libnvJitLink.so*" + + therefore_not_usable = ". Therefore cuda.bindings.nvjitlink is not usable and" + try: - from cuda.bindings import nvjitlink as _nvjitlink + _nvjitlink = importlib.import_module("cuda.bindings.nvjitlink") + except ModuleNotFoundError: + problem = "cuda.bindings.nvjitlink is not available, therefore" + except RuntimeError: + problem = libname() + " is not available" + therefore_not_usable + else: from cuda.bindings._internal import nvjitlink as inner_nvjitlink - except ImportError: - # binding is not available + + if inner_nvjitlink._inspect_function_pointer("__nvJitLinkVersion"): + return False # Use nvjitlink + + problem = libname() + " is is too old (<12.3)" + therefore_not_usable _nvjitlink = None - else: - if inner_nvjitlink._inspect_function_pointer("__nvJitLinkVersion") == 0: - # binding is available, but nvJitLink is not installed - _nvjitlink = None - - if _nvjitlink is None: - warn( - "nvJitLink is not installed or too old (<12.3). Therefore it is not usable " - "and the culink APIs will be used instead.", - stacklevel=3, - category=RuntimeWarning, - ) - _driver = driver - return True - else: - return False + + warn( + problem + " the culink APIs will be used instead.", + stacklevel=2, + category=RuntimeWarning, + ) + _driver = driver + return True def _lazy_init(): From 9390c85346f993028d01948e3acce5bd2145f93b Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Tue, 30 Sep 2025 17:16:13 -0700 Subject: [PATCH 02/12] Fix misunderstanding: RuntimeError is raised only from inner_nvjitlink._inspect_function_pointer() --- cuda_core/cuda/core/experimental/_linker.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/cuda_core/cuda/core/experimental/_linker.py b/cuda_core/cuda/core/experimental/_linker.py index 39672c993c..fa117c4116 100644 --- a/cuda_core/cuda/core/experimental/_linker.py +++ b/cuda_core/cuda/core/experimental/_linker.py @@ -49,15 +49,16 @@ def libname(): _nvjitlink = importlib.import_module("cuda.bindings.nvjitlink") except ModuleNotFoundError: problem = "cuda.bindings.nvjitlink is not available, therefore" - except RuntimeError: - problem = libname() + " is not available" + therefore_not_usable else: from cuda.bindings._internal import nvjitlink as inner_nvjitlink - if inner_nvjitlink._inspect_function_pointer("__nvJitLinkVersion"): - return False # Use nvjitlink - - problem = libname() + " is is too old (<12.3)" + therefore_not_usable + try: + if inner_nvjitlink._inspect_function_pointer("__nvJitLinkVersion"): + return False # Use nvjitlink + except RuntimeError: + problem = libname() + " is not available" + therefore_not_usable + else: + problem = libname() + " is is too old (<12.3)" + therefore_not_usable _nvjitlink = None warn( From 6bf421ed0bd3a2460b62c7252d54365bfe7c8fa0 Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Wed, 1 Oct 2025 12:04:20 -0700 Subject: [PATCH 03/12] Better way of formatting warning messages. --- cuda_core/cuda/core/experimental/_linker.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/cuda_core/cuda/core/experimental/_linker.py b/cuda_core/cuda/core/experimental/_linker.py index fa117c4116..2a011e1f7a 100644 --- a/cuda_core/cuda/core/experimental/_linker.py +++ b/cuda_core/cuda/core/experimental/_linker.py @@ -40,29 +40,30 @@ def _decide_nvjitlink_or_driver() -> bool: _driver_ver = handle_return(driver.cuDriverGetVersion()) _driver_ver = (_driver_ver // 1000, (_driver_ver % 1000) // 10) - def libname(): - return "nvJitLink*.dll" if sys.platform == "win32" else "libnvJitLink.so*" - - therefore_not_usable = ". Therefore cuda.bindings.nvjitlink is not usable and" - try: _nvjitlink = importlib.import_module("cuda.bindings.nvjitlink") except ModuleNotFoundError: - problem = "cuda.bindings.nvjitlink is not available, therefore" + warn_txt = "cuda.bindings.nvjitlink is not available, therefore" else: from cuda.bindings._internal import nvjitlink as inner_nvjitlink + def build_warn_txt(detail): + return ( + f"{'nvJitLink*.dll' if sys.platform == 'win32' else 'libnvJitLink.so*'} is {detail}." + " Therefore cuda.bindings.nvjitlink is not usable and" + ) + try: if inner_nvjitlink._inspect_function_pointer("__nvJitLinkVersion"): return False # Use nvjitlink except RuntimeError: - problem = libname() + " is not available" + therefore_not_usable + warn_txt = build_warn_txt("not available") else: - problem = libname() + " is is too old (<12.3)" + therefore_not_usable + warn_txt = build_warn_txt("too old (<12.3)") _nvjitlink = None warn( - problem + " the culink APIs will be used instead.", + warn_txt + " the culink APIs will be used instead.", stacklevel=2, category=RuntimeWarning, ) From 9220d58d22431691151ffeb1ed084df0d12adeac Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Wed, 1 Oct 2025 15:14:41 -0700 Subject: [PATCH 04/12] Change from importlib.import_module() to plain import (the latter does also raise ModuleNotFoundError) --- cuda_core/cuda/core/experimental/_linker.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cuda_core/cuda/core/experimental/_linker.py b/cuda_core/cuda/core/experimental/_linker.py index 2a011e1f7a..60a9a1ab55 100644 --- a/cuda_core/cuda/core/experimental/_linker.py +++ b/cuda_core/cuda/core/experimental/_linker.py @@ -5,7 +5,6 @@ from __future__ import annotations import ctypes -import importlib import sys import weakref from contextlib import contextmanager @@ -41,7 +40,7 @@ def _decide_nvjitlink_or_driver() -> bool: _driver_ver = (_driver_ver // 1000, (_driver_ver % 1000) // 10) try: - _nvjitlink = importlib.import_module("cuda.bindings.nvjitlink") + import cuda.bindings.nvjitlink as _nvjitlink except ModuleNotFoundError: warn_txt = "cuda.bindings.nvjitlink is not available, therefore" else: From 7e9fce44c5a2469e5263ec45ae1daa4dd94b37be Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Wed, 1 Oct 2025 21:23:20 -0700 Subject: [PATCH 05/12] Enhance to warning messages, to make them actionable. --- cuda_core/cuda/core/experimental/_linker.py | 28 ++++++++++----------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/cuda_core/cuda/core/experimental/_linker.py b/cuda_core/cuda/core/experimental/_linker.py index 60a9a1ab55..bb26197acd 100644 --- a/cuda_core/cuda/core/experimental/_linker.py +++ b/cuda_core/cuda/core/experimental/_linker.py @@ -39,33 +39,33 @@ def _decide_nvjitlink_or_driver() -> bool: _driver_ver = handle_return(driver.cuDriverGetVersion()) _driver_ver = (_driver_ver // 1000, (_driver_ver % 1000) // 10) + warn_txt_common = ( + "the culink APIs will be used instead, which do not support" + " minor version compatibility or linking LTO IRs." + " For best results, consider upgrading to a recent version of" + ) + try: import cuda.bindings.nvjitlink as _nvjitlink except ModuleNotFoundError: - warn_txt = "cuda.bindings.nvjitlink is not available, therefore" + warn_txt = f"cuda.bindings.nvjitlink is not available, therefore {warn_txt_common} cuda-bindings." else: from cuda.bindings._internal import nvjitlink as inner_nvjitlink - def build_warn_txt(detail): - return ( - f"{'nvJitLink*.dll' if sys.platform == 'win32' else 'libnvJitLink.so*'} is {detail}." - " Therefore cuda.bindings.nvjitlink is not usable and" - ) - try: if inner_nvjitlink._inspect_function_pointer("__nvJitLinkVersion"): return False # Use nvjitlink except RuntimeError: - warn_txt = build_warn_txt("not available") + warn_detail = "not available" else: - warn_txt = build_warn_txt("too old (<12.3)") + warn_detail = "too old (<12.3)" + warn_txt = ( + f"{'nvJitLink*.dll' if sys.platform == 'win32' else 'libnvJitLink.so*'} is {warn_detail}." + f" Therefore cuda.bindings.nvjitlink is not usable and the {warn_txt_common} nvJitLink." + ) _nvjitlink = None - warn( - warn_txt + " the culink APIs will be used instead.", - stacklevel=2, - category=RuntimeWarning, - ) + warn(warn_txt, stacklevel=2, category=RuntimeWarning) _driver = driver return True From 47247692d06404e8eb58f12f1ec4632bb9bd0122 Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Wed, 1 Oct 2025 21:50:18 -0700 Subject: [PATCH 06/12] Factor out _nvjitlink_has_version_symbol() for clarity and testability This aids unit testing by allowing localized stubbing of the version-symbol check, without needing to patch the full inner nvjitlink module. --- cuda_core/cuda/core/experimental/_linker.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/cuda_core/cuda/core/experimental/_linker.py b/cuda_core/cuda/core/experimental/_linker.py index bb26197acd..8613e0133e 100644 --- a/cuda_core/cuda/core/experimental/_linker.py +++ b/cuda_core/cuda/core/experimental/_linker.py @@ -29,6 +29,11 @@ _nvjitlink_input_types = None # populated if nvJitLink cannot be used +def _nvjitlink_has_version_symbol(inner_nvjitlink) -> bool: + # This condition is equivalent to testing for version >= 12.3 + return bool(inner_nvjitlink._inspect_function_pointer("__nvJitLinkVersion")) + + # Note: this function is reused in the tests def _decide_nvjitlink_or_driver() -> bool: """Returns True if falling back to the cuLink* driver APIs.""" @@ -53,7 +58,7 @@ def _decide_nvjitlink_or_driver() -> bool: from cuda.bindings._internal import nvjitlink as inner_nvjitlink try: - if inner_nvjitlink._inspect_function_pointer("__nvJitLinkVersion"): + if _nvjitlink_has_version_symbol(inner_nvjitlink): return False # Use nvjitlink except RuntimeError: warn_detail = "not available" From 6630d8ebf116cb62da0bc03a62f24f531c0526b1 Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Wed, 1 Oct 2025 22:12:59 -0700 Subject: [PATCH 07/12] Add test_linker_warnings.py As generated by ChatGPT 5, with minor manual tweaks. --- cuda_core/tests/test_linker_warnings.py | 187 ++++++++++++++++++++++++ 1 file changed, 187 insertions(+) create mode 100644 cuda_core/tests/test_linker_warnings.py diff --git a/cuda_core/tests/test_linker_warnings.py b/cuda_core/tests/test_linker_warnings.py new file mode 100644 index 0000000000..0539159dc7 --- /dev/null +++ b/cuda_core/tests/test_linker_warnings.py @@ -0,0 +1,187 @@ +# SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: LicenseRef-NVIDIA-SOFTWARE-LICENSE + +import builtins +import sys +import types +import warnings + +import pytest +from cuda.core.experimental import _linker as linker + + +@pytest.fixture(autouse=True) +def fresh_env(monkeypatch): + """ + Put the module under test into a predictable state: + - neutralize global caches, + - provide a minimal 'driver' object, + - make 'handle_return' a passthrough, + - stabilize platform for deterministic messages. + """ + + class FakeDriver: + # Something realistic but not used by the logic under test + def cuDriverGetVersion(self): + return 12090 + + monkeypatch.setattr(linker, "_driver", None, raising=False) + monkeypatch.setattr(linker, "_nvjitlink", None, raising=False) + monkeypatch.setattr(linker, "_driver_ver", None, raising=False) + monkeypatch.setattr(linker, "driver", FakeDriver(), raising=False) + monkeypatch.setattr(linker, "handle_return", lambda x: x, raising=False) + + # Normalize platform-dependent wording (if any) + monkeypatch.setattr(sys, "platform", "linux", raising=False) + + # Ensure a clean sys.modules slate for our synthetic packages + for modname in list(sys.modules): + if modname.startswith("cuda.bindings.nvjitlink") or modname == "cuda.bindings" or modname == "cuda": + sys.modules.pop(modname, None) + + yield + + # Cleanup any stubs we added + for modname in list(sys.modules): + if modname.startswith("cuda.bindings.nvjitlink") or modname == "cuda.bindings" or modname == "cuda": + sys.modules.pop(modname, None) + + +def _install_public_nvjitlink_stub(): + """ + Provide enough structure so that: + - `from cuda.bindings import nvjitlink` succeeds + - `from cuda.bindings._internal import nvjitlink as inner_nvjitlink` succeeds + We don't care about the contents of inner_nvjitlink because tests stub + `_nvjitlink_has_version_symbol()` directly. + """ + # Make 'cuda' a package + cuda_pkg = sys.modules.get("cuda") or types.ModuleType("cuda") + cuda_pkg.__path__ = [] # mark as package + sys.modules["cuda"] = cuda_pkg + + # Make 'cuda.bindings' a package + bindings_pkg = sys.modules.get("cuda.bindings") or types.ModuleType("cuda.bindings") + bindings_pkg.__path__ = [] # mark as package + sys.modules["cuda.bindings"] = bindings_pkg + + # Public-facing nvjitlink module + sys.modules["cuda.bindings.nvjitlink"] = types.ModuleType("cuda.bindings.nvjitlink") + + # Make 'cuda.bindings._internal' a package + internal_pkg = sys.modules.get("cuda.bindings._internal") or types.ModuleType("cuda.bindings._internal") + internal_pkg.__path__ = [] # mark as package + sys.modules["cuda.bindings._internal"] = internal_pkg + + # Dummy inner nvjitlink module (imported but not actually used by tests) + inner_nvjitlink_mod = types.ModuleType("cuda.bindings._internal.nvjitlink") + # (optional) a no-op placeholder so attributes exist if accessed accidentally + inner_nvjitlink_mod._inspect_function_pointer = lambda *_args, **_kw: True + sys.modules["cuda.bindings._internal.nvjitlink"] = inner_nvjitlink_mod + + +def _collect_runtime_warnings(record): + return [w for w in record if issubclass(w.category, RuntimeWarning)] + + +def _block_nvjitlink_import(monkeypatch): + """Force 'from cuda.bindings import nvjitlink' to fail, regardless of sys.path.""" + real_import = builtins.__import__ + + def fake_import(name, globals=None, locals=None, fromlist=(), level=0): + # Handle both 'from cuda.bindings import nvjitlink' and direct submodule imports + target = "cuda.bindings.nvjitlink" + if name == target or (name == "cuda.bindings" and fromlist and "nvjitlink" in fromlist): + raise ModuleNotFoundError(target) + return real_import(name, globals, locals, fromlist, level) + + monkeypatch.setattr(builtins, "__import__", fake_import) + + +def test_warns_when_python_nvjitlink_missing(monkeypatch): + """ + Case 1: 'from cuda.bindings import nvjitlink' fails -> bindings missing. + Expect a RuntimeWarning stating that cuda.bindings.nvjitlink is not available, + and that we fall back to cuLink* (function returns True). + """ + # Ensure nothing is preloaded and actively block future imports. + sys.modules.pop("cuda.bindings.nvjitlink", None) + sys.modules.pop("cuda.bindings", None) + sys.modules.pop("cuda", None) + _block_nvjitlink_import(monkeypatch) + + with warnings.catch_warnings(record=True) as rec: + warnings.simplefilter("always") + ret = linker._decide_nvjitlink_or_driver() + + assert ret is True + warns = _collect_runtime_warnings(rec) + assert len(warns) == 1 + msg = str(warns[0].message) + assert "cuda.bindings.nvjitlink is not available" in msg + assert "the culink APIs will be used instead" in msg + assert "recent version of cuda-bindings." in msg + + +def test_warns_when_nvjitlink_symbol_probe_raises(monkeypatch): + """ + Case 2: Bindings present, but symbol probe raises RuntimeError -> 'not available'. + Expect a RuntimeWarning mentioning 'libnvJitLink.so* is not available' and fallback. + """ + _install_public_nvjitlink_stub() + + def raising_probe(_inner): + raise RuntimeError("simulated: nvJitLink symbol unavailable") + + monkeypatch.setattr(linker, "_nvjitlink_has_version_symbol", raising_probe, raising=True) + + with warnings.catch_warnings(record=True) as rec: + warnings.simplefilter("always") + ret = linker._decide_nvjitlink_or_driver() + + assert ret is True + warns = _collect_runtime_warnings(rec) + assert len(warns) == 1 + msg = str(warns[0].message) + assert "libnvJitLink.so* is not available" in msg + assert "cuda.bindings.nvjitlink is not usable" in msg + assert "the culink APIs will be used instead" in msg + assert "recent version of nvJitLink." in msg + + +def test_warns_when_nvjitlink_too_old(monkeypatch): + """ + Case 3: Bindings present, probe returns False -> 'too old (<12.3)'. + Expect a RuntimeWarning mentioning 'too old (<12.3)' and fallback. + """ + _install_public_nvjitlink_stub() + monkeypatch.setattr(linker, "_nvjitlink_has_version_symbol", lambda _inner: False, raising=True) + + with warnings.catch_warnings(record=True) as rec: + warnings.simplefilter("always") + ret = linker._decide_nvjitlink_or_driver() + + assert ret is True + warns = _collect_runtime_warnings(rec) + assert len(warns) == 1 + msg = str(warns[0].message) + assert "libnvJitLink.so* is too old (<12.3)" in msg + assert "cuda.bindings.nvjitlink is not usable" in msg + assert "the culink APIs will be used instead" in msg + assert "recent version of nvJitLink." in msg + + +def test_uses_nvjitlink_when_available_and_ok(monkeypatch): + """ + Sanity: Bindings present and probe returns True → no warning, use nvJitLink. + """ + _install_public_nvjitlink_stub() + monkeypatch.setattr(linker, "_nvjitlink_has_version_symbol", lambda _inner: True, raising=True) + + with warnings.catch_warnings(record=True) as rec: + warnings.simplefilter("always") + ret = linker._decide_nvjitlink_or_driver() + + assert ret is False # do NOT fall back + warns = _collect_runtime_warnings(rec) + assert not warns From f1c29f684314875370fefbd467de0cba7d75c104 Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Thu, 2 Oct 2025 11:06:33 -0700 Subject: [PATCH 08/12] Fix "the the" oversight --- cuda_core/cuda/core/experimental/_linker.py | 2 +- cuda_core/tests/test_linker_warnings.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/cuda_core/cuda/core/experimental/_linker.py b/cuda_core/cuda/core/experimental/_linker.py index 8613e0133e..3eaa83c60f 100644 --- a/cuda_core/cuda/core/experimental/_linker.py +++ b/cuda_core/cuda/core/experimental/_linker.py @@ -66,7 +66,7 @@ def _decide_nvjitlink_or_driver() -> bool: warn_detail = "too old (<12.3)" warn_txt = ( f"{'nvJitLink*.dll' if sys.platform == 'win32' else 'libnvJitLink.so*'} is {warn_detail}." - f" Therefore cuda.bindings.nvjitlink is not usable and the {warn_txt_common} nvJitLink." + f" Therefore cuda.bindings.nvjitlink is not usable and {warn_txt_common} nvJitLink." ) _nvjitlink = None diff --git a/cuda_core/tests/test_linker_warnings.py b/cuda_core/tests/test_linker_warnings.py index 0539159dc7..49a57e91fe 100644 --- a/cuda_core/tests/test_linker_warnings.py +++ b/cuda_core/tests/test_linker_warnings.py @@ -119,7 +119,7 @@ def test_warns_when_python_nvjitlink_missing(monkeypatch): assert len(warns) == 1 msg = str(warns[0].message) assert "cuda.bindings.nvjitlink is not available" in msg - assert "the culink APIs will be used instead" in msg + assert "therefore the culink APIs will be used instead" in msg assert "recent version of cuda-bindings." in msg @@ -145,7 +145,7 @@ def raising_probe(_inner): msg = str(warns[0].message) assert "libnvJitLink.so* is not available" in msg assert "cuda.bindings.nvjitlink is not usable" in msg - assert "the culink APIs will be used instead" in msg + assert "and the culink APIs will be used instead" in msg assert "recent version of nvJitLink." in msg @@ -167,7 +167,7 @@ def test_warns_when_nvjitlink_too_old(monkeypatch): msg = str(warns[0].message) assert "libnvJitLink.so* is too old (<12.3)" in msg assert "cuda.bindings.nvjitlink is not usable" in msg - assert "the culink APIs will be used instead" in msg + assert "and the culink APIs will be used instead" in msg assert "recent version of nvJitLink." in msg From 09489423db8a31dce8b6431d51a8d09cbee30c98 Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Thu, 2 Oct 2025 16:20:22 -0700 Subject: [PATCH 09/12] =?UTF-8?q?Replace=20"culink=20APIs"=20=E2=86=92=20"?= =?UTF-8?q?driver=20APIs"=20in=20warning=20message.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cuda_core/cuda/core/experimental/_linker.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cuda_core/cuda/core/experimental/_linker.py b/cuda_core/cuda/core/experimental/_linker.py index 3eaa83c60f..2e2dedc4c4 100644 --- a/cuda_core/cuda/core/experimental/_linker.py +++ b/cuda_core/cuda/core/experimental/_linker.py @@ -45,7 +45,7 @@ def _decide_nvjitlink_or_driver() -> bool: _driver_ver = (_driver_ver // 1000, (_driver_ver % 1000) // 10) warn_txt_common = ( - "the culink APIs will be used instead, which do not support" + "the driver APIs will be used instead, which do not support" " minor version compatibility or linking LTO IRs." " For best results, consider upgrading to a recent version of" ) From b2376697d054da353f9931ed0782e7cc7e2d7859 Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Fri, 3 Oct 2025 09:40:58 -0700 Subject: [PATCH 10/12] Fix oversight: test_linker_warnings.py needs to be updated after commit 09489423db8a31dce8b6431d51a8d09cbee30c98 --- cuda_core/tests/test_linker_warnings.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cuda_core/tests/test_linker_warnings.py b/cuda_core/tests/test_linker_warnings.py index 49a57e91fe..2fbe094028 100644 --- a/cuda_core/tests/test_linker_warnings.py +++ b/cuda_core/tests/test_linker_warnings.py @@ -102,7 +102,7 @@ def test_warns_when_python_nvjitlink_missing(monkeypatch): """ Case 1: 'from cuda.bindings import nvjitlink' fails -> bindings missing. Expect a RuntimeWarning stating that cuda.bindings.nvjitlink is not available, - and that we fall back to cuLink* (function returns True). + and that we fall back to driver APIs (function returns True). """ # Ensure nothing is preloaded and actively block future imports. sys.modules.pop("cuda.bindings.nvjitlink", None) @@ -119,7 +119,7 @@ def test_warns_when_python_nvjitlink_missing(monkeypatch): assert len(warns) == 1 msg = str(warns[0].message) assert "cuda.bindings.nvjitlink is not available" in msg - assert "therefore the culink APIs will be used instead" in msg + assert "therefore the driver APIs will be used instead" in msg assert "recent version of cuda-bindings." in msg @@ -145,7 +145,7 @@ def raising_probe(_inner): msg = str(warns[0].message) assert "libnvJitLink.so* is not available" in msg assert "cuda.bindings.nvjitlink is not usable" in msg - assert "and the culink APIs will be used instead" in msg + assert "and the driver APIs will be used instead" in msg assert "recent version of nvJitLink." in msg @@ -167,7 +167,7 @@ def test_warns_when_nvjitlink_too_old(monkeypatch): msg = str(warns[0].message) assert "libnvJitLink.so* is too old (<12.3)" in msg assert "cuda.bindings.nvjitlink is not usable" in msg - assert "and the culink APIs will be used instead" in msg + assert "and the driver APIs will be used instead" in msg assert "recent version of nvJitLink." in msg From 4dc08e73f91ed2e81534bb9e5a30c42ad87e1f13 Mon Sep 17 00:00:00 2001 From: Leo Fang Date: Fri, 3 Oct 2025 20:00:27 -0400 Subject: [PATCH 11/12] fix skipping the check for nvidia-smi (#1084) --- cuda_core/build_hooks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cuda_core/build_hooks.py b/cuda_core/build_hooks.py index 7c5fd4672a..6a55b6edd3 100644 --- a/cuda_core/build_hooks.py +++ b/cuda_core/build_hooks.py @@ -45,7 +45,7 @@ def _get_proper_cuda_bindings_major_version() -> str: m = re.search(r"CUDA Version:\s*([\d\.]+)", out.stdout.decode()) if m: return m.group(1).split(".")[0] - except FileNotFoundError: + except (FileNotFoundError, subprocess.CalledProcessError): # the build machine has no driver installed pass From 5ba11be295cd6ab944decbe67f0d5ab19eb8bf7f Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Tue, 7 Oct 2025 14:48:50 -0700 Subject: [PATCH 12/12] rm cuda_core/tests/test_linker_warnings.py: see https://github.com/NVIDIA/cuda-python/issues/1095 --- cuda_core/tests/test_linker_warnings.py | 187 ------------------------ 1 file changed, 187 deletions(-) delete mode 100644 cuda_core/tests/test_linker_warnings.py diff --git a/cuda_core/tests/test_linker_warnings.py b/cuda_core/tests/test_linker_warnings.py deleted file mode 100644 index 2fbe094028..0000000000 --- a/cuda_core/tests/test_linker_warnings.py +++ /dev/null @@ -1,187 +0,0 @@ -# SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# SPDX-License-Identifier: LicenseRef-NVIDIA-SOFTWARE-LICENSE - -import builtins -import sys -import types -import warnings - -import pytest -from cuda.core.experimental import _linker as linker - - -@pytest.fixture(autouse=True) -def fresh_env(monkeypatch): - """ - Put the module under test into a predictable state: - - neutralize global caches, - - provide a minimal 'driver' object, - - make 'handle_return' a passthrough, - - stabilize platform for deterministic messages. - """ - - class FakeDriver: - # Something realistic but not used by the logic under test - def cuDriverGetVersion(self): - return 12090 - - monkeypatch.setattr(linker, "_driver", None, raising=False) - monkeypatch.setattr(linker, "_nvjitlink", None, raising=False) - monkeypatch.setattr(linker, "_driver_ver", None, raising=False) - monkeypatch.setattr(linker, "driver", FakeDriver(), raising=False) - monkeypatch.setattr(linker, "handle_return", lambda x: x, raising=False) - - # Normalize platform-dependent wording (if any) - monkeypatch.setattr(sys, "platform", "linux", raising=False) - - # Ensure a clean sys.modules slate for our synthetic packages - for modname in list(sys.modules): - if modname.startswith("cuda.bindings.nvjitlink") or modname == "cuda.bindings" or modname == "cuda": - sys.modules.pop(modname, None) - - yield - - # Cleanup any stubs we added - for modname in list(sys.modules): - if modname.startswith("cuda.bindings.nvjitlink") or modname == "cuda.bindings" or modname == "cuda": - sys.modules.pop(modname, None) - - -def _install_public_nvjitlink_stub(): - """ - Provide enough structure so that: - - `from cuda.bindings import nvjitlink` succeeds - - `from cuda.bindings._internal import nvjitlink as inner_nvjitlink` succeeds - We don't care about the contents of inner_nvjitlink because tests stub - `_nvjitlink_has_version_symbol()` directly. - """ - # Make 'cuda' a package - cuda_pkg = sys.modules.get("cuda") or types.ModuleType("cuda") - cuda_pkg.__path__ = [] # mark as package - sys.modules["cuda"] = cuda_pkg - - # Make 'cuda.bindings' a package - bindings_pkg = sys.modules.get("cuda.bindings") or types.ModuleType("cuda.bindings") - bindings_pkg.__path__ = [] # mark as package - sys.modules["cuda.bindings"] = bindings_pkg - - # Public-facing nvjitlink module - sys.modules["cuda.bindings.nvjitlink"] = types.ModuleType("cuda.bindings.nvjitlink") - - # Make 'cuda.bindings._internal' a package - internal_pkg = sys.modules.get("cuda.bindings._internal") or types.ModuleType("cuda.bindings._internal") - internal_pkg.__path__ = [] # mark as package - sys.modules["cuda.bindings._internal"] = internal_pkg - - # Dummy inner nvjitlink module (imported but not actually used by tests) - inner_nvjitlink_mod = types.ModuleType("cuda.bindings._internal.nvjitlink") - # (optional) a no-op placeholder so attributes exist if accessed accidentally - inner_nvjitlink_mod._inspect_function_pointer = lambda *_args, **_kw: True - sys.modules["cuda.bindings._internal.nvjitlink"] = inner_nvjitlink_mod - - -def _collect_runtime_warnings(record): - return [w for w in record if issubclass(w.category, RuntimeWarning)] - - -def _block_nvjitlink_import(monkeypatch): - """Force 'from cuda.bindings import nvjitlink' to fail, regardless of sys.path.""" - real_import = builtins.__import__ - - def fake_import(name, globals=None, locals=None, fromlist=(), level=0): - # Handle both 'from cuda.bindings import nvjitlink' and direct submodule imports - target = "cuda.bindings.nvjitlink" - if name == target or (name == "cuda.bindings" and fromlist and "nvjitlink" in fromlist): - raise ModuleNotFoundError(target) - return real_import(name, globals, locals, fromlist, level) - - monkeypatch.setattr(builtins, "__import__", fake_import) - - -def test_warns_when_python_nvjitlink_missing(monkeypatch): - """ - Case 1: 'from cuda.bindings import nvjitlink' fails -> bindings missing. - Expect a RuntimeWarning stating that cuda.bindings.nvjitlink is not available, - and that we fall back to driver APIs (function returns True). - """ - # Ensure nothing is preloaded and actively block future imports. - sys.modules.pop("cuda.bindings.nvjitlink", None) - sys.modules.pop("cuda.bindings", None) - sys.modules.pop("cuda", None) - _block_nvjitlink_import(monkeypatch) - - with warnings.catch_warnings(record=True) as rec: - warnings.simplefilter("always") - ret = linker._decide_nvjitlink_or_driver() - - assert ret is True - warns = _collect_runtime_warnings(rec) - assert len(warns) == 1 - msg = str(warns[0].message) - assert "cuda.bindings.nvjitlink is not available" in msg - assert "therefore the driver APIs will be used instead" in msg - assert "recent version of cuda-bindings." in msg - - -def test_warns_when_nvjitlink_symbol_probe_raises(monkeypatch): - """ - Case 2: Bindings present, but symbol probe raises RuntimeError -> 'not available'. - Expect a RuntimeWarning mentioning 'libnvJitLink.so* is not available' and fallback. - """ - _install_public_nvjitlink_stub() - - def raising_probe(_inner): - raise RuntimeError("simulated: nvJitLink symbol unavailable") - - monkeypatch.setattr(linker, "_nvjitlink_has_version_symbol", raising_probe, raising=True) - - with warnings.catch_warnings(record=True) as rec: - warnings.simplefilter("always") - ret = linker._decide_nvjitlink_or_driver() - - assert ret is True - warns = _collect_runtime_warnings(rec) - assert len(warns) == 1 - msg = str(warns[0].message) - assert "libnvJitLink.so* is not available" in msg - assert "cuda.bindings.nvjitlink is not usable" in msg - assert "and the driver APIs will be used instead" in msg - assert "recent version of nvJitLink." in msg - - -def test_warns_when_nvjitlink_too_old(monkeypatch): - """ - Case 3: Bindings present, probe returns False -> 'too old (<12.3)'. - Expect a RuntimeWarning mentioning 'too old (<12.3)' and fallback. - """ - _install_public_nvjitlink_stub() - monkeypatch.setattr(linker, "_nvjitlink_has_version_symbol", lambda _inner: False, raising=True) - - with warnings.catch_warnings(record=True) as rec: - warnings.simplefilter("always") - ret = linker._decide_nvjitlink_or_driver() - - assert ret is True - warns = _collect_runtime_warnings(rec) - assert len(warns) == 1 - msg = str(warns[0].message) - assert "libnvJitLink.so* is too old (<12.3)" in msg - assert "cuda.bindings.nvjitlink is not usable" in msg - assert "and the driver APIs will be used instead" in msg - assert "recent version of nvJitLink." in msg - - -def test_uses_nvjitlink_when_available_and_ok(monkeypatch): - """ - Sanity: Bindings present and probe returns True → no warning, use nvJitLink. - """ - _install_public_nvjitlink_stub() - monkeypatch.setattr(linker, "_nvjitlink_has_version_symbol", lambda _inner: True, raising=True) - - with warnings.catch_warnings(record=True) as rec: - warnings.simplefilter("always") - ret = linker._decide_nvjitlink_or_driver() - - assert ret is False # do NOT fall back - warns = _collect_runtime_warnings(rec) - assert not warns