Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions cuda_core/cuda/core/system/_system.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,17 @@ else:
if CUDA_BINDINGS_NVML_IS_COMPATIBLE:
try:
from cuda.bindings import nvml
# _nvml_context imports cuda.bindings.nvml itself, so it has to be
# inside the same try: importing it after nvml failed would raise the
# very ImportError this block exists to absorb.
Comment on lines +50 to +52

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
# _nvml_context imports cuda.bindings.nvml itself, so it has to be
# inside the same try: importing it after nvml failed would raise the
# very ImportError this block exists to absorb.

from cuda.core.system._nvml_context import initialize
except ImportError:
CUDA_BINDINGS_NVML_IS_COMPATIBLE = False

from cuda.core.system._nvml_context import initialize
else:
# Deliberately a second `if`, not an `else` on the one above: the flag can be
# cleared by the import that just failed, and the non-NVML fallbacks are
# exactly what every consumer below reaches for once it is False.
Comment on lines +57 to +59

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
# Deliberately a second `if`, not an `else` on the one above: the flag can be
# cleared by the import that just failed, and the non-NVML fallbacks are
# exactly what every consumer below reaches for once it is False.

if not CUDA_BINDINGS_NVML_IS_COMPATIBLE:
from cuda.core._utils.cuda_utils import driver, handle_return, runtime


Expand Down
8 changes: 8 additions & 0 deletions cuda_core/docs/source/release/1.2.0-notes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,14 @@ Fixes and enhancements
Windows, both ``ctypes.CFUNCTYPE`` and ``ctypes.WINFUNCTYPE`` are accepted.
(`#2439 <https://github.com/NVIDIA/cuda-python/issues/2439>`__)

- ``cuda.core.system`` now really does fall back to its non-NVML
implementations when ``cuda.bindings.nvml`` cannot be imported. The
``except ImportError`` cleared ``CUDA_BINDINGS_NVML_IS_COMPATIBLE``, but the
``else`` that binds ``driver`` / ``handle_return`` / ``runtime`` hung off the
outer ``if``, which had already been evaluated, and the unconditional
``_nvml_context`` import that followed pulls in ``cuda.bindings.nvml``
itself -- so ``import cuda.core.system`` raised instead of degrading.
Comment on lines +76 to +82

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Release notes should be to-the-point and not go into implementation details.

Suggested change
- ``cuda.core.system`` now really does fall back to its non-NVML
implementations when ``cuda.bindings.nvml`` cannot be imported. The
``except ImportError`` cleared ``CUDA_BINDINGS_NVML_IS_COMPATIBLE``, but the
``else`` that binds ``driver`` / ``handle_return`` / ``runtime`` hung off the
outer ``if``, which had already been evaluated, and the unconditional
``_nvml_context`` import that followed pulls in ``cuda.bindings.nvml``
itself -- so ``import cuda.core.system`` raised instead of degrading.
- ``cuda.core.system`` now really does fall back to its non-NVML
implementations when ``cuda.bindings.nvml`` cannot be imported.


Deprecation Notices
-------------------

Expand Down
43 changes: 43 additions & 0 deletions cuda_core/tests/system/test_system_system.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@


import os
import subprocess
import sys

import pytest
from cuda_python_test_helpers.arch_check import skip_if_nvml_unsupported
Expand Down Expand Up @@ -80,3 +82,44 @@ def test_get_driver_branch():
driver_branch = system.get_driver_branch()
assert isinstance(driver_branch, str)
assert len(driver_branch) > 0


# The NVML-unavailable fallback is decided at import time, so it can only be
# exercised in a fresh interpreter with cuda.bindings.nvml blocked.
_NO_NVML_SCRIPT = """
import sys


class _BlockNvml:
def find_spec(self, name, path=None, target=None):
if name == "cuda.bindings.nvml":
raise ImportError("blocked for testing", name=name)
return None


sys.meta_path.insert(0, _BlockNvml())

# Used to raise ImportError out of this import: the flag was cleared, but the
# `else` that binds the non-NVML fallbacks belonged to the outer `if`, which
# had already been evaluated, and _nvml_context (imported unconditionally
# right after) imports cuda.bindings.nvml itself.
from cuda.core import system
from cuda.core.system import _system

assert system.CUDA_BINDINGS_NVML_IS_COMPATIBLE is False, "flag must be cleared when nvml is unimportable"
for name in ("driver", "handle_return", "runtime"):
assert hasattr(_system, name), f"non-NVML fallback {name!r} is not bound"
print("ok")
"""


@pytest.mark.agent_authored(model="claude-opus-5")
def test_system_falls_back_when_nvml_is_unimportable():
proc = subprocess.run( # noqa: S603
[sys.executable, "-c", _NO_NVML_SCRIPT],
capture_output=True,
text=True,
timeout=300,
)
assert proc.returncode == 0, f"stdout={proc.stdout!r} stderr={proc.stderr!r}"
assert proc.stdout.strip().endswith("ok")
Loading