From 903b394cfc904b807ff36e8c1f2ff1261a578363 Mon Sep 17 00:00:00 2001 From: Rob Parolin Date: Sat, 25 Apr 2026 01:12:35 -0700 Subject: [PATCH 1/5] fix(test): unblock root pixi run test workflow `pixi run test` failed at the cuda_bindings build stage because list-form pixi `cmd` arrays didn't expand `$PIXI_ENVIRONMENT_NAME` reliably, so the inner per-package `pixi run` calls picked the cuda_bindings default environment (no cuda-version pin). The conda solver then resolved cuda-version=12.9 and the build failed with a missing `CUatomicOperation_enum` (a CUDA-13.x-only symbol). Wrap the three test-* tasks in `bash -c '...'` so the shell expands `$PIXI_ENVIRONMENT_NAME` and forward it explicitly via `-e` to each inner pixi run. Once the bindings build was unblocked, cuda_core's cython test build hit a second issue: `cythonize` cannot resolve `cimport cuda.bindings.*` against pixi-build's editable install, which exposes the cuda namespace package via a finder hook that Cython's filesystem .pxd resolver does not consult. Replace the `cythonize` CLI invocation with a small Python wrapper that calls `Cython.Build.cythonize()` with an explicit `include_path` resolved from the imported `cuda.bindings` package. Co-Authored-By: Claude Opus 4.7 (1M context) --- cuda_core/tests/cython/build_tests.py | 31 +++++++++++++++++++++++++++ cuda_core/tests/cython/build_tests.sh | 2 +- pixi.toml | 25 ++++++++++----------- 3 files changed, 45 insertions(+), 13 deletions(-) create mode 100644 cuda_core/tests/cython/build_tests.py diff --git a/cuda_core/tests/cython/build_tests.py b/cuda_core/tests/cython/build_tests.py new file mode 100644 index 00000000000..4eea33291e6 --- /dev/null +++ b/cuda_core/tests/cython/build_tests.py @@ -0,0 +1,31 @@ +# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +# The Cython CLI has no --include-path flag, and pixi-build's editable install +# exposes the cuda namespace package through a finder hook that Cython's +# filesystem .pxd resolver does not consult. Locate the package's parent +# directory at runtime and pass it to cythonize() explicitly. + +import glob +import os +from pathlib import Path + +import cuda.bindings +from Cython.Build import cythonize +from setuptools import setup + +HERE = Path(__file__).resolve().parent +CUDA_PKG_PARENT = Path(cuda.bindings.__file__).parents[2] + +# `setup(... build_ext --inplace)` resolves the .so destination relative to cwd +# and the module's basename, so chdir into HERE before invoking it. +os.chdir(HERE) + +extensions = cythonize( + sorted(glob.glob("test_*.pyx")), + language_level=3, + include_path=[str(CUDA_PKG_PARENT)], + compiler_directives={"freethreading_compatible": True}, +) + +setup(ext_modules=extensions, script_args=["build_ext", "--inplace"]) diff --git a/cuda_core/tests/cython/build_tests.sh b/cuda_core/tests/cython/build_tests.sh index 3e20136133a..01e45b235d6 100755 --- a/cuda_core/tests/cython/build_tests.sh +++ b/cuda_core/tests/cython/build_tests.sh @@ -15,4 +15,4 @@ else exit 1 fi -cythonize -3 -i -Xfreethreading_compatible=True ${SCRIPTPATH}/test_*.pyx +python "${SCRIPTPATH}/build_tests.py" diff --git a/pixi.toml b/pixi.toml index 71d6b5d91bc..d36e8f22986 100644 --- a/pixi.toml +++ b/pixi.toml @@ -19,29 +19,30 @@ PIXI_ENVIRONMENT_NAME = "${PIXI_ENVIRONMENT_NAME/default/cu13}" # Test Tasks # Runs tests across all sub-packages: pathfinder → bindings → core (dependency order) -# Each sub-package has its own pixi.toml; the -e environment propagates via PIXI_ENVIRONMENT_NAME +# Each sub-package has its own pixi.toml; the active environment is forwarded +# explicitly via -e "$PIXI_ENVIRONMENT_NAME" in each inner pixi run. # # Usage: pixi run test | pixi run -e cu12 test | pixi run -e cu13 test [target.linux.tasks.test-pathfinder] cmd = [ - "pixi", - "run", - "--manifest-path", - "$PIXI_PROJECT_ROOT/cuda_pathfinder", - "test", + "bash", + "-c", + 'pixi run --manifest-path "$PIXI_PROJECT_ROOT/cuda_pathfinder" -e "$PIXI_ENVIRONMENT_NAME" test', ] [target.linux.tasks.test-bindings] cmd = [ - "pixi", - "run", - "--manifest-path", - "$PIXI_PROJECT_ROOT/cuda_bindings", - "test", + "bash", + "-c", + 'pixi run --manifest-path "$PIXI_PROJECT_ROOT/cuda_bindings" -e "$PIXI_ENVIRONMENT_NAME" test', ] [target.linux.tasks.test-core] -cmd = ["pixi", "run", "--manifest-path", "$PIXI_PROJECT_ROOT/cuda_core", "test"] +cmd = [ + "bash", + "-c", + 'pixi run --manifest-path "$PIXI_PROJECT_ROOT/cuda_core" -e "$PIXI_ENVIRONMENT_NAME" test', +] [target.linux.tasks.test] depends-on = [ From 241b3e6391eb623f363372eab43ef360f8ad3054 Mon Sep 17 00:00:00 2001 From: Rob Parolin Date: Sat, 25 Apr 2026 01:15:41 -0700 Subject: [PATCH 2/5] fix(test): satisfy ruff isort grouping in build_tests.py Co-Authored-By: Claude Opus 4.7 (1M context) --- cuda_core/tests/cython/build_tests.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cuda_core/tests/cython/build_tests.py b/cuda_core/tests/cython/build_tests.py index 4eea33291e6..47f2c8eb6da 100644 --- a/cuda_core/tests/cython/build_tests.py +++ b/cuda_core/tests/cython/build_tests.py @@ -10,10 +10,11 @@ import os from pathlib import Path -import cuda.bindings from Cython.Build import cythonize from setuptools import setup +import cuda.bindings + HERE = Path(__file__).resolve().parent CUDA_PKG_PARENT = Path(cuda.bindings.__file__).parents[2] From d9668c6ed0856966965da2985ee8a8c8bc6e37e4 Mon Sep 17 00:00:00 2001 From: Rob Parolin Date: Sat, 25 Apr 2026 01:24:18 -0700 Subject: [PATCH 3/5] fix(test): replace cython build wrapper with PYTHONPATH shim Drop cuda_core/tests/cython/build_tests.py in favor of a small PYTHONPATH shim in build_tests.sh. Same outcome (Cython's .pxd resolver finds cuda.bindings via the package's parent directory), three lines instead of a separate setuptools entry point. Co-Authored-By: Claude Opus 4.7 (1M context) --- cuda_core/tests/cython/build_tests.py | 32 --------------------------- cuda_core/tests/cython/build_tests.sh | 9 +++++++- 2 files changed, 8 insertions(+), 33 deletions(-) delete mode 100644 cuda_core/tests/cython/build_tests.py diff --git a/cuda_core/tests/cython/build_tests.py b/cuda_core/tests/cython/build_tests.py deleted file mode 100644 index 47f2c8eb6da..00000000000 --- a/cuda_core/tests/cython/build_tests.py +++ /dev/null @@ -1,32 +0,0 @@ -# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 - -# The Cython CLI has no --include-path flag, and pixi-build's editable install -# exposes the cuda namespace package through a finder hook that Cython's -# filesystem .pxd resolver does not consult. Locate the package's parent -# directory at runtime and pass it to cythonize() explicitly. - -import glob -import os -from pathlib import Path - -from Cython.Build import cythonize -from setuptools import setup - -import cuda.bindings - -HERE = Path(__file__).resolve().parent -CUDA_PKG_PARENT = Path(cuda.bindings.__file__).parents[2] - -# `setup(... build_ext --inplace)` resolves the .so destination relative to cwd -# and the module's basename, so chdir into HERE before invoking it. -os.chdir(HERE) - -extensions = cythonize( - sorted(glob.glob("test_*.pyx")), - language_level=3, - include_path=[str(CUDA_PKG_PARENT)], - compiler_directives={"freethreading_compatible": True}, -) - -setup(ext_modules=extensions, script_args=["build_ext", "--inplace"]) diff --git a/cuda_core/tests/cython/build_tests.sh b/cuda_core/tests/cython/build_tests.sh index 01e45b235d6..c92f3dd04eb 100755 --- a/cuda_core/tests/cython/build_tests.sh +++ b/cuda_core/tests/cython/build_tests.sh @@ -15,4 +15,11 @@ else exit 1 fi -python "${SCRIPTPATH}/build_tests.py" +# pixi-build's editable install exposes the cuda namespace package via a +# finder hook that Cython's filesystem .pxd resolver does not consult. +# Surface the package's parent directory on PYTHONPATH so `cimport +# cuda.bindings.*` can locate the .pxd files. +CUDA_PKG_PARENT=$(python -c "import cuda.bindings as m, os; print(os.path.dirname(os.path.dirname(os.path.dirname(m.__file__))))") +export PYTHONPATH="${CUDA_PKG_PARENT}${PYTHONPATH:+:${PYTHONPATH}}" + +cythonize -3 -i -Xfreethreading_compatible=True ${SCRIPTPATH}/test_*.pyx From ee5aff5e3a5ede1189e681a92d083cbc85a2aca0 Mon Sep 17 00:00:00 2001 From: Rob Parolin Date: Fri, 1 May 2026 14:19:04 -0700 Subject: [PATCH 4/5] fix(test): use Cython include_path wrapper for editable installs Replace the PYTHONPATH shim in cuda_core/tests/cython/build_tests.sh with a small Python driver (build_tests.py) that calls Cython.Build.cythonize() with an explicit include_path resolved at runtime from cuda.bindings.__file__. Avoids platform-specific PYTHONPATH separator handling and surfaces missing-import failures as Python exceptions instead of silent fallbacks. Apply the same wrapper pattern to cuda_bindings/tests/cython/ for symmetry; both shell scripts gain `set -eo pipefail` and `${VAR:-}` defaults so the previously optional CPLUS_INCLUDE_PATH / CL env vars keep working under stricter error mode. Expand the pixi.toml comment block to document why each test-* task is wrapped in `bash -c '...'` and note Linux-only scope. Verified end-to-end: `pixi run test` passes 4,314 tests (974 pathfinder + 384 bindings + 2,956 core), 208 skipped, 2 xfailed. Co-Authored-By: Claude Opus 4.7 (1M context) --- cuda_bindings/tests/cython/build_tests.py | 53 +++++++++++++++++++++++ cuda_bindings/tests/cython/build_tests.sh | 13 ++++-- cuda_core/tests/cython/build_tests.py | 52 ++++++++++++++++++++++ cuda_core/tests/cython/build_tests.sh | 17 +++----- pixi.toml | 15 +++++-- 5 files changed, 133 insertions(+), 17 deletions(-) create mode 100644 cuda_bindings/tests/cython/build_tests.py create mode 100644 cuda_core/tests/cython/build_tests.py diff --git a/cuda_bindings/tests/cython/build_tests.py b/cuda_bindings/tests/cython/build_tests.py new file mode 100644 index 00000000000..bc2d006cfcf --- /dev/null +++ b/cuda_bindings/tests/cython/build_tests.py @@ -0,0 +1,53 @@ +# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: LicenseRef-NVIDIA-SOFTWARE-LICENSE +"""Build cuda_bindings Cython test extensions in-place. + +pixi-build's editable install exposes the `cuda` namespace package via a +PEP 660 finder hook. Python's import machinery honors the hook, but +Cython's filesystem .pxd resolver only walks real directories on sys.path, +so `cimport cuda.bindings.*` fails to locate the .pxd files. We resolve +the namespace package's source root from `cuda.bindings.__file__` and pass +it via `include_path=` so cythonize finds the .pxd tree on every platform. +""" + +from __future__ import annotations + +import sys +from pathlib import Path + +import cuda.bindings +from Cython.Build import cythonize +from setuptools import setup + + +def _bindings_source_root() -> Path: + # cuda.bindings.__file__ -> ...//cuda/bindings/__init__.py + root = Path(cuda.bindings.__file__).resolve().parents[2] + if not (root / "cuda" / "bindings").is_dir(): + raise RuntimeError( + f"cuda.bindings source tree not found at {root}; " + "pixi-build editable install layout may have changed." + ) + return root + + +def main() -> None: + script_dir = Path(__file__).resolve().parent + pyx_files = sorted(str(p) for p in script_dir.glob("test_*.pyx")) + if not pyx_files: + raise SystemExit(f"no test_*.pyx files under {script_dir}") + + ext_modules = cythonize( + pyx_files, + language_level=3, + nthreads=1, + include_path=[str(_bindings_source_root())], + compiler_directives={"freethreading_compatible": True}, + ) + + sys.argv = [sys.argv[0], "build_ext", "--inplace"] + setup(name="cuda_bindings_cython_tests", ext_modules=ext_modules) + + +if __name__ == "__main__": + main() diff --git a/cuda_bindings/tests/cython/build_tests.sh b/cuda_bindings/tests/cython/build_tests.sh index c2ddc9ea79d..12cf46f7ffa 100755 --- a/cuda_bindings/tests/cython/build_tests.sh +++ b/cuda_bindings/tests/cython/build_tests.sh @@ -1,4 +1,5 @@ #!/bin/bash +set -eo pipefail # SPDX-FileCopyrightText: Copyright (c) 2024-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. # SPDX-License-Identifier: LicenseRef-NVIDIA-SOFTWARE-LICENSE @@ -6,13 +7,17 @@ UNAME=$(uname) if [ "$UNAME" == "Linux" ] ; then SCRIPTPATH=$(dirname $(realpath "$0")) - export CPLUS_INCLUDE_PATH=$CUDA_HOME/include:$CPLUS_INCLUDE_PATH + export CPLUS_INCLUDE_PATH=$CUDA_HOME/include:${CPLUS_INCLUDE_PATH:-} elif [[ "$UNAME" == CYGWIN* || "$UNAME" == MINGW* || "$UNAME" == MSYS* ]] ; then SCRIPTPATH="$(dirname $(cygpath -w $(realpath "$0")))" - export CL="/I\"${CUDA_HOME}\\include\" ${CL}" + export CL="/I\"${CUDA_HOME}\\include\" ${CL:-}" else exit 1 fi -# Use -j 1 to side-step any process-pool issues and ensure deterministic single-threaded builds -cythonize -3 -j 1 -i -Xfreethreading_compatible=True ${SCRIPTPATH}/test_*.pyx +# Use a Python driver so the cuda.bindings source root is resolved at +# runtime and passed via Cython's include_path -- avoids platform-specific +# PYTHONPATH separator handling and surfaces import errors as exceptions. +# nthreads=1 inside the driver mirrors the previous `-j 1` to side-step +# any process-pool issues and keep builds deterministic. +python "${SCRIPTPATH}/build_tests.py" diff --git a/cuda_core/tests/cython/build_tests.py b/cuda_core/tests/cython/build_tests.py new file mode 100644 index 00000000000..8f69604cb8f --- /dev/null +++ b/cuda_core/tests/cython/build_tests.py @@ -0,0 +1,52 @@ +# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 +"""Build cuda_core Cython test extensions in-place. + +pixi-build's editable install exposes the `cuda` namespace package via a +PEP 660 finder hook. Python's import machinery honors the hook, but +Cython's filesystem .pxd resolver only walks real directories on sys.path, +so `cimport cuda.bindings.*` fails to locate the .pxd files. We resolve +the namespace package's source root from `cuda.bindings.__file__` and pass +it via `include_path=` so cythonize finds the .pxd tree on every platform. +""" + +from __future__ import annotations + +import sys +from pathlib import Path + +import cuda.bindings +from Cython.Build import cythonize +from setuptools import setup + + +def _bindings_source_root() -> Path: + # cuda.bindings.__file__ -> ...//cuda/bindings/__init__.py + root = Path(cuda.bindings.__file__).resolve().parents[2] + if not (root / "cuda" / "bindings").is_dir(): + raise RuntimeError( + f"cuda.bindings source tree not found at {root}; " + "pixi-build editable install layout may have changed." + ) + return root + + +def main() -> None: + script_dir = Path(__file__).resolve().parent + pyx_files = sorted(str(p) for p in script_dir.glob("test_*.pyx")) + if not pyx_files: + raise SystemExit(f"no test_*.pyx files under {script_dir}") + + ext_modules = cythonize( + pyx_files, + language_level=3, + include_path=[str(_bindings_source_root())], + compiler_directives={"freethreading_compatible": True}, + ) + + sys.argv = [sys.argv[0], "build_ext", "--inplace"] + setup(name="cuda_core_cython_tests", ext_modules=ext_modules) + + +if __name__ == "__main__": + main() diff --git a/cuda_core/tests/cython/build_tests.sh b/cuda_core/tests/cython/build_tests.sh index c92f3dd04eb..7ee65c50d68 100755 --- a/cuda_core/tests/cython/build_tests.sh +++ b/cuda_core/tests/cython/build_tests.sh @@ -1,4 +1,5 @@ #!/bin/bash +set -eo pipefail # SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. # SPDX-License-Identifier: Apache-2.0 @@ -6,20 +7,16 @@ UNAME=$(uname) if [ "$UNAME" == "Linux" ] ; then SCRIPTPATH=$(dirname $(realpath "$0")) - export CPLUS_INCLUDE_PATH=${SCRIPTPATH}/../../cuda/core/_include:$CUDA_HOME/include:$CPLUS_INCLUDE_PATH + export CPLUS_INCLUDE_PATH=${SCRIPTPATH}/../../cuda/core/_include:$CUDA_HOME/include:${CPLUS_INCLUDE_PATH:-} elif [[ "$UNAME" == CYGWIN* || "$UNAME" == MINGW* || "$UNAME" == MSYS* ]] ; then SCRIPTPATH="$(dirname $(cygpath -w $(realpath "$0")))" CUDA_CORE_INCLUDE_PATH=$(echo "${SCRIPTPATH}\..\..\cuda\core\_include" | sed 's/\\/\\\\/g') - export CL="/I\"${CUDA_CORE_INCLUDE_PATH}\" /I\"${CUDA_HOME}\\include\" ${CL}" + export CL="/I\"${CUDA_CORE_INCLUDE_PATH}\" /I\"${CUDA_HOME}\\include\" ${CL:-}" else exit 1 fi -# pixi-build's editable install exposes the cuda namespace package via a -# finder hook that Cython's filesystem .pxd resolver does not consult. -# Surface the package's parent directory on PYTHONPATH so `cimport -# cuda.bindings.*` can locate the .pxd files. -CUDA_PKG_PARENT=$(python -c "import cuda.bindings as m, os; print(os.path.dirname(os.path.dirname(os.path.dirname(m.__file__))))") -export PYTHONPATH="${CUDA_PKG_PARENT}${PYTHONPATH:+:${PYTHONPATH}}" - -cythonize -3 -i -Xfreethreading_compatible=True ${SCRIPTPATH}/test_*.pyx +# Use a Python driver so the cuda.bindings source root is resolved at +# runtime and passed via Cython's include_path -- avoids platform-specific +# PYTHONPATH separator handling and surfaces import errors as exceptions. +python "${SCRIPTPATH}/build_tests.py" diff --git a/pixi.toml b/pixi.toml index d36e8f22986..f73d299e012 100644 --- a/pixi.toml +++ b/pixi.toml @@ -18,9 +18,18 @@ docs = { features = [], solve-group = "docs" } PIXI_ENVIRONMENT_NAME = "${PIXI_ENVIRONMENT_NAME/default/cu13}" # Test Tasks -# Runs tests across all sub-packages: pathfinder → bindings → core (dependency order) -# Each sub-package has its own pixi.toml; the active environment is forwarded -# explicitly via -e "$PIXI_ENVIRONMENT_NAME" in each inner pixi run. +# Runs tests across all sub-packages: pathfinder → bindings → core (dependency order). +# +# Each sub-package has its own pixi.toml. We wrap each task in `bash -c '...'` +# (single-quoted in TOML, double-quoted inside) because pixi `cmd` arrays do +# not expand shell variables -- without this, the inner `pixi run` would drop +# into the sub-package's `default` environment, which lacks a cuda-version +# pin, causing the conda solver to pick a mismatched CUDA toolkit. Forwarding +# `-e "$PIXI_ENVIRONMENT_NAME"` keeps the active environment consistent end +# to end. +# +# Linux-only for now; Windows/macOS root test-task parity is tracked +# separately. # # Usage: pixi run test | pixi run -e cu12 test | pixi run -e cu13 test [target.linux.tasks.test-pathfinder] From 1ebe10f6c4ced2252089e5ef64cd18ae2f13f474 Mon Sep 17 00:00:00 2001 From: Rob Parolin Date: Fri, 1 May 2026 14:23:18 -0700 Subject: [PATCH 5/5] style: ruff isort grouping and format in build_tests.py Co-Authored-By: Claude Opus 4.7 (1M context) --- cuda_bindings/tests/cython/build_tests.py | 6 +++--- cuda_core/tests/cython/build_tests.py | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/cuda_bindings/tests/cython/build_tests.py b/cuda_bindings/tests/cython/build_tests.py index bc2d006cfcf..53e66070634 100644 --- a/cuda_bindings/tests/cython/build_tests.py +++ b/cuda_bindings/tests/cython/build_tests.py @@ -15,18 +15,18 @@ import sys from pathlib import Path -import cuda.bindings from Cython.Build import cythonize from setuptools import setup +import cuda.bindings + def _bindings_source_root() -> Path: # cuda.bindings.__file__ -> ...//cuda/bindings/__init__.py root = Path(cuda.bindings.__file__).resolve().parents[2] if not (root / "cuda" / "bindings").is_dir(): raise RuntimeError( - f"cuda.bindings source tree not found at {root}; " - "pixi-build editable install layout may have changed." + f"cuda.bindings source tree not found at {root}; pixi-build editable install layout may have changed." ) return root diff --git a/cuda_core/tests/cython/build_tests.py b/cuda_core/tests/cython/build_tests.py index 8f69604cb8f..af08ab25ea4 100644 --- a/cuda_core/tests/cython/build_tests.py +++ b/cuda_core/tests/cython/build_tests.py @@ -15,18 +15,18 @@ import sys from pathlib import Path -import cuda.bindings from Cython.Build import cythonize from setuptools import setup +import cuda.bindings + def _bindings_source_root() -> Path: # cuda.bindings.__file__ -> ...//cuda/bindings/__init__.py root = Path(cuda.bindings.__file__).resolve().parents[2] if not (root / "cuda" / "bindings").is_dir(): raise RuntimeError( - f"cuda.bindings source tree not found at {root}; " - "pixi-build editable install layout may have changed." + f"cuda.bindings source tree not found at {root}; pixi-build editable install layout may have changed." ) return root