Repository navigation
Description
Since 1.3.0, cuda-core declares its cuda-bindings floors (>=12.9.8 for CUDA 12, >=13.5.0 for CUDA 13; #2783, #2920) only in the cu12/cu13 extras, and enforces them at import time. A package that depends on plain cuda-core therefore never passes the floor to the resolver. pip and uv install cuda-core 1.3.0 next to an older cuda-bindings, pip check passes (it ignores requirements gated on extras), and the import fails:
$ pip install "cuda-core==1.3.0" "cuda-bindings==12.9.3" # exit 0
$ pip check
No broken requirements found.
$ python -c "import cuda.core"
ImportError: cuda.core 1.3.0 requires cuda-bindings >= 12.9.8 for CUDA 12, but cuda-bindings 12.9.3 is installed. Upgrade with: pip install -U "cuda-bindings>=12.9.8,<13"
This is how cuDF's oldest-deps CI broke: it pins cuda-bindings==12.9.3 and gets cuda-core only through numba-cuda-mlir, which depends on plain cuda-core (NVIDIA/cudf#25224, NVIDIA/numba-cuda-mlir#456).
The two floors cannot go into a single unconditional requirement: comma-separated specifiers are intersected, and a != list of the excluded minors would need an edit at every floor move.
Proposal
Add lean extras that carry only the floor (the names cuda-cccl already uses), and build cu12/cu13 on top of them:
dependencies = [
"cuda-pathfinder >=1.4.2",
"numpy",
]
[project.optional-dependencies]
minimal-sysctk12 = ["cuda-bindings>=12.9.8,<13"]
minimal-sysctk13 = ["cuda-bindings>=13.5.0,<14"]
cu12 = ["cuda-core[minimal-sysctk12]", "cuda-bindings[all]", "cuda-toolkit==12.*"]
cu13 = ["cuda-core[minimal-sysctk13]", "cuda-bindings[all]", "cuda-toolkit==13.*"]Release this as 1.3.1, then yank 1.3.0 on PyPI. The yank is needed because pip and uv do not skip a release that lacks a requested extra: they warn (does not provide the extra / does not have an extra named) and use it. That is harmless for 1.2.x and older, which have no import-time floor, but 1.3.0 would fail at import.
Checked against a local index of empty wheels with this layout (pip 25.0.1 and 26.2.2, uv 0.13.8):
- A package whose
cu12extra requestscuda-core[minimal-sysctk12], installed together withcuda-bindings==12.9.3, resolves tocuda-core1.2.2 once 1.3.0 is yanked (to 1.3.0 while it is not). Installed into an environment that already has 12.9.3, it upgradescuda-bindingsto the newest 12.x. cuda-core[cu12]==1.3.1withcuda-bindings==12.9.3has no solution, so the self-reference carries the floor.
1. Code changes in cuda-core
Everything below reads the floors through floors_from_extras, which accepts only cu<N> extras of the form cuda-bindings>=<floor>,<<next major>. With the layout above it raises ValueError: the 'cu12' extra must pin cuda-bindings as '>=<floor>,<<next major>' ... but it lists 'cuda-bindings[all]', so the build and pre-commit fail until it changes.
cuda_core/pyproject.toml: the layout above; update the comment over the extras; add the new extras to[tool.uv].conflicts.cuda_core/cuda/core/_bindings_floor.py: keep reading the floor of eachcu<N>extra, but follow acuda-core[...]self-reference and skip an unversionedcuda-bindings[all], so thecu<N>layout above yields the floor written inminimal-sysctk<N>(and the module docstring namesminimal-sysctk<N>as the single source).ci/tools/cuda_core_bindings_floor.pyreads the floor from the wheel METADATA line of thecu<N>extra. With the new layout it exits withMETADATA pins cuda-bindings without a floor for the cu12 extra. Make it follow thecuda-core[minimal-sysctk<N>]line of thecu<N>extra in the same way.- These callers work unchanged once the parser does, but their docstrings or messages name
cu<N>:build_hooks.py,toolshed/check_cuda_core_bindings_floor.py,docs/source/conf.py,ci/tools/bindings_config.py. - Tests: keep the existing tests of the
cu<N>form unchanged, and add tests for the self-reference, intests/test_bindings_floor.py,tests/test_build_hooks.py,ci/tools/tests/test_cuda_core_bindings_floor.py. cuda_core/AGENTS.md: the "Bumping the cuda-bindings floor" checklist and the toolkit-bump window note should nameminimal-sysctk<N>.- Outside this repo: the conda-forge recipe pins the floor per CUDA variant and needs no packaging change; its comment that points at the
cu12/cu13extras can be updated.
2. Docs
install.rst, "Installing from PyPI": add theminimal-sysctk12/minimal-sysctk13extras; extend the advice for downstream packages: depend oncuda-core[minimal-sysctk12/13](no toolkit wheels) orcuda-core[cu12/13](with toolkit wheels) in CUDA-major-specific requirements, never on plaincuda-core, because plaincuda-corehides the floor from pip and uv and a mismatch shows up only at import.support.rst, "cuda-bindings Version Requirements": the floors are declared byminimal-sysctk12/minimal-sysctk13, and reach pip and uv only through those extras or throughcu12/cu13.DESCRIPTION.rst(the PyPI page): mention the lean extras next topip install cuda-core[cu12].- New
docs/source/release/1.3.1-notes.rst: the new extras, and that 1.3.0 was yanked and why.
3. Downstream
Packages that depend on cuda-core should request one of these extras from their own CUDA-major-specific extras, and cut a release:
cuda-core[minimal-sysctk12]/[minimal-sysctk13]when the CUDA Toolkit comes from the system or from their own requirements;cuda-core[cu12]/[cu13]when they also want the toolkit wheels.
Current PyPI metadata of known dependents:
- numba-cuda-mlir 0.6.0 and numba-cuda 0.30.4 depend on plain
cuda-core; theircu12/cu13extras pincuda-bindingsbut notcuda-core. Have cu12/cu13 extras depend on cuda-core extra numba-cuda-mlir#457 addscuda-core[cu12]/[cu13];minimal-sysctk12/minimal-sysctk13would do the same without pullingcuda-bindings[all]and more toolkit wheels. - cuda-cccl 1.3.0 depends on plain
cuda-core; itsminimal-cu12/13andminimal-sysctk12/13extras pin onlycuda-bindings. - nvmath-python 1.0.0:
cu12/cu13already usecuda-core[cu12]/[cu13];sysctk12/sysctk13pin onlycuda-bindings.
A cuda-core lower bound is not required: older releases resolve with the warning above, which is harmless once 1.3.0 is yanked. Use cuda-core[...]>=1.3.1 to require the floor-carrying metadata.
CI that pins an old cuda-bindings (for example cuDF's oldest-deps jobs, cuda-bindings==12.9.3 / ==13.0.2) will then resolve to cuda-core 1.2.x instead of breaking at import. To test cuda-core 1.3.x there, raise those pins to the floors.
-- Leo's bot
Activity