Skip to content

Commit 6ea0dec

Browse files
committed
Add cuda-bindings version validation to prevent PyPI fallback
Add validation in _get_cuda_bindings_require() to check if cuda-bindings is already installed and validate its version compatibility. Strategy: - If cuda-bindings is not installed: require matching CUDA major version - If installed from sources (editable): keep it regardless of version - If installed from wheel: validate major version matches CUDA major - Raise clear error if version mismatch detected This prevents accidentally using PyPI versions that don't match the CUDA toolkit version being used for compilation. Changes: - Add _check_cuda_bindings_installed() to detect installation status - Check for editable installs via direct_url.json, repo location, or .egg-link - Validate version compatibility in _get_cuda_bindings_require() - Move imports to module level (PEP 8 compliance) - Add noqa: S110 for broad exception handling (intentional)
1 parent f6200ec commit 6ea0dec

1 file changed

Lines changed: 98 additions & 1 deletion

File tree

cuda_core/build_hooks.py

Lines changed: 98 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,11 @@
99

1010
import functools
1111
import glob
12+
import importlib.metadata
13+
import json
1214
import os
1315
import re
16+
from pathlib import Path
1417

1518
from Cython.Build import cythonize
1619
from setuptools import Extension
@@ -191,9 +194,103 @@ def build_wheel(wheel_directory, config_settings=None, metadata_directory=None):
191194
return _build_meta.build_wheel(wheel_directory, config_settings, metadata_directory)
192195

193196

197+
def _check_cuda_bindings_installed():
198+
"""Check if cuda-bindings is installed and validate its version.
199+
200+
Returns:
201+
tuple: (is_installed: bool, is_editable: bool, major_version: str | None)
202+
If not installed, returns (False, False, None)
203+
"""
204+
try:
205+
bindings_dist = importlib.metadata.distribution("cuda-bindings")
206+
except importlib.metadata.PackageNotFoundError:
207+
return (False, False, None)
208+
209+
bindings_version = bindings_dist.version
210+
bindings_major_version = bindings_version.split(".")[0]
211+
212+
# Check if it's an editable install
213+
is_editable = False
214+
215+
# Method 1: Check direct_url.json (PEP 610 - modern editable installs)
216+
try:
217+
dist_location = Path(bindings_dist.locate_file(""))
218+
# dist-info or egg-info directory
219+
metadata_dir = dist_location.parent
220+
direct_url_path = metadata_dir / "direct_url.json"
221+
if direct_url_path.exists():
222+
with open(direct_url_path) as f:
223+
direct_url = json.load(f)
224+
if direct_url.get("dir_info", {}).get("editable"):
225+
is_editable = True
226+
except Exception: # noqa: S110
227+
pass
228+
229+
# Method 2: Check if package is in current repository (another way to detect editable)
230+
if not is_editable:
231+
try:
232+
import cuda.bindings
233+
234+
bindings_path = Path(cuda.bindings.__file__).resolve()
235+
repo_root = Path(__file__).resolve().parent.parent.parent
236+
if repo_root in bindings_path.parents:
237+
is_editable = True
238+
except Exception: # noqa: S110
239+
pass
240+
241+
# Method 3: Check for .egg-link file (old editable install method)
242+
if not is_editable:
243+
try:
244+
# Look for .egg-link files in site-packages
245+
import site
246+
247+
for site_dir in site.getsitepackages():
248+
egg_link_path = Path(site_dir) / "cuda-bindings.egg-link"
249+
if egg_link_path.exists():
250+
is_editable = True
251+
break
252+
except Exception: # noqa: S110
253+
pass
254+
255+
return (True, is_editable, bindings_major_version)
256+
257+
194258
def _get_cuda_bindings_require():
259+
"""Determine cuda-bindings build requirement.
260+
261+
Strategy:
262+
1. If cuda-bindings is already installed (any version), don't require it (keep existing)
263+
2. If installed from sources (editable), definitely keep it
264+
3. Otherwise, if installed version major doesn't match CUDA major, raise error
265+
4. If not installed, require matching CUDA major version
266+
"""
267+
bindings_installed, bindings_editable, bindings_major = _check_cuda_bindings_installed()
268+
269+
# If not installed, require matching CUDA major version
270+
if not bindings_installed:
271+
cuda_major = _determine_cuda_major_version()
272+
return [f"cuda-bindings=={cuda_major}.*"]
273+
274+
# If installed from sources (editable), keep it (don't require anything)
275+
if bindings_editable:
276+
return []
277+
278+
# If installed but not editable, check version matches CUDA major
195279
cuda_major = _determine_cuda_major_version()
196-
return [f"cuda-bindings=={cuda_major}.*"]
280+
if bindings_major != cuda_major:
281+
raise RuntimeError(
282+
f"Installed cuda-bindings version has major version {bindings_major}, "
283+
f"but CUDA major version is {cuda_major}.\n"
284+
f"This mismatch could cause build or runtime errors.\n"
285+
f"\n"
286+
f"To fix:\n"
287+
f" 1. Uninstall cuda-bindings: pip uninstall cuda-bindings\n"
288+
f" 2. Or install from sources: pip install -e ./cuda_bindings\n"
289+
f" 3. Or install matching version: pip install 'cuda-bindings=={cuda_major}.*'"
290+
)
291+
292+
# Installed and version matches (or is editable), keep it
293+
return []
197294

198295

199296
def get_requires_for_build_editable(config_settings=None):

0 commit comments

Comments
 (0)