Skip to content

Commit f983b9d

Browse files
authored
Use cuda-pathfinder in build-system.requires (#1817)
* build: use cuda.pathfinder.get_cuda_path_or_home in build hooks Pin cuda-pathfinder>=1.5 in both build-system.requires and project.dependencies. Made-with: Cursor * chore: update stale pixi package version to 1.5.0 The previous 1.3.4a0 was a stale value that could confuse someone. Made-with: Cursor * build: raise actionable error when cuda-pathfinder not found on sys.path Made-with: Cursor * build: replace inline docstring with reference to issue #1824 Made-with: Cursor * build: narrow except to only catch cuda.pathfinder-not-found Made-with: Cursor * build: use importlib.metadata to locate cuda-pathfinder instead of scanning sys.path Made-with: Cursor * build: produce actionable error when cuda namespace is entirely absent Made-with: Cursor * build: diagnostic for importlib.metadata locate_file in PEP 517 builds Made-with: Cursor * Revert "build: diagnostic for importlib.metadata locate_file in PEP 517 builds" This reverts commit 3cb0313. * build: revert to sys.path scan, importlib.metadata finds wrong dist-info See #1817 (comment) See #1817 (comment) Made-with: Cursor * Revert changes to cuda-pathfinder runtime dependency pinning. * Exclude broken cuda-toolkit patch releases from dependency specs. Use exclusion-style constraints in cuda_bindings, cuda_core, and cuda_pathfinder so CI avoids cuda-toolkit 12.9.2 and 13.0.3 while still allowing newer good patch releases. Made-with: Cursor * Revert "Exclude broken cuda-toolkit patch releases from dependency specs." This reverts commit 937ff7f.
1 parent c93623b commit f983b9d

File tree

5 files changed

+74
-11
lines changed

5 files changed

+74
-11
lines changed

cuda_bindings/build_hooks.py

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,41 @@
3333
_extensions = None
3434

3535

36+
# Please keep in sync with the copy in cuda_core/build_hooks.py.
37+
def _import_get_cuda_path_or_home():
38+
"""Import get_cuda_path_or_home, working around PEP 517 namespace shadowing.
39+
40+
See https://github.com/NVIDIA/cuda-python/issues/1824 for why this helper is needed.
41+
"""
42+
try:
43+
import cuda.pathfinder
44+
except ModuleNotFoundError as exc:
45+
if exc.name not in ("cuda", "cuda.pathfinder"):
46+
raise
47+
try:
48+
import cuda
49+
except ModuleNotFoundError:
50+
cuda = None
51+
52+
for p in sys.path:
53+
sp_cuda = os.path.join(p, "cuda")
54+
if os.path.isdir(os.path.join(sp_cuda, "pathfinder")):
55+
cuda.__path__ = list(cuda.__path__) + [sp_cuda]
56+
break
57+
else:
58+
raise ModuleNotFoundError(
59+
"cuda-pathfinder is not installed in the build environment. "
60+
"Ensure 'cuda-pathfinder>=1.5' is in build-system.requires."
61+
)
62+
import cuda.pathfinder
63+
64+
return cuda.pathfinder.get_cuda_path_or_home
65+
66+
3667
@functools.cache
3768
def _get_cuda_path() -> str:
38-
# Not using cuda.pathfinder.get_cuda_path_or_home() here because this
39-
# build backend runs in an isolated venv where the cuda namespace package
40-
# from backend-path shadows the installed cuda-pathfinder. See #1803 for
41-
# a workaround to apply after cuda-pathfinder >= 1.5 is released.
42-
cuda_path = os.environ.get("CUDA_PATH", os.environ.get("CUDA_HOME"))
69+
get_cuda_path_or_home = _import_get_cuda_path_or_home()
70+
cuda_path = get_cuda_path_or_home()
4371
if not cuda_path:
4472
raise RuntimeError("Environment variable CUDA_PATH or CUDA_HOME is not set")
4573
print("CUDA path:", cuda_path)

cuda_bindings/pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ requires = [
66
"setuptools_scm[simple]>=8",
77
"cython>=3.2,<3.3",
88
"pyclibrary>=0.1.7",
9+
"cuda-pathfinder>=1.5",
910
]
1011
build-backend = "build_hooks"
1112
backend-path = ["."]

cuda_core/build_hooks.py

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,41 @@
2828
COMPILE_FOR_COVERAGE = bool(int(os.environ.get("CUDA_PYTHON_COVERAGE", "0")))
2929

3030

31+
# Please keep in sync with the copy in cuda_bindings/build_hooks.py.
32+
def _import_get_cuda_path_or_home():
33+
"""Import get_cuda_path_or_home, working around PEP 517 namespace shadowing.
34+
35+
See https://github.com/NVIDIA/cuda-python/issues/1824 for why this helper is needed.
36+
"""
37+
try:
38+
import cuda.pathfinder
39+
except ModuleNotFoundError as exc:
40+
if exc.name not in ("cuda", "cuda.pathfinder"):
41+
raise
42+
try:
43+
import cuda
44+
except ModuleNotFoundError:
45+
cuda = None
46+
47+
for p in sys.path:
48+
sp_cuda = os.path.join(p, "cuda")
49+
if os.path.isdir(os.path.join(sp_cuda, "pathfinder")):
50+
cuda.__path__ = list(cuda.__path__) + [sp_cuda]
51+
break
52+
else:
53+
raise ModuleNotFoundError(
54+
"cuda-pathfinder is not installed in the build environment. "
55+
"Ensure 'cuda-pathfinder>=1.5' is in build-system.requires."
56+
)
57+
import cuda.pathfinder
58+
59+
return cuda.pathfinder.get_cuda_path_or_home
60+
61+
3162
@functools.cache
3263
def _get_cuda_path() -> str:
33-
# Not using cuda.pathfinder.get_cuda_path_or_home() here because this
34-
# build backend runs in an isolated venv where the cuda namespace package
35-
# from backend-path shadows the installed cuda-pathfinder. See #1803 for
36-
# a workaround to apply after cuda-pathfinder >= 1.5 is released.
37-
cuda_path = os.environ.get("CUDA_PATH", os.environ.get("CUDA_HOME"))
64+
get_cuda_path_or_home = _import_get_cuda_path_or_home()
65+
cuda_path = get_cuda_path_or_home()
3866
if not cuda_path:
3967
raise RuntimeError("Environment variable CUDA_PATH or CUDA_HOME is not set")
4068
print("CUDA path:", cuda_path)

cuda_core/pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ requires = [
77
"setuptools>=80",
88
"setuptools-scm[simple]>=8",
99
"Cython>=3.2,<3.3",
10+
"cuda-pathfinder>=1.5"
1011
]
1112
build-backend = "build_hooks"
1213
backend-path = ["."]

cuda_core/tests/test_build_hooks.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# SPDX-FileCopyrightText: Copyright (c) 2024-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
1+
# SPDX-FileCopyrightText: Copyright (c) 2024-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
22
# SPDX-License-Identifier: Apache-2.0
33

44
"""Tests for build_hooks.py build infrastructure.
@@ -24,6 +24,8 @@
2424

2525
import pytest
2626

27+
from cuda.pathfinder import get_cuda_path_or_home
28+
2729
# build_hooks.py imports Cython and setuptools at the top level, so skip if not available
2830
pytest.importorskip("Cython")
2931
pytest.importorskip("setuptools")
@@ -68,6 +70,7 @@ def _check_version_detection(
6870

6971
build_hooks._get_cuda_path.cache_clear()
7072
build_hooks._determine_cuda_major_version.cache_clear()
73+
get_cuda_path_or_home.cache_clear()
7174

7275
mock_env = {
7376
k: v
@@ -92,6 +95,7 @@ def test_env_var_override(self, version):
9295
"""CUDA_CORE_BUILD_MAJOR env var override works with various versions."""
9396
build_hooks._get_cuda_path.cache_clear()
9497
build_hooks._determine_cuda_major_version.cache_clear()
98+
get_cuda_path_or_home.cache_clear()
9599
with mock.patch.dict(os.environ, {"CUDA_CORE_BUILD_MAJOR": version}, clear=False):
96100
result = build_hooks._determine_cuda_major_version()
97101
assert result == version
@@ -125,6 +129,7 @@ def test_missing_cuda_path_raises_error(self):
125129
"""RuntimeError is raised when CUDA_PATH/CUDA_HOME not set and no env var override."""
126130
build_hooks._get_cuda_path.cache_clear()
127131
build_hooks._determine_cuda_major_version.cache_clear()
132+
get_cuda_path_or_home.cache_clear()
128133
with (
129134
mock.patch.dict(os.environ, {}, clear=True),
130135
pytest.raises(RuntimeError, match="CUDA_PATH or CUDA_HOME"),

0 commit comments

Comments
 (0)