Skip to content

cuda.core: carry the cuda-bindings floors to pip/uv through minimal-sysctk12/13 extras, and yank 1.3.0 #3459

Description

@leofang

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 cu12 extra requests cuda-core[minimal-sysctk12], installed together with cuda-bindings==12.9.3, resolves to cuda-core 1.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 upgrades cuda-bindings to the newest 12.x.
  • cuda-core[cu12]==1.3.1 with cuda-bindings==12.9.3 has 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.

2. Docs

  • install.rst, "Installing from PyPI": add the minimal-sysctk12/minimal-sysctk13 extras; extend the advice for downstream packages: depend on cuda-core[minimal-sysctk12/13] (no toolkit wheels) or cuda-core[cu12/13] (with toolkit wheels) in CUDA-major-specific requirements, never on plain cuda-core, because plain cuda-core hides the floor from pip and uv and a mismatch shows up only at import.
  • support.rst, "cuda-bindings Version Requirements": the floors are declared by minimal-sysctk12/minimal-sysctk13, and reach pip and uv only through those extras or through cu12/cu13.
  • DESCRIPTION.rst (the PyPI page): mention the lean extras next to pip 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; their cu12/cu13 extras pin cuda-bindings but not cuda-core. Have cu12/cu13 extras depend on cuda-core extra numba-cuda-mlir#457 adds cuda-core[cu12]/[cu13]; minimal-sysctk12/minimal-sysctk13 would do the same without pulling cuda-bindings[all] and more toolkit wheels.
  • cuda-cccl 1.3.0 depends on plain cuda-core; its minimal-cu12/13 and minimal-sysctk12/13 extras pin only cuda-bindings.
  • nvmath-python 1.0.0: cu12/cu13 already use cuda-core[cu12]/[cu13]; sysctk12/sysctk13 pin only cuda-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

  1. self-assigned this
    on Nov 13, 2026
  2. added
    packagingAnything related to wheels or Conda packages
    cuda.coreEverything related to the cuda.core module
    P0High priority - Must do!
    on Nov 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

P0High priority - Must do!cuda.coreEverything related to the cuda.core modulepackagingAnything related to wheels or Conda packages

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions