[ci][deps] Source torch-scatter from Astral's GPU index and drop the dead data.pyg.org find-links - #65899
Conversation
…dead data.pyg.org find-links data.pyg.org, the only host that ever served prebuilt PyTorch Geometric extension wheels, has been a dangling CNAME since 2026-09-02 (pyg-team/pyg-lib#719). Every ML/data/rllib/doc/windows depset declared it as --find-links, uv fails eagerly on an unreadable find-links URL, and the same line sits at the top of each lock, so both the raydepsets compile job and any cache-miss rebuild of the ML/GPU CI images were broken. #65880 soft-failed the job as a stopgap. - torch-scatter now comes from https://wheels.astral.sh/simple/{cpu,cu128}/, a PEP 503/691 index, so the requirement files use --extra-index-url. Astral ships manylinux only; the CPU pin is gated on sys_platform == 'linux'. - torch-sparse, torch-cluster and torch-spline-conv are dropped: no wheel source remains (PyPI is sdist-only), nothing in Ray imports them, and torch-geometric 2.5.3 neither requires them nor imports them unguarded. - deepspeed is pinned with === (arbitrary equality): Astral's cu128 index also publishes deepspeed 0.18.9+cu.12.8.torch.2.9, a bare ==0.18.9 matches that local version, and unsafe-best-match would otherwise swap deepspeed's build in every GPU depset and pull in nvidia-ml-py. - ci.sh compile_pip_dependencies strips the whole cpu local tag so the compiled file stays a bare torch-scatter==2.1.2 constraint for GPU depsets. - Windows loses the +pt27cpu PyG pins and their relax-list entries. - The raydepsets compile job hard-fails again (soft_fail removed). requirements_compiled.txt and 32 locks regenerated; the package-level delta in every lock is the PyG lines only. Signed-off-by: Elliot Barnwell <elliot.barnwell@anyscale.com> Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Signed-off-by: elliot-barn <elliot.barnwell@anyscale.com>
There was a problem hiding this comment.
Code Review
This pull request updates PyTorch Geometric (PyG) binary dependencies across various configuration and lock files, migrating from the defunct data.pyg.org wheel host to Astral's PEP 503 indexes (wheels.astral.sh). It drops torch-sparse, torch-cluster, and torch-spline-conv due to a lack of wheel sources, keeping only torch-scatter on Linux. Additionally, deepspeed is pinned using arbitrary equality (===) to avoid matching local versions from Astral's index. Feedback on the changes suggests improving the regular expression in ci/ci.sh to robustly handle pre-release or post-release versions and to enhance portability across different sed implementations.
| # this file is also the constraint for the GPU depsets, and only a bare | ||
| # torch-scatter==2.1.2 lets their +cu.12.8.torch.2.9 pin satisfy it. The old | ||
| # pattern stopped at the first "cpu" and left 2.1.2.torch.2.9 behind. | ||
| sed -i -E 's/==([.0-9]+)\+[A-Za-z0-9.]*cpu[A-Za-z0-9.]*/==\1/g' "python/$TARGET" |
There was a problem hiding this comment.
The current regular expression ==([.0-9]+)\+ only matches digits and dots for the public version part. If a pre-release, post-release, or dev-release version (e.g., 2.10.0rc1+cpu or 2.10.0.post0+cpu) is used in the future, this pattern will fail to match the full public version correctly.
Additionally, using [+] instead of \+ is more portable across different sed implementations (such as BSD sed on macOS vs GNU sed on Linux) when running in Extended Regular Expression (-E) mode, as some engines can have quirks with escaped metacharacters.
Updating the pattern to ([A-Za-z0-9.]+) and [+] resolves both issues robustly.
| sed -i -E 's/==([.0-9]+)\+[A-Za-z0-9.]*cpu[A-Za-z0-9.]*/==\1/g' "python/$TARGET" | |
| sed -i -E 's/==([A-Za-z0-9.]+)[+][A-Za-z0-9.]*cpu[A-Za-z0-9.]*/==\1/g' "python/$TARGET" |
There was a problem hiding this comment.
Taken in 7bbfe65. Verified the widened class is identical on every current line shape and a no-op on the regenerated requirements_compiled.txt; it only changes behaviour for pre/post/dev public versions, which the old [.0-9]+ left unstripped.
Review suggestion: the first capture group only admitted digits and dots, so a future torch==2.10.0rc1+cpu or 2.10.0.post0+cpu pin would have escaped the strip and conflicted as a constraint. No-op on the current compiled file. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Signed-off-by: elliot-barn <elliot.barnwell@anyscale.com>
| packages: | ||
| - torch | ||
| - torchvision | ||
| # ABI-coupled PyG binaries; re-pinned as +pt27cpu builds in |
There was a problem hiding this comment.
no longer windows requirements
|
Converting to draft: verified empirically (torch 2.9.0 + torch-geometric 2.5.3, none of the four extensions installed) that |
…es into the py3.14 snapshot torch_geometric.loader.NeighborSampler builds a SparseTensor in __init__ and raises "ImportError: 'SparseTensor' requires 'torch-sparse'" without it; python/ray/train/examples/pytorch_geometric/distributed_sage_example.py (CI test distributed_sage_example, train_v2_gpu) uses it, and torch_sparse imports torch_scatter. No wheel source for torch-sparse exists other than data.pyg.org (PyPI is sdist-only; not on Astral, download.pytorch.org or pypi.nvidia.com), and that host is a flat page, not a PEP 503 index, so it stays on --find-links. Pinned to the explicit local builds so a missing wheel fails loudly rather than falling back to the sdist; gated on Linux like torch-scatter since torch_sparse cannot import without it and nothing on Windows or macOS uses PyG. torch-cluster and torch-spline-conv stay dropped: the example's full path trains with scatter + sparse only. requirements_compiled_py3.14.txt has no generator and is maintained by mirroring the hunks applied to requirements_compiled.txt; the same header and pin deltas are applied to it here, keeping its own torch-2.7.0 find-links. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Signed-off-by: elliot-barn <elliot.barnwell@anyscale.com>
|
Un-drafting. |
There was a problem hiding this comment.
Code Review
This pull request updates PyTorch Geometric (PyG) dependency management to resolve issues with PyG's dangled wheel host. It migrates torch-scatter to Astral's simple PEP 503 indexes, restricts torch-scatter and torch-sparse to Linux, and drops unused extensions (torch-cluster and torch-spline-conv). Additionally, deepspeed is pinned using arbitrary equality (===) to prevent matching local versions from Astral's index. Feedback on the changes suggests refining the regular expression in ci/ci.sh to fully support PEP 440 local version identifiers by matching hyphens and underscores.
| # pattern stopped at the first "cpu" and left 2.1.2.torch.2.9 behind. The | ||
| # public version may carry a pre/post/dev suffix (2.10.0rc1, 2.10.0.post0), | ||
| # hence letters in the first class. | ||
| sed -i -E 's/==([A-Za-z0-9.]+)[+][A-Za-z0-9.]*cpu[A-Za-z0-9.]*/==\1/g' "python/$TARGET" |
There was a problem hiding this comment.
PEP 440 local version identifiers can contain hyphens (-) and underscores (_) as separators between segments (e.g., +cpu-ubuntu or +cpu_custom). The current regular expression [A-Za-z0-9.]* only matches alphanumeric characters and dots, which would stop matching at a hyphen or underscore, leaving a trailing suffix (e.g., ==2.1.2-ubuntu or ==2.1.2_custom) and resulting in an invalid version specifier.
To make the regex fully robust and PEP 440 compliant, we should include hyphens and underscores in the character class for the local version part.
| sed -i -E 's/==([A-Za-z0-9.]+)[+][A-Za-z0-9.]*cpu[A-Za-z0-9.]*/==\1/g' "python/$TARGET" | |
| sed -i -E 's/==([A-Za-z0-9.]+)[+][A-Za-z0-9._-]*cpu[A-Za-z0-9._-]*/==\1/g' "python/$TARGET" |
There was a problem hiding this comment.
Taken in 0e44f9f. Note pip-compile normalizes local separators to dots (packaging's Version str), so -/_ can't actually appear in the compiled file; the wider class is harmless and verified a no-op on both compiled files.
Review nit: PEP 440 local versions may use -/_ as separators; pip-compile normalizes them to dots so this cannot occur in the compiled file today, but the wider class costs nothing. Verified a no-op on both compiled files. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Signed-off-by: elliot-barn <elliot.barnwell@anyscale.com>
|
Two follow-ups from review:
|
Description
data.pyg.org— the only host that ever served prebuilt wheels for the PyTorch Geometric extensions — has been a dangling CNAME since 2026-09-02 (pyg-team/pyg-lib#719). Every ML/data/rllib/doc/windows depset declared it as--find-links, and uv fails eagerly on an unreadable find-links URL, soraydepsets: compile all dependencieshas been red on master (postmerge 19501, 19507) and was madesoft_failin #65880. The same URL is line 2 of every affected lock, so any cache-miss rebuild of themlbuild/mlgpubuild/mllightning1gpubuildimages (and the core/rllib/doc GPU images) fails atuv pip install -r <lock>too.This PR narrows the dependency on
data.pyg.orgto the one package that needs it, sourcestorch-scatterfrom Astral's index, and re-enables the hard failure:torch-scatternow comes from Astral's GPU indexes (https://wheels.astral.sh/simple/{cpu,cu128}/). These are real PEP 503/691 indexes, so the requirement files use--extra-index-urlinstead of a flat find-links page. Versions encode the torch minor:2.1.2+cpu.torch.2.9/2.1.2+cu.12.8.torch.2.9. Astral publishes manylinux wheels only, so the CPU pin carries; sys_platform == 'linux'; nothing in Ray importstorch_scatteron any platform.torch-sparsestays, still fromdata.pyg.org(which came back on 2026-09-03; pyg-lib#719 closed). It is required:torch_geometric.loader.NeighborSampler.__init__builds aSparseTensorfromtorch_geometric.typing, whose stub raisesImportError: 'SparseTensor' requires 'torch-sparse'when the package is absent — verified empirically on torch 2.9.0 + torch-geometric 2.5.3 — andpython/ray/train/examples/pytorch_geometric/distributed_sage_example.py(CI testdistributed_sage_example,train_v2_gpu) uses it.torch_sparsealso importstorch_scatterin 8 modules, so scatter is a hard dependency of sparse. No other wheel source for torch-sparse exists (PyPI is sdist-only; not on Astral,download.pytorch.orgorpypi.nvidia.com; no GitHub release assets), so the--find-linksfor it remains and this PR does not remove the dependency on that host — it narrows it to one package. Pinned explicitly (+pt29cpu/+pt29cu128) so a missing wheel fails loudly instead of falling back to the sdist.torch-clusterandtorch-spline-convare dropped. Nothing inpython/,rllib/,release/ordoc/imports them,torch-geometric==2.5.3lists none of the four inrequires_dist, and the example's full path (FakeDataset → RandomNodeSplit → NeighborSampler → SAGEConv) trains with only scatter + sparse installed (run in a glibc-2.36 container against the exact pinned wheels).sys_platform == 'linux': Astral ships manylinux wheels only,torch_sparsecannot import withouttorch_scatter, and nothing on Windows or macOS uses PyG. Windows therefore loses the four+pt27cpupins, their find-links, and the matchingrelaxed_windows_testsentries.deepspeed:>=0.12.3→===0.18.9intrain-requirements.txt(PEP 440 arbitrary equality; the compiled constraint follows asdeepspeed==0.18.9→deepspeed===0.18.9). Why: Astral's cu128 index also publishesdeepspeed 0.18.9+cu.12.8.torch.2.9(prebuilt CUDA ops). Under PEP 440 a bare==0.18.9matches that local version, and--index-strategy unsafe-best-matchprefers it, so without this every GPU depset would silently switch to Astral's deepspeed build and pull innvidia-ml-py(verified withuv pip compile).===is the only specifier that excludes local versions, so deepspeed stays on the PyPI build it uses today; uv honours it as requirement and constraint, and pip-tools writes it through torequirements_compiled.txt. The resolved version is unchanged (0.18.9 before and after). If we ever want Astral's prebuilt-ops deepspeed, the opt-in is the reverse edit,===→==— not part of this PR.ci/ci.sh compile_pip_dependencies: the+cpu-stripping sed now removes the whole local segment. The compiled file is the GPU depsets' constraint (via therequirements_compiled_py3.1x.txtsymlinks), and only a baretorch-scatter==2.1.2lets+cu.12.8.torch.2.9satisfy it; the old[^\b]*cpupattern would have left2.1.2.torch.2.9..buildkite/dependencies.rayci.yml:soft_failand its comment removed — the job hard-fails again.--find-linksremoved fromrelease/ray_release/byod/requirements_ml_byod_3.10.txt(it pinned none of the four); docs and stale comments updated.Regenerated:
python/requirements_compiled.txt(./ci/ci.sh compile_pip_dependencies, py3.11 + pip-tools 7.4.1) and all 32 affected locks underpython/deplocks/(bazel run //ci/raydepsets:raydepsets -- build --all-configs). Package-level delta in every lock is only the PyG lines; deepspeed stays0.18.9, no new packages.python/requirements_compiled_py3.14.txthas no generator (it pins torch 2.7.0, which has no cp314 wheels) and has always been maintained by mirroring the hunks applied torequirements_compiled.txt(#65616, #65046, #64056 all changed identical pin counts in both), so the same header/pin deltas were applied to it by hand; its owntorch-2.7.0+cpufind-links is kept to match its torch pin. That line is only ever read by pip:requirements_compiled_py${PYTHON_VERSION}.txtis staged into thebase-deps/base-slimimages as/home/ray/requirements_compiled.txt(those images install from the rayimg lock with uv, not from this file) and then used as-cbyci/docker/ray-image.Dockerfile,docker/ray/Dockerfileandruntime_env_container/Dockerfile— allpip install -c. pip treats an unreachable--find-linksin a constraints file as a warning (5 retries, exit 0; verified against an NXDOMAIN host), whereas uv fails eagerly (exit 2; also verified). The only uv consumer is raydepsets, whose pre-hook strips the header lines. So no py3.10–3.14 image build hard-fails on that line even if data.pyg.org goes dark again; the uv exposure is the--find-linksemitted into the ML/data/rllib/doc locks, which this PR narrows to torch-sparse.Left untouched, flagged for follow-up:
release/ray_release/byod/byod_horovod*.sh(install torch-2.0.1-era PyG wheels from the same dead host — separate, older problem),ci/raydepsets/tests/test_cli.py(fixture strings only).Related issues
Follows up #65880 (temporary
soft_fail). Upstream: pyg-team/pyg-lib#719, pytorch_geometric#10793, pytorch_geometric#10716.Additional information
Not a duplicate: no open PR touches
data.pyg.org,wheels.astral.sh, or the PyG pins (searchedpyg,astral,find-links,torch-scatter,data.pyg.org; only unrelated dependabot torch bumps match).Tests run locally:
./ci/ci.sh compile_pip_dependenciesin a py3.11 venv → exit 0; diff vs master is exactly the two header lines, the PyG pins/markers anddeepspeed===bazel run //ci/raydepsets:raydepsets -- build --all-configs→ exit 0, 107 depsets compiled; the 32 changed locks differ from master only in the PyG lines and the index/find-links header, and thetorch-sparsehashes are unchangedbazel run //ci/raydepsets:raydepsets -- build --all-configs --check(CI's exact command) → exit 0, all 107 depsets regenerate byte-identically (including the py3.14-constrained ones against the mirrored snapshot), working tree clean afterwardspython:3.10-bookworm: the example's path trains with scatter+sparse only; fails without sparse, and sparse fails without scatterpre-commit run --files <changed source files>→ shellcheck, semgrep passuv pip compileprobes:torch-scatter==2.1.2+cu.12.8.torch.2.9resolves fromwheels.astral.sh/simple/cu128/;deepspeed==0.18.9flips to0.18.9+cu.12.8.torch.2.9with that index present,deepspeed===0.18.9does not.AI assistance (Claude Code) was used for this change; every changed line was reviewed by the submitter.
🤖 Generated with Claude Code