diff --git a/cuda_bindings/build_hooks.py b/cuda_bindings/build_hooks.py index a48aa0f0e9..b08b0d24d8 100644 --- a/cuda_bindings/build_hooks.py +++ b/cuda_bindings/build_hooks.py @@ -33,13 +33,41 @@ _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 as exc: + if exc.name not in ("cuda", "cuda.pathfinder"): + raise + try: + import cuda + except ModuleNotFoundError: + cuda = None + + 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 f4866fc4f8..7e1a239339 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 = ["."] @@ -35,8 +36,8 @@ dependencies = ["cuda-pathfinder >=1.4.2"] [project.optional-dependencies] all = [ - "cuda-toolkit[nvrtc,nvjitlink,nvvm,nvfatbin]==13.*", - "cuda-toolkit[cufile]==13.*; sys_platform == 'linux'", + "cuda-toolkit[nvrtc,nvjitlink,nvvm,nvfatbin]==13.*,!=13.0.3", + "cuda-toolkit[cufile]==13.*,!=13.0.3; sys_platform == 'linux'", ] [dependency-groups] diff --git a/cuda_core/build_hooks.py b/cuda_core/build_hooks.py index b368b02759..491879c2ab 100644 --- a/cuda_core/build_hooks.py +++ b/cuda_core/build_hooks.py @@ -28,13 +28,41 @@ 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 as exc: + if exc.name not in ("cuda", "cuda.pathfinder"): + raise + try: + import cuda + except ModuleNotFoundError: + cuda = None + + 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 80711f39ed..b229b67a5b 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 = ["."] @@ -58,12 +59,12 @@ cu13 = ["cuda-bindings[all]==13.*"] [dependency-groups] test = ["cython>=3.2,<3.3", "setuptools", "pytest>=6.2.4", "pytest-benchmark", "pytest-randomly", "pytest-repeat", "pytest-rerunfailures", "cloudpickle", "psutil", "cffi"] ml-dtypes = ["ml-dtypes>=0.5.4,<0.6.0"] -test-cu12 = [ {include-group = "ml-dtypes" }, {include-group = "test" }, "cupy-cuda12x; python_version < '3.14'", "cuda-toolkit[cudart]==12.*"] # runtime headers needed by CuPy -test-cu13 = [ {include-group = "ml-dtypes" }, {include-group = "test" }, "cupy-cuda13x; python_version < '3.14'", "cuda-toolkit[cudart]==13.*"] # runtime headers needed by CuPy +test-cu12 = [ {include-group = "ml-dtypes" }, {include-group = "test" }, "cupy-cuda12x; python_version < '3.14'", "cuda-toolkit[cudart]==12.*,!=12.9.2"] # runtime headers needed by CuPy +test-cu13 = [ {include-group = "ml-dtypes" }, {include-group = "test" }, "cupy-cuda13x; python_version < '3.14'", "cuda-toolkit[cudart]==13.*,!=13.0.3"] # runtime headers needed by CuPy # free threaded build, cupy doesn't support free-threaded builds yet, so avoid installing it for now # TODO: cupy should support free threaded builds -test-cu12-ft = [ {include-group = "ml-dtypes" }, {include-group = "test" }, "cuda-toolkit[cudart]==12.*"] -test-cu13-ft = [ {include-group = "ml-dtypes" }, {include-group = "test" }, "cuda-toolkit[cudart]==13.*"] +test-cu12-ft = [ {include-group = "ml-dtypes" }, {include-group = "test" }, "cuda-toolkit[cudart]==12.*,!=12.9.2"] +test-cu13-ft = [ {include-group = "ml-dtypes" }, {include-group = "test" }, "cuda-toolkit[cudart]==13.*,!=13.0.3"] [tool.uv] conflicts = [ 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/pyproject.toml b/cuda_pathfinder/pyproject.toml index 7d96e72023..801834f52c 100644 --- a/cuda_pathfinder/pyproject.toml +++ b/cuda_pathfinder/pyproject.toml @@ -19,8 +19,8 @@ test = [ ] # Internal organization of test dependencies. cu12 = [ - "cuda-toolkit[nvcc,cublas,nvrtc,cudart,cufft,curand,cusolver,cusparse,npp,nvfatbin,nvjitlink,nvjpeg,cccl,cupti,profiler]==12.*", - "cuda-toolkit[cufile]==12.*; sys_platform != 'win32'", + "cuda-toolkit[nvcc,cublas,nvrtc,cudart,cufft,curand,cusolver,cusparse,npp,nvfatbin,nvjitlink,nvjpeg,cccl,cupti,profiler]==12.*,!=12.9.2", + "cuda-toolkit[cufile]==12.*,!=12.9.2; sys_platform != 'win32'", "cutensor-cu12", "nvidia-cublasmp-cu12; sys_platform != 'win32'", "nvidia-cudss-cu12", @@ -32,8 +32,8 @@ cu12 = [ "nvidia-nvshmem-cu12; sys_platform != 'win32'", ] cu13 = [ - "cuda-toolkit[nvcc,cublas,nvrtc,cudart,cufft,curand,cusolver,cusparse,npp,nvfatbin,nvjitlink,nvjpeg,cccl,cupti,profiler,nvvm]==13.*", - "cuda-toolkit[cufile]==13.*; sys_platform != 'win32'", + "cuda-toolkit[nvcc,cublas,nvrtc,cudart,cufft,curand,cusolver,cusparse,npp,nvfatbin,nvjitlink,nvjpeg,cccl,cupti,profiler,nvvm]==13.*,!=13.0.3", + "cuda-toolkit[cufile]==13.*,!=13.0.3; sys_platform != 'win32'", "cutensor-cu13", "nvidia-cublasmp-cu13; sys_platform != 'win32'", "nvidia-cudla; platform_system == 'Linux' and platform_machine == 'aarch64'",