Skip to content

Commit 83b2d68

Browse files
authored
Merge preview (cp315) numpy wheels from scientific-python nightly wheelhouse into index (#8367)
## Summary numpy is a `PACKAGE_LINKS_ALLOW_LIST` package: its index on download.pytorch.org (e.g. https://download.pytorch.org/whl/nightly/numpy) is a mirror of PyPI's simple index, generated by `update_dependencies.py`. PyPI does **not** publish cp315 wheels, so pip on Python 3.15 falls back to building numpy from source and fails during torch preview-Python (3.15 / 3.15t) builds and smoke tests. The [scientific-python nightly wheelhouse](https://pypi.anaconda.org/scientific-python-nightly-wheels/simple/numpy) publishes cp315 / cp315t numpy wheels for **all platforms** (Linux, macOS, Windows). This PR merges those wheel URLs into the numpy index so pip can resolve a real wheel. ## How it works In `update_dependencies.py`, `upload_package_using_simple_index()` now calls `append_preview_numpy_wheels()` after fetching the PyPI index: - `fetch_preview_numpy_anchors()` downloads `https://pypi.anaconda.org/scientific-python-nightly-wheels/simple/numpy/` and extracts every wheel whose ABI tag is in `PREVIEW_PYTHON_TAGS` (`cp315`), absolutizing each href against `https://pypi.anaconda.org`. - Those links are merged into the mirrored index before `</body>`, skipping any already present (PyPI links for every other version are untouched). - If the upstream index is unreachable or lists no preview wheels, the index is left unchanged — a no-op. Restricted to **numpy** on the **nightly** and **test** channels only (`PREVIEW_WHEEL_INJECT_PACKAGES` / `PREVIEW_WHEEL_INJECT_PREFIXES`); no other package or channel is affected. `manage_v2.py` copies this index into the per-CUDA subdirectories as today, and the absolute URLs stay valid there. ## Notes - Supersedes the earlier approach of hosting self-built numpy wheels on download.pytorch.org — the links now point directly at the upstream nightly wheelhouse. - `PREVIEW_PYTHON_TAGS` is the single knob to extend this to future preview interpreters. - Verified against the live upstream index: 22 preview anchors (11 cp315 + 11 cp315t, all platforms), absolute URLs, dedup preserves existing PyPI links.
1 parent 5655be5 commit 83b2d68

1 file changed

Lines changed: 104 additions & 0 deletions

File tree

s3_management/update_dependencies.py

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -758,6 +758,28 @@
758758
"pycparser": [{"project": "vllm"}],
759759
}
760760

761+
# Preview CPython ABI tags whose wheels are not yet published on PyPI. e.g.
762+
# numpy has no cp315 wheel on PyPI, so pip on Python 3.15 would fall back to a
763+
# source build and fail. The scientific-python nightly wheelhouse does publish
764+
# them, so we merge those links into our (otherwise PyPI-mirrored) numpy index.
765+
PREVIEW_PYTHON_TAGS = ("cp315",)
766+
767+
# Restrict the preview-wheel injection to numpy on the nightly and test channels
768+
# only. Nothing else is touched.
769+
PREVIEW_WHEEL_INJECT_PACKAGES = {"numpy"}
770+
PREVIEW_WHEEL_INJECT_PREFIXES = {"whl/nightly", "whl/test"}
771+
772+
# Upstream source of preview numpy wheels (scientific-python nightly wheelhouse).
773+
PREVIEW_NUMPY_INDEX_URL = (
774+
"https://pypi.anaconda.org/scientific-python-nightly-wheels/simple/numpy/"
775+
)
776+
# Host used to absolutize the relative hrefs served by that index.
777+
PREVIEW_NUMPY_INDEX_BASE = "https://pypi.anaconda.org"
778+
# Only merge wheels from this numpy release line. Matches the final release and
779+
# its pre-releases (e.g. 2.6.0, 2.6.0.dev0, 2.6.0rc1) but not other lines such
780+
# as 2.7.0.dev0.
781+
PREVIEW_NUMPY_VERSION = "2.6.0"
782+
761783

762784
def is_nvidia_package(pkg_name: str) -> bool:
763785
"""Check if a package is from NVIDIA and should use pypi.nvidia.com"""
@@ -877,6 +899,84 @@ def upload_index_html(
877899
)
878900

879901

