diff --git a/cuda_bindings/build_hooks.py b/cuda_bindings/build_hooks.py index a48aa0f0e9..9c6710a264 100644 --- a/cuda_bindings/build_hooks.py +++ b/cuda_bindings/build_hooks.py @@ -33,13 +33,36 @@ _extensions = None +# Please keep in sync with the copy in cuda_core/build_hooks.py. +def _import_get_cuda_path_or_home(): + """Import get_cuda_path_or_home, working around PEP 517 namespace shadowing. + + See https://github.com/NVIDIA/cuda-python/issues/1824 for why this helper is needed. + """ + try: + import cuda.pathfinder + except ModuleNotFoundError: + import cuda + + for p in sys.path: + sp_cuda = os.path.join(p, "cuda") + if os.path.isdir(os.path.join(sp_cuda, "pathfinder")): + cuda.__path__ = list(cuda.__path__) + [sp_cuda] + break + else: + raise ModuleNotFoundError( + "cuda-pathfinder is not installed in the build environment. " + "Ensure 'cuda-pathfinder>=1.5' is in build-system.requires." + ) + import cuda.pathfinder + + return cuda.pathfinder.get_cuda_path_or_home + + @functools.cache def _get_cuda_path() -> str: - # Not using cuda.pathfinder.get_cuda_path_or_home() here because this - # build backend runs in an isolated venv where the cuda namespace package - # from backend-path shadows the installed cuda-pathfinder. See #1803 for - # a workaround to apply after cuda-pathfinder >= 1.5 is released. - cuda_path = os.environ.get("CUDA_PATH", os.environ.get("CUDA_HOME")) + get_cuda_path_or_home = _import_get_cuda_path_or_home() + cuda_path = get_cuda_path_or_home() if not cuda_path: raise RuntimeError("Environment variable CUDA_PATH or CUDA_HOME is not set") print("CUDA path:", cuda_path) diff --git a/cuda_bindings/pyproject.toml b/cuda_bindings/pyproject.toml index 96cfb4dd07..9a4f189815 100644 --- a/cuda_bindings/pyproject.toml +++ b/cuda_bindings/pyproject.toml @@ -6,6 +6,7 @@ requires = [ "setuptools_scm[simple]>=8", "cython>=3.2,<3.3", "pyclibrary>=0.1.7", + "cuda-pathfinder>=1.5", ] build-backend = "build_hooks" backend-path = ["."] @@ -31,7 +32,7 @@ classifiers = [ "Environment :: GPU :: NVIDIA CUDA", ] dynamic = ["version", "readme"] -dependencies = ["cuda-pathfinder >=1.4.2"] +dependencies = ["cuda-pathfinder >=1.5"] [project.optional-dependencies] all = [ diff --git a/cuda_core/build_hooks.py b/cuda_core/build_hooks.py index b368b02759..ce77de3c17 100644 --- a/cuda_core/build_hooks.py +++ b/cuda_core/build_hooks.py @@ -28,13 +28,36 @@ COMPILE_FOR_COVERAGE = bool(int(os.environ.get("CUDA_PYTHON_COVERAGE", "0"))) +# Please keep in sync with the copy in cuda_bindings/build_hooks.py. +def _import_get_cuda_path_or_home(): + """Import get_cuda_path_or_home, working around PEP 517 namespace shadowing. + + See https://github.com/NVIDIA/cuda-python/issues/1824 for why this helper is needed. + """ + try: + import cuda.pathfinder + except ModuleNotFoundError: + import cuda + + for p in sys.path: + sp_cuda = os.path.join(p, "cuda") + if os.path.isdir(os.path.join(sp_cuda, "pathfinder")): + cuda.__path__ = list(cuda.__path__) + [sp_cuda] + break + else: + raise ModuleNotFoundError( + "cuda-pathfinder is not installed in the build environment. " + "Ensure 'cuda-pathfinder>=1.5' is in build-system.requires." + ) + import cuda.pathfinder + + return cuda.pathfinder.get_cuda_path_or_home + + @functools.cache def _get_cuda_path() -> str: - # Not using cuda.pathfinder.get_cuda_path_or_home() here because this - # build backend runs in an isolated venv where the cuda namespace package - # from backend-path shadows the installed cuda-pathfinder. See #1803 for - # a workaround to apply after cuda-pathfinder >= 1.5 is released. - cuda_path = os.environ.get("CUDA_PATH", os.environ.get("CUDA_HOME")) + get_cuda_path_or_home = _import_get_cuda_path_or_home() + cuda_path = get_cuda_path_or_home() if not cuda_path: raise RuntimeError("Environment variable CUDA_PATH or CUDA_HOME is not set") print("CUDA path:", cuda_path) diff --git a/cuda_core/pyproject.toml b/cuda_core/pyproject.toml index 107c2ffb92..dc418342b5 100644 --- a/cuda_core/pyproject.toml +++ b/cuda_core/pyproject.toml @@ -7,6 +7,7 @@ requires = [ "setuptools>=80", "setuptools-scm[simple]>=8", "Cython>=3.2,<3.3", + "cuda-pathfinder>=1.5" ] build-backend = "build_hooks" backend-path = ["."] @@ -47,7 +48,7 @@ classifiers = [ "Environment :: GPU :: NVIDIA CUDA :: 13", ] dependencies = [ - "cuda-pathfinder >=1.4.2", + "cuda-pathfinder >=1.5", "numpy", ] diff --git a/cuda_core/tests/test_build_hooks.py b/cuda_core/tests/test_build_hooks.py index b298e7a977..121ed1be05 100644 --- a/cuda_core/tests/test_build_hooks.py +++ b/cuda_core/tests/test_build_hooks.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: Copyright (c) 2024-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-FileCopyrightText: Copyright (c) 2024-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. # SPDX-License-Identifier: Apache-2.0 """Tests for build_hooks.py build infrastructure. @@ -24,6 +24,8 @@ import pytest +from cuda.pathfinder import get_cuda_path_or_home + # build_hooks.py imports Cython and setuptools at the top level, so skip if not available pytest.importorskip("Cython") pytest.importorskip("setuptools") @@ -68,6 +70,7 @@ def _check_version_detection( build_hooks._get_cuda_path.cache_clear() build_hooks._determine_cuda_major_version.cache_clear() + get_cuda_path_or_home.cache_clear() mock_env = { k: v @@ -92,6 +95,7 @@ def test_env_var_override(self, version): """CUDA_CORE_BUILD_MAJOR env var override works with various versions.""" build_hooks._get_cuda_path.cache_clear() build_hooks._determine_cuda_major_version.cache_clear() + get_cuda_path_or_home.cache_clear() with mock.patch.dict(os.environ, {"CUDA_CORE_BUILD_MAJOR": version}, clear=False): result = build_hooks._determine_cuda_major_version() assert result == version @@ -125,6 +129,7 @@ def test_missing_cuda_path_raises_error(self): """RuntimeError is raised when CUDA_PATH/CUDA_HOME not set and no env var override.""" build_hooks._get_cuda_path.cache_clear() build_hooks._determine_cuda_major_version.cache_clear() + get_cuda_path_or_home.cache_clear() with ( mock.patch.dict(os.environ, {}, clear=True), pytest.raises(RuntimeError, match="CUDA_PATH or CUDA_HOME"), diff --git a/cuda_pathfinder/pixi.toml b/cuda_pathfinder/pixi.toml index 0d780290b6..961450699c 100644 --- a/cuda_pathfinder/pixi.toml +++ b/cuda_pathfinder/pixi.toml @@ -36,7 +36,7 @@ cu12 = { features = ["cu12", "test"], solve-group = "cu12" } # TODO: check if these can be extracted from pyproject.toml [package] name = "cuda-pathfinder" -version = "1.3.4a0" +version = "1.5.0" [package.build] backend = { name = "pixi-build-python", version = "*" }