From 50bf893fe0036ee2173ece6138b648c85250db8e Mon Sep 17 00:00:00 2001 From: Omar Atie Date: Thu, 23 Jul 2026 16:06:57 -0700 Subject: [PATCH 1/6] fix(cuda.core): fall back to driver when nvJitLink < 12.3 is installed Stop probing nvJitLink availability via module.version(), which calls the unversioned nvJitLinkVersion symbol missing in nvJitLink 12.0-12.2. Use symbol pointer inspection via _nvjitlink_has_version_symbol() instead, restoring cuda-core 0.6.0 fallback behavior. Fixes #2408 Signed-off-by: Omar Atie Co-authored-by: Cursor --- cuda_core/cuda/core/_linker.pyx | 8 ++--- .../tests/test_optional_dependency_imports.py | 35 +++++++++++++++++-- 2 files changed, 37 insertions(+), 6 deletions(-) diff --git a/cuda_core/cuda/core/_linker.pyx b/cuda_core/cuda/core/_linker.pyx index 39b1f010a9e..f7f8e06b218 100644 --- a/cuda_core/cuda/core/_linker.pyx +++ b/cuda_core/cuda/core/_linker.pyx @@ -683,10 +683,10 @@ def _decide_nvjitlink_or_driver() -> bool: " For best results, consider upgrading to a recent version of" ) - nvjitlink_module = _optional_cuda_import( - "cuda.bindings.nvjitlink", - probe_function=lambda module: module.version(), # probe triggers nvJitLink runtime load - ) + # Do not probe via module.version(): nvJitLink 12.0-12.2 lacks the unversioned + # nvJitLinkVersion symbol, so calling version() raises FunctionNotFoundError. + # Use _nvjitlink_has_version_symbol() below instead (symbol pointer inspection). + nvjitlink_module = _optional_cuda_import("cuda.bindings.nvjitlink") if nvjitlink_module is None: warn_txt = f"cuda.bindings.nvjitlink is not available, therefore {warn_txt_common} cuda-bindings." else: diff --git a/cuda_core/tests/test_optional_dependency_imports.py b/cuda_core/tests/test_optional_dependency_imports.py index 02edcc9839a..4ec58c9fa2a 100644 --- a/cuda_core/tests/test_optional_dependency_imports.py +++ b/cuda_core/tests/test_optional_dependency_imports.py @@ -78,7 +78,7 @@ def fake__optional_cuda_import(modname, probe_function=None): def test_decide_nvjitlink_or_driver_reraises_nested_module_not_found(monkeypatch): def fake__optional_cuda_import(modname, probe_function=None): assert modname == "cuda.bindings.nvjitlink" - assert probe_function is not None + assert probe_function is None err = ModuleNotFoundError("No module named 'not_a_real_dependency'") err.name = "not_a_real_dependency" raise err @@ -93,7 +93,7 @@ def fake__optional_cuda_import(modname, probe_function=None): def test_decide_nvjitlink_or_driver_falls_back_when_module_missing(monkeypatch): def fake__optional_cuda_import(modname, probe_function=None): assert modname == "cuda.bindings.nvjitlink" - assert probe_function is not None + assert probe_function is None return None monkeypatch.setattr(_linker, "_optional_cuda_import", fake__optional_cuda_import) @@ -103,3 +103,34 @@ def fake__optional_cuda_import(modname, probe_function=None): assert use_driver_backend is True assert _linker._use_nvjitlink_backend is False + + +def test_decide_nvjitlink_or_driver_falls_back_when_nvjitlink_too_old(monkeypatch): + def fake__optional_cuda_import(modname, probe_function=None): + assert modname == "cuda.bindings.nvjitlink" + assert probe_function is None + return object() + + monkeypatch.setattr(_linker, "_optional_cuda_import", fake__optional_cuda_import) + monkeypatch.setattr(_linker, "_nvjitlink_has_version_symbol", lambda _nvjitlink: False) + + with pytest.warns(RuntimeWarning, match="too old \\(<12.3\\)"): + use_driver_backend = _linker._decide_nvjitlink_or_driver() + + assert use_driver_backend is True + assert _linker._use_nvjitlink_backend is False + + +def test_decide_nvjitlink_or_driver_selects_nvjitlink_when_version_symbol_present(monkeypatch): + def fake__optional_cuda_import(modname, probe_function=None): + assert modname == "cuda.bindings.nvjitlink" + assert probe_function is None + return object() + + monkeypatch.setattr(_linker, "_optional_cuda_import", fake__optional_cuda_import) + monkeypatch.setattr(_linker, "_nvjitlink_has_version_symbol", lambda _nvjitlink: True) + + use_driver_backend = _linker._decide_nvjitlink_or_driver() + + assert use_driver_backend is False + assert _linker._use_nvjitlink_backend is True From 20dd8e160177ceae35f93b446ac8bd22e167ef23 Mon Sep 17 00:00:00 2001 From: Omar Atie Date: Thu, 23 Jul 2026 16:06:57 -0700 Subject: [PATCH 2/6] test(cuda.core): add coverage for nvJitLink <12.3 driver fallback Add regression tests for Linker.which_backend() and _decide_nvjitlink_or_driver() when the nvJitLinkVersion symbol is missing (nvJitLink 12.0-12.2). Related to #2408 Signed-off-by: Omar Atie Co-authored-by: Cursor --- cuda_core/tests/test_linker.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/cuda_core/tests/test_linker.py b/cuda_core/tests/test_linker.py index 9d95b5fd9c3..a8dbce7bcc5 100644 --- a/cuda_core/tests/test_linker.py +++ b/cuda_core/tests/test_linker.py @@ -303,6 +303,24 @@ def fake_decide(): assert result == "nvJitLink" assert called, "_decide_nvjitlink_or_driver was not called" + def test_which_backend_falls_back_when_nvjitlink_too_old(self, monkeypatch): + """Regression test for #2408: old nvJitLink must not crash which_backend().""" + monkeypatch.setattr(_linker, "_use_nvjitlink_backend", None) + monkeypatch.setattr(_linker, "_driver", None) + + def fake__optional_cuda_import(modname, probe_function=None): + assert modname == "cuda.bindings.nvjitlink" + assert probe_function is None + return object() + + monkeypatch.setattr(_linker, "_optional_cuda_import", fake__optional_cuda_import) + monkeypatch.setattr(_linker, "_nvjitlink_has_version_symbol", lambda _nvjitlink: False) + + with pytest.warns(RuntimeWarning, match="too old \\(<12.3\\)"): + assert Linker.which_backend() == "driver" + + assert _linker._use_nvjitlink_backend is False + def test_which_backend_is_classmethod(self): attr = inspect.getattr_static(Linker, "which_backend") assert isinstance(attr, classmethod) From 68b7e896ca5aa29a0fa0c2a8bb05cb9d8b0966f9 Mon Sep 17 00:00:00 2001 From: Omar Atie Date: Thu, 23 Jul 2026 17:46:28 -0700 Subject: [PATCH 3/6] docs(cuda.core): add 1.2.0 release note for nvJitLink <12.3 fallback fix Document the #2408 regression fix in the cuda.core 1.2.0 release notes. Related to #2408 Signed-off-by: Omar Atie Co-authored-by: Cursor --- cuda_core/docs/source/release/1.2.0-notes.rst | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/cuda_core/docs/source/release/1.2.0-notes.rst b/cuda_core/docs/source/release/1.2.0-notes.rst index de255d01cbb..cf884ed64cc 100644 --- a/cuda_core/docs/source/release/1.2.0-notes.rst +++ b/cuda_core/docs/source/release/1.2.0-notes.rst @@ -25,6 +25,15 @@ Fixes and enhancements versions 12.2 or newer. (`#2352 `__) +- :meth:`Linker.which_backend` and constructing a :class:`Linker` no longer + raise ``FunctionNotFoundError`` when an nvJitLink older than 12.3 + (12.0–12.2) is installed. These versions do not export the unversioned + ``nvJitLinkVersion`` symbol, so probing the version crashed instead of + falling back. ``cuda.core`` now warns and falls back to the driver + (``cuLink``) backend, restoring the pre-0.7.0 behavior. + (`#2409 `__, + closes `#2408 `__) + Deprecation Notices ------------------- From 2ddd4951f6ce2508a1cb1626ddfcb12bf8c5cda4 Mon Sep 17 00:00:00 2001 From: Omar Atie Date: Wed, 29 Jul 2026 20:13:25 -0700 Subject: [PATCH 4/6] fix(cuda.core): probe nvJitLink version under DynamicLibNotFoundError guard Address review feedback: keep the >=12.3 version-symbol check inside _optional_cuda_import's probe so a missing nvJitLink dylib still falls back to cuLink. Continue avoiding module.version(), which raises FunctionNotFoundError on nvJitLink 12.0-12.2 (#2408). Add coverage for missing-dylib fallback and a guard that the probe does not call module.version(). Signed-off-by: Omar Atie Co-authored-by: Cursor --- cuda_core/cuda/core/_linker.pyx | 30 ++++++--- cuda_core/tests/test_linker.py | 30 ++++++++- .../tests/test_optional_dependency_imports.py | 64 +++++++++++++++++-- 3 files changed, 109 insertions(+), 15 deletions(-) diff --git a/cuda_core/cuda/core/_linker.pyx b/cuda_core/cuda/core/_linker.pyx index f7f8e06b218..929404d2d38 100644 --- a/cuda_core/cuda/core/_linker.pyx +++ b/cuda_core/cuda/core/_linker.pyx @@ -683,22 +683,32 @@ def _decide_nvjitlink_or_driver() -> bool: " For best results, consider upgrading to a recent version of" ) - # Do not probe via module.version(): nvJitLink 12.0-12.2 lacks the unversioned - # nvJitLinkVersion symbol, so calling version() raises FunctionNotFoundError. - # Use _nvjitlink_has_version_symbol() below instead (symbol pointer inspection). - nvjitlink_module = _optional_cuda_import("cuda.bindings.nvjitlink") - if nvjitlink_module is None: - warn_txt = f"cuda.bindings.nvjitlink is not available, therefore {warn_txt_common} cuda-bindings." - else: + # Probe via symbol-pointer inspection (not module.version()): + # - Triggers nvJitLink dylib load; DynamicLibNotFoundError is caught by + # _optional_cuda_import so a missing dylib falls back to cuLink. + # - Avoids FunctionNotFoundError on nvJitLink 12.0-12.2, which lack the + # unversioned nvJitLinkVersion export added in 12.3 (#2408). + has_version_symbol = [False] + + def _probe_nvjitlink(module): from cuda.bindings._internal import nvjitlink - if _nvjitlink_has_version_symbol(nvjitlink): - _use_nvjitlink_backend = True - return False # Use nvjitlink + has_version_symbol[0] = _nvjitlink_has_version_symbol(nvjitlink) + + nvjitlink_module = _optional_cuda_import( + "cuda.bindings.nvjitlink", + probe_function=_probe_nvjitlink, + ) + if nvjitlink_module is None: + warn_txt = f"cuda.bindings.nvjitlink is not available, therefore {warn_txt_common} cuda-bindings." + elif not has_version_symbol[0]: warn_txt = ( f"{'nvJitLink*.dll' if sys.platform == 'win32' else 'libnvJitLink.so*'} is too old (<12.3)." f" Therefore cuda.bindings.nvjitlink is not usable and {warn_txt_common} nvJitLink." ) + else: + _use_nvjitlink_backend = True + return False # Use nvjitlink warn(warn_txt, stacklevel=2, category=RuntimeWarning) _use_nvjitlink_backend = False diff --git a/cuda_core/tests/test_linker.py b/cuda_core/tests/test_linker.py index a8dbce7bcc5..f73c9178dd9 100644 --- a/cuda_core/tests/test_linker.py +++ b/cuda_core/tests/test_linker.py @@ -310,7 +310,8 @@ def test_which_backend_falls_back_when_nvjitlink_too_old(self, monkeypatch): def fake__optional_cuda_import(modname, probe_function=None): assert modname == "cuda.bindings.nvjitlink" - assert probe_function is None + assert probe_function is not None + probe_function(object()) return object() monkeypatch.setattr(_linker, "_optional_cuda_import", fake__optional_cuda_import) @@ -321,6 +322,33 @@ def fake__optional_cuda_import(modname, probe_function=None): assert _linker._use_nvjitlink_backend is False + def test_which_backend_falls_back_when_dylib_missing(self, monkeypatch): + """Missing nvJitLink dylib must fall back without raising.""" + from cuda.pathfinder import DynamicLibNotFoundError + + monkeypatch.setattr(_linker, "_use_nvjitlink_backend", None) + monkeypatch.setattr(_linker, "_driver", None) + + def raise_missing(_nvjitlink): + raise DynamicLibNotFoundError("missing") + + def fake__optional_cuda_import(modname, probe_function=None): + assert modname == "cuda.bindings.nvjitlink" + assert probe_function is not None + try: + probe_function(object()) + except DynamicLibNotFoundError: + return None + return object() + + monkeypatch.setattr(_linker, "_nvjitlink_has_version_symbol", raise_missing) + monkeypatch.setattr(_linker, "_optional_cuda_import", fake__optional_cuda_import) + + with pytest.warns(RuntimeWarning, match="cuda.bindings.nvjitlink is not available"): + assert Linker.which_backend() == "driver" + + assert _linker._use_nvjitlink_backend is False + def test_which_backend_is_classmethod(self): attr = inspect.getattr_static(Linker, "which_backend") assert isinstance(attr, classmethod) diff --git a/cuda_core/tests/test_optional_dependency_imports.py b/cuda_core/tests/test_optional_dependency_imports.py index 4ec58c9fa2a..c0875b88c53 100644 --- a/cuda_core/tests/test_optional_dependency_imports.py +++ b/cuda_core/tests/test_optional_dependency_imports.py @@ -5,6 +5,7 @@ import pytest from cuda.core import _linker, _program +from cuda.pathfinder import DynamicLibNotFoundError @pytest.fixture(autouse=True) @@ -78,7 +79,7 @@ def fake__optional_cuda_import(modname, probe_function=None): def test_decide_nvjitlink_or_driver_reraises_nested_module_not_found(monkeypatch): def fake__optional_cuda_import(modname, probe_function=None): assert modname == "cuda.bindings.nvjitlink" - assert probe_function is None + assert probe_function is not None err = ModuleNotFoundError("No module named 'not_a_real_dependency'") err.name = "not_a_real_dependency" raise err @@ -93,7 +94,7 @@ def fake__optional_cuda_import(modname, probe_function=None): def test_decide_nvjitlink_or_driver_falls_back_when_module_missing(monkeypatch): def fake__optional_cuda_import(modname, probe_function=None): assert modname == "cuda.bindings.nvjitlink" - assert probe_function is None + assert probe_function is not None return None monkeypatch.setattr(_linker, "_optional_cuda_import", fake__optional_cuda_import) @@ -105,10 +106,37 @@ def fake__optional_cuda_import(modname, probe_function=None): assert _linker._use_nvjitlink_backend is False +def test_decide_nvjitlink_or_driver_falls_back_when_dylib_missing(monkeypatch): + """Missing nvJitLink dylib must fall back via DynamicLibNotFoundError in probe.""" + + def raise_missing(_nvjitlink): + raise DynamicLibNotFoundError("libnvJitLink missing") + + def fake__optional_cuda_import(modname, probe_function=None): + assert modname == "cuda.bindings.nvjitlink" + assert probe_function is not None + try: + # Faithfully simulate _optional_cuda_import's catch of DynamicLibNotFoundError. + probe_function(object()) + except DynamicLibNotFoundError: + return None + return object() + + monkeypatch.setattr(_linker, "_nvjitlink_has_version_symbol", raise_missing) + monkeypatch.setattr(_linker, "_optional_cuda_import", fake__optional_cuda_import) + + with pytest.warns(RuntimeWarning, match="cuda.bindings.nvjitlink is not available"): + use_driver_backend = _linker._decide_nvjitlink_or_driver() + + assert use_driver_backend is True + assert _linker._use_nvjitlink_backend is False + + def test_decide_nvjitlink_or_driver_falls_back_when_nvjitlink_too_old(monkeypatch): def fake__optional_cuda_import(modname, probe_function=None): assert modname == "cuda.bindings.nvjitlink" - assert probe_function is None + assert probe_function is not None + probe_function(object()) return object() monkeypatch.setattr(_linker, "_optional_cuda_import", fake__optional_cuda_import) @@ -124,7 +152,8 @@ def fake__optional_cuda_import(modname, probe_function=None): def test_decide_nvjitlink_or_driver_selects_nvjitlink_when_version_symbol_present(monkeypatch): def fake__optional_cuda_import(modname, probe_function=None): assert modname == "cuda.bindings.nvjitlink" - assert probe_function is None + assert probe_function is not None + probe_function(object()) return object() monkeypatch.setattr(_linker, "_optional_cuda_import", fake__optional_cuda_import) @@ -134,3 +163,30 @@ def fake__optional_cuda_import(modname, probe_function=None): assert use_driver_backend is False assert _linker._use_nvjitlink_backend is True + + +def test_decide_nvjitlink_or_driver_probe_does_not_call_version(monkeypatch): + """Regression guard for #2408: probe must not call module.version().""" + called = {"version": False, "inspect": False} + + class FakeModule: + def version(self): + called["version"] = True + raise AssertionError("module.version() must not be used for nvJitLink probing") + + def fake_has_version(_nvjitlink): + called["inspect"] = True + return True + + def fake__optional_cuda_import(modname, probe_function=None): + assert modname == "cuda.bindings.nvjitlink" + assert probe_function is not None + probe_function(FakeModule()) + return FakeModule() + + monkeypatch.setattr(_linker, "_optional_cuda_import", fake__optional_cuda_import) + monkeypatch.setattr(_linker, "_nvjitlink_has_version_symbol", fake_has_version) + + assert _linker._decide_nvjitlink_or_driver() is False + assert called["inspect"] is True + assert called["version"] is False From 210c7a2cf2ee3e8857698ed4f2373d22aa74c491 Mon Sep 17 00:00:00 2001 From: Omar Atie Date: Thu, 30 Jul 2026 15:45:49 -0700 Subject: [PATCH 5/6] fix(cuda.core): use explicit try/except for nvJitLink version probe Address review feedback: drop the probe side-effect and catch DynamicLibNotFoundError around _nvjitlink_has_version_symbol so missing dylibs still fall back to cuLink. Keep avoiding module.version() for nvJitLink <12.3 (#2408). Mark newly added tests with agent_authored authorship markers. Signed-off-by: Omar Atie Co-authored-by: Cursor --- cuda_core/cuda/core/_linker.pyx | 45 +++++++++---------- cuda_core/tests/test_linker.py | 11 ++--- .../tests/test_optional_dependency_imports.py | 32 ++++++------- 3 files changed, 40 insertions(+), 48 deletions(-) diff --git a/cuda_core/cuda/core/_linker.pyx b/cuda_core/cuda/core/_linker.pyx index 929404d2d38..9a62e2dfb82 100644 --- a/cuda_core/cuda/core/_linker.pyx +++ b/cuda_core/cuda/core/_linker.pyx @@ -29,6 +29,7 @@ from dataclasses import dataclass from typing import TYPE_CHECKING, Union from warnings import warn +from cuda.pathfinder import DynamicLibNotFoundError from cuda.pathfinder._optional_cuda_import import _optional_cuda_import from cuda.core._device import Device from cuda.core._module import ObjectCode @@ -683,32 +684,30 @@ def _decide_nvjitlink_or_driver() -> bool: " For best results, consider upgrading to a recent version of" ) - # Probe via symbol-pointer inspection (not module.version()): - # - Triggers nvJitLink dylib load; DynamicLibNotFoundError is caught by - # _optional_cuda_import so a missing dylib falls back to cuLink. - # - Avoids FunctionNotFoundError on nvJitLink 12.0-12.2, which lack the - # unversioned nvJitLinkVersion export added in 12.3 (#2408). - has_version_symbol = [False] - - def _probe_nvjitlink(module): - from cuda.bindings._internal import nvjitlink - - has_version_symbol[0] = _nvjitlink_has_version_symbol(nvjitlink) - - nvjitlink_module = _optional_cuda_import( - "cuda.bindings.nvjitlink", - probe_function=_probe_nvjitlink, - ) + # Do not call module.version(): nvJitLink 12.0-12.2 lack the unversioned + # nvJitLinkVersion export, so version() raises FunctionNotFoundError (#2408). + # Inspect the symbol pointer instead, and catch DynamicLibNotFoundError when + # the dylib is missing so we can fall back to cuLink. + nvjitlink_module = _optional_cuda_import("cuda.bindings.nvjitlink") if nvjitlink_module is None: warn_txt = f"cuda.bindings.nvjitlink is not available, therefore {warn_txt_common} cuda-bindings." - elif not has_version_symbol[0]: - warn_txt = ( - f"{'nvJitLink*.dll' if sys.platform == 'win32' else 'libnvJitLink.so*'} is too old (<12.3)." - f" Therefore cuda.bindings.nvjitlink is not usable and {warn_txt_common} nvJitLink." - ) else: - _use_nvjitlink_backend = True - return False # Use nvjitlink + from cuda.bindings._internal import nvjitlink + + try: + has_version_symbol = _nvjitlink_has_version_symbol(nvjitlink) + except DynamicLibNotFoundError: + warn_txt = ( + f"cuda.bindings.nvjitlink is not available, therefore {warn_txt_common} cuda-bindings." + ) + else: + if has_version_symbol: + _use_nvjitlink_backend = True + return False # Use nvjitlink + warn_txt = ( + f"{'nvJitLink*.dll' if sys.platform == 'win32' else 'libnvJitLink.so*'} is too old (<12.3)." + f" Therefore cuda.bindings.nvjitlink is not usable and {warn_txt_common} nvJitLink." + ) warn(warn_txt, stacklevel=2, category=RuntimeWarning) _use_nvjitlink_backend = False diff --git a/cuda_core/tests/test_linker.py b/cuda_core/tests/test_linker.py index f73c9178dd9..4f4433a1a1a 100644 --- a/cuda_core/tests/test_linker.py +++ b/cuda_core/tests/test_linker.py @@ -303,6 +303,7 @@ def fake_decide(): assert result == "nvJitLink" assert called, "_decide_nvjitlink_or_driver was not called" + @pytest.mark.agent_authored(model="grok-4.5") def test_which_backend_falls_back_when_nvjitlink_too_old(self, monkeypatch): """Regression test for #2408: old nvJitLink must not crash which_backend().""" monkeypatch.setattr(_linker, "_use_nvjitlink_backend", None) @@ -310,8 +311,7 @@ def test_which_backend_falls_back_when_nvjitlink_too_old(self, monkeypatch): def fake__optional_cuda_import(modname, probe_function=None): assert modname == "cuda.bindings.nvjitlink" - assert probe_function is not None - probe_function(object()) + assert probe_function is None return object() monkeypatch.setattr(_linker, "_optional_cuda_import", fake__optional_cuda_import) @@ -322,6 +322,7 @@ def fake__optional_cuda_import(modname, probe_function=None): assert _linker._use_nvjitlink_backend is False + @pytest.mark.agent_authored(model="grok-4.5") def test_which_backend_falls_back_when_dylib_missing(self, monkeypatch): """Missing nvJitLink dylib must fall back without raising.""" from cuda.pathfinder import DynamicLibNotFoundError @@ -334,11 +335,7 @@ def raise_missing(_nvjitlink): def fake__optional_cuda_import(modname, probe_function=None): assert modname == "cuda.bindings.nvjitlink" - assert probe_function is not None - try: - probe_function(object()) - except DynamicLibNotFoundError: - return None + assert probe_function is None return object() monkeypatch.setattr(_linker, "_nvjitlink_has_version_symbol", raise_missing) diff --git a/cuda_core/tests/test_optional_dependency_imports.py b/cuda_core/tests/test_optional_dependency_imports.py index c0875b88c53..9ba7358f9fe 100644 --- a/cuda_core/tests/test_optional_dependency_imports.py +++ b/cuda_core/tests/test_optional_dependency_imports.py @@ -79,7 +79,7 @@ def fake__optional_cuda_import(modname, probe_function=None): def test_decide_nvjitlink_or_driver_reraises_nested_module_not_found(monkeypatch): def fake__optional_cuda_import(modname, probe_function=None): assert modname == "cuda.bindings.nvjitlink" - assert probe_function is not None + assert probe_function is None err = ModuleNotFoundError("No module named 'not_a_real_dependency'") err.name = "not_a_real_dependency" raise err @@ -94,7 +94,7 @@ def fake__optional_cuda_import(modname, probe_function=None): def test_decide_nvjitlink_or_driver_falls_back_when_module_missing(monkeypatch): def fake__optional_cuda_import(modname, probe_function=None): assert modname == "cuda.bindings.nvjitlink" - assert probe_function is not None + assert probe_function is None return None monkeypatch.setattr(_linker, "_optional_cuda_import", fake__optional_cuda_import) @@ -106,24 +106,20 @@ def fake__optional_cuda_import(modname, probe_function=None): assert _linker._use_nvjitlink_backend is False +@pytest.mark.agent_authored(model="grok-4.5") def test_decide_nvjitlink_or_driver_falls_back_when_dylib_missing(monkeypatch): - """Missing nvJitLink dylib must fall back via DynamicLibNotFoundError in probe.""" + """Missing nvJitLink dylib must fall back via DynamicLibNotFoundError.""" def raise_missing(_nvjitlink): raise DynamicLibNotFoundError("libnvJitLink missing") def fake__optional_cuda_import(modname, probe_function=None): assert modname == "cuda.bindings.nvjitlink" - assert probe_function is not None - try: - # Faithfully simulate _optional_cuda_import's catch of DynamicLibNotFoundError. - probe_function(object()) - except DynamicLibNotFoundError: - return None + assert probe_function is None return object() - monkeypatch.setattr(_linker, "_nvjitlink_has_version_symbol", raise_missing) monkeypatch.setattr(_linker, "_optional_cuda_import", fake__optional_cuda_import) + monkeypatch.setattr(_linker, "_nvjitlink_has_version_symbol", raise_missing) with pytest.warns(RuntimeWarning, match="cuda.bindings.nvjitlink is not available"): use_driver_backend = _linker._decide_nvjitlink_or_driver() @@ -132,11 +128,11 @@ def fake__optional_cuda_import(modname, probe_function=None): assert _linker._use_nvjitlink_backend is False +@pytest.mark.agent_authored(model="grok-4.5") def test_decide_nvjitlink_or_driver_falls_back_when_nvjitlink_too_old(monkeypatch): def fake__optional_cuda_import(modname, probe_function=None): assert modname == "cuda.bindings.nvjitlink" - assert probe_function is not None - probe_function(object()) + assert probe_function is None return object() monkeypatch.setattr(_linker, "_optional_cuda_import", fake__optional_cuda_import) @@ -149,11 +145,11 @@ def fake__optional_cuda_import(modname, probe_function=None): assert _linker._use_nvjitlink_backend is False +@pytest.mark.agent_authored(model="grok-4.5") def test_decide_nvjitlink_or_driver_selects_nvjitlink_when_version_symbol_present(monkeypatch): def fake__optional_cuda_import(modname, probe_function=None): assert modname == "cuda.bindings.nvjitlink" - assert probe_function is not None - probe_function(object()) + assert probe_function is None return object() monkeypatch.setattr(_linker, "_optional_cuda_import", fake__optional_cuda_import) @@ -165,8 +161,9 @@ def fake__optional_cuda_import(modname, probe_function=None): assert _linker._use_nvjitlink_backend is True -def test_decide_nvjitlink_or_driver_probe_does_not_call_version(monkeypatch): - """Regression guard for #2408: probe must not call module.version().""" +@pytest.mark.agent_authored(model="grok-4.5") +def test_decide_nvjitlink_or_driver_does_not_call_version(monkeypatch): + """Regression guard for #2408: must not call module.version().""" called = {"version": False, "inspect": False} class FakeModule: @@ -180,8 +177,7 @@ def fake_has_version(_nvjitlink): def fake__optional_cuda_import(modname, probe_function=None): assert modname == "cuda.bindings.nvjitlink" - assert probe_function is not None - probe_function(FakeModule()) + assert probe_function is None return FakeModule() monkeypatch.setattr(_linker, "_optional_cuda_import", fake__optional_cuda_import) From e6f39237f515c3a233b2527266d182cb7d80fc1f Mon Sep 17 00:00:00 2001 From: Omar Atie Date: Thu, 30 Jul 2026 16:17:40 -0700 Subject: [PATCH 6/6] fix(cuda.core): drop obsolete nvJitLink probe comments Signed-off-by: Omar Atie Co-authored-by: Cursor --- cuda_core/cuda/core/_linker.pyx | 4 ---- 1 file changed, 4 deletions(-) diff --git a/cuda_core/cuda/core/_linker.pyx b/cuda_core/cuda/core/_linker.pyx index 9a62e2dfb82..c30bc70b05f 100644 --- a/cuda_core/cuda/core/_linker.pyx +++ b/cuda_core/cuda/core/_linker.pyx @@ -684,10 +684,6 @@ def _decide_nvjitlink_or_driver() -> bool: " For best results, consider upgrading to a recent version of" ) - # Do not call module.version(): nvJitLink 12.0-12.2 lack the unversioned - # nvJitLinkVersion export, so version() raises FunctionNotFoundError (#2408). - # Inspect the symbol pointer instead, and catch DynamicLibNotFoundError when - # the dylib is missing so we can fall back to cuLink. nvjitlink_module = _optional_cuda_import("cuda.bindings.nvjitlink") if nvjitlink_module is None: warn_txt = f"cuda.bindings.nvjitlink is not available, therefore {warn_txt_common} cuda-bindings."