902+
def normalize_pkg_name(name: str) -> str:
903+
"""PEP 503 normalisation: lowercase, collapse ``[-_.]+`` to ``_``."""
904+
return re.sub(r"[-_.]+", "_", name).lower()
905+
906+
907+
def fetch_preview_numpy_anchors() -> List[tuple[str, str]]:
908+
"""Fetch preview-CPython numpy wheel links from the upstream nightly index.
909+
910+
PyPI does not publish cp315 numpy wheels, but the scientific-python nightly
911+
wheelhouse does. Returns ``(filename, anchor_html)`` pairs for each wheel
912+
whose ABI tag is in :data:`PREVIEW_PYTHON_TAGS`, with the href absolutized
913+
so it stays valid when the index is copied into subdirectories. Returns an
914+
empty list if the index cannot be fetched or has no preview wheels.
915+
"""
916+
try:
917+
html = download(PREVIEW_NUMPY_INDEX_URL).decode("utf-8", errors="ignore")
918+
except Exception as e:
919+
print(f"WARNING: could not fetch {PREVIEW_NUMPY_INDEX_URL}: {e}")
920+
return []
921+
922+
# numpy-<version>-<pytag>-<abitag>-<plat>.whl -> version is the 2nd field.
923+
version_re = re.compile(rf"^{re.escape(PREVIEW_NUMPY_VERSION)}(\D|$)")
924+
925+
anchors: List[tuple[str, str]] = []
926+
for href, name in re.findall(r'<a href="([^"]+)"[^>]*>([^<]+)</a>', html):
927+
filename = name.strip()
928+
if not filename.endswith(".whl"):
929+
continue
930+
if not any(f"-{tag}-" in filename for tag in PREVIEW_PYTHON_TAGS):
931+
continue
932+
parts = filename.split("-")
933+
if len(parts) < 2 or not version_re.match(parts[1]):
934+
continue
935+
# Absolutize the href against the upstream host.
936+
if href.startswith("//"):
937+
url = f"https:{href}"
938+
elif href.startswith(("http://", "https://")):
939+
url = href
940+
else:
941+
url = f"{PREVIEW_NUMPY_INDEX_BASE}/{href.lstrip('/')}"
942+
anchors.append((filename, f' <a href="{url}">{filename}</a><br/>'))
943+
return anchors
944+
945+
946+
def append_preview_numpy_wheels(html: str, pkg_name: str, prefix: str) -> str:
947+
"""Merge upstream preview-CPython numpy wheel links into *html*.
948+
949+
The PyPI simple index does not list preview-CPython (e.g. cp315) wheels, so
950+
we merge the links published on the scientific-python nightly wheelhouse for
951+
any that are not already present.
952+
953+
Limited to numpy on the nightly and test channels; a no-op otherwise.
954+
"""
955+
if (
956+
normalize_pkg_name(pkg_name) not in PREVIEW_WHEEL_INJECT_PACKAGES
957+
or prefix not in PREVIEW_WHEEL_INJECT_PREFIXES
958+
):
959+
return html
960+
961+
additions = [
962+
anchor
963+
for filename, anchor in fetch_preview_numpy_anchors()
964+
if filename not in html
965+
]
966+
967+
if not additions:
968+
return html
969+
970+
print(
971+
f"INFO: Merging {len(additions)} preview numpy wheel link(s) "
972+
f"for {pkg_name} under {prefix}"
973+
)
974+
block = "\n".join(additions)
975+
if "</body>" in html:
976+
return html.replace("</body>", f"{block}\n </body>", 1)
977+
return f"{html}\n{block}\n"
978+
979+
880980
def upload_package_using_simple_index(
881981
pkg_name: str,
882982
prefix: str,
@@ -902,6 +1002,10 @@ def upload_package_using_simple_index(
9021002
print(f"Error fetching package {pkg_name}: {e}")
9031003
return
9041004

1005+
# PyPI does not host preview-CPython (e.g. cp315) wheels; merge in the links
1006+
# published on the scientific-python nightly wheelhouse so pip can resolve them.
1007+
raw_html = append_preview_numpy_wheels(raw_html, pkg_name, prefix)
1008+
9051009
# Upload modified index.html with absolute links
9061010
upload_index_html(pkg_name, prefix, raw_html, source_url, dry_run=dry_run)
9071011

0 commit comments

Comments
 (0)