Skip to content

Commit dcaa6a7

Browse files
rparolincpcloud
andauthored
Cython .pth file support for pixi path dependencies (#1562)
* using .pth files * auto format * Update cuda_core/build_hooks.py Co-authored-by: Phillip Cloud <417981+cpcloud@users.noreply.github.com> * Update cuda_core/build_hooks.py Co-authored-by: Phillip Cloud <417981+cpcloud@users.noreply.github.com> * Update cuda_core/build_hooks.py Co-authored-by: Phillip Cloud <417981+cpcloud@users.noreply.github.com> * Update cuda_core/build_hooks.py Co-authored-by: Phillip Cloud <417981+cpcloud@users.noreply.github.com> * Update cuda_core/build_hooks.py Co-authored-by: Phillip Cloud <417981+cpcloud@users.noreply.github.com> * Update cuda_core/build_hooks.py Co-authored-by: Phillip Cloud <417981+cpcloud@users.noreply.github.com> * updating lock files --------- Co-authored-by: Phillip Cloud <417981+cpcloud@users.noreply.github.com>
1 parent d17668b commit dcaa6a7

3 files changed

Lines changed: 516 additions & 462 deletions

File tree

cuda_core/build_hooks.py

Lines changed: 106 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@
1111
import glob
1212
import os
1313
import re
14+
import sys
15+
import tempfile
16+
import zipfile
17+
from pathlib import Path
1418

1519
from Cython.Build import cythonize
1620
from setuptools import Extension
@@ -93,6 +97,22 @@ def _build_cuda_core():
9397
# This function populates "_extensions".
9498
global _extensions
9599

100+
# Add cuda-bindings to sys.path so Cython can find .pxd files
101+
# This is needed for editable installs where meta path finders don't work for Cython
102+
# We need to add the directory containing the 'cuda' package so Cython can resolve
103+
# "from cuda.bindings cimport cydriver"
104+
try:
105+
import cuda.bindings
106+
107+
bindings_path = Path(cuda.bindings.__file__).parent # .../cuda/bindings/
108+
cuda_package_dir = bindings_path.parent.parent # .../cuda_bindings/ (contains cuda/)
109+
if str(cuda_package_dir) not in sys.path:
110+
sys.path.insert(0, str(cuda_package_dir))
111+
print(f"Added cuda-bindings parent path for Cython: {cuda_package_dir}", file=sys.stderr)
112+
except ImportError:
113+
# cuda-bindings not available in editable mode, will use installed version
114+
pass
115+
96116
# It seems setuptools' wildcard support has problems for namespace packages,
97117
# so we explicitly spell out all Extension instances.
98118
def module_names():
@@ -151,9 +171,94 @@ def get_sources(mod_name):
151171
return
152172

153173

174+
def _add_cython_include_paths_to_pth(wheel_path: str) -> None:
175+
"""
176+
Modify the .pth file in an editable install wheel to add Cython include paths.
177+
178+
This is needed because Cython cannot find .pxd files through meta path finders,
179+
it only looks in sys.path directories. By adding direct paths to the .pth file,
180+
we enable Cython to find .pxd files from editable-installed cuda-bindings.
181+
182+
See: https://github.com/scikit-build/scikit-build-core/pull/516
183+
See: https://github.com/cython/cython/issues/7326
184+
"""
185+
# Find cuda-bindings location
186+
# When building with pixi path dependencies, cuda-bindings should be importable
187+
try:
188+
import cuda.bindings
189+
190+
bindings_path = Path(cuda.bindings.__file__).parent # .../cuda/bindings/
191+
# We need the directory containing the 'cuda' package for Cython imports
192+
cuda_package_dir = bindings_path.parent.parent # .../cuda_bindings/ (contains cuda/)
193+
print(f"Found cuda-bindings at: {bindings_path}", file=sys.stderr)
194+
print(f"Will add to .pth for Cython: {cuda_package_dir}", file=sys.stderr)
195+
except ImportError:
196+
# If cuda-bindings isn't available yet, we can't add the path
197+
# This might happen in some build scenarios, but it's okay - the
198+
# wildcard dependency will work in those cases
199+
print("cuda-bindings not found in current environment, skipping .pth modification")
200+
return
201+
202+
# Create a temporary directory for wheel manipulation
203+
with tempfile.TemporaryDirectory() as tmpdir:
204+
tmpdir_path = Path(tmpdir)
205+
wheel_file = Path(wheel_path)
206+
207+
# Extract the wheel
208+
extract_dir = tmpdir_path / "extracted"
209+
with zipfile.ZipFile(wheel_file, "r") as zf:
210+
zf.extractall(extract_dir)
211+
212+
# Find the .pth file (should be named something like __editable___cuda_core-*.pth)
213+
pth_files = list(extract_dir.glob("**/*.pth"))
214+
if not pth_files:
215+
print("Warning: No .pth file found in editable wheel", file=sys.stderr)
216+
return
217+
218+
# Modify each .pth file (usually just one)
219+
for pth_file in pth_files:
220+
print(f"Modifying {pth_file.name} to add Cython include paths", file=sys.stderr)
221+
222+
# Read existing content
223+
content = pth_file.read_text()
224+
225+
# Add the cuda-bindings source path to sys.path for Cython
226+
# This allows Cython to find .pxd files via direct path lookup
227+
# The path must be the directory containing the 'cuda' package
228+
path_to_add = str(cuda_package_dir.absolute())
229+
230+
# Ensure content ends with newline before adding path
231+
if not content.endswith("\n"):
232+
content += "\n"
233+
234+
# Append to the .pth file (after the import hook line)
235+
if path_to_add not in content:
236+
pth_file.write_text(content + path_to_add + "\n")
237+
print(f"Added Cython include path: {cuda_package_dir}", file=sys.stderr)
238+
239+
# Repackage the wheel
240+
# Remove the old wheel first
241+
wheel_file.unlink()
242+
243+
# Create new wheel with same name
244+
with zipfile.ZipFile(wheel_file, "w", zipfile.ZIP_DEFLATED) as zf:
245+
for file_path in extract_dir.rglob("*"):
246+
if file_path.is_file():
247+
arcname = file_path.relative_to(extract_dir)
248+
zf.write(file_path, arcname)
249+
250+
print(f"Successfully patched {wheel_file.name}", file=sys.stderr)
251+
252+
154253
def build_editable(wheel_directory, config_settings=None, metadata_directory=None):
155254
_build_cuda_core()
156-
return _build_meta.build_editable(wheel_directory, config_settings, metadata_directory)
255+
wheel_name = _build_meta.build_editable(wheel_directory, config_settings, metadata_directory)
256+
257+
# Patch the .pth file to add Cython include paths
258+
wheel_path = os.path.join(wheel_directory, wheel_name)
259+
_add_cython_include_paths_to_pth(wheel_path)
260+
261+
return wheel_name
157262

158263

159264
def build_wheel(wheel_directory, config_settings=None, metadata_directory=None):

0 commit comments

Comments
 (0)