Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion scripts/refresh_nightly_stacks.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
r"^(?P<name>[A-Za-z0-9_]+)-(?P<version>[A-Za-z0-9.!+]+)"
r"-(?P<pytag>[a-z0-9]+)-(?P<abitag>[a-z0-9]+)-(?P<plattag>[a-z0-9_.]+)\.whl$"
)
ANY_ABI = "any"
DEV_DATE_RE = re.compile(r"\.dev(\d{8})\+")
ANCHOR_TEXT_RE = re.compile(r">([^<>]+\.whl)</a>")

Expand Down Expand Up @@ -64,12 +65,22 @@ def abi_for_wheel(pytag, abitag):
"""`major.minor` for a standard CPython wheel; None for anything else
(freethreaded cp31Xt, abi3, pypy) - the desktop app's venvs are
standard CPython."""
if pytag == "py3" and abitag == "none":
return ANY_ABI
m = re.fullmatch(r"cp3(\d+)", pytag)
if not m or abitag != pytag:
return None
return f"3.{m.group(1)}"


def shared_abis(abi_sets):
"""ABIs common to every set; a set holding ANY_ABI fits all of them."""
concrete = [s for s in abi_sets if ANY_ABI not in s]
if not concrete:
return set()
return set.intersection(*concrete)


def fetch_index(tag, package):
"""(version, platform, abi) triples for one package's dated nightly
wheels on desktop-relevant platforms."""
Expand Down Expand Up @@ -142,7 +153,7 @@ def resolve_tag(tag, today):
for p in ("win32", "linux"):
if not all(p in s["plat_abis"] for s in slots.values()):
continue
shared = set.intersection(*(s["plat_abis"][p] for s in slots.values()))
shared = shared_abis([s["plat_abis"][p] for s in slots.values()])
if shared:
abis_by_platform[p] = shared
if not abis_by_platform:
Expand Down
44 changes: 44 additions & 0 deletions scripts/test_torch_index_scripts.py
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,23 @@ def test_parse_index_filters_wheels(self):
],
)

def test_parse_index_accepts_py3_none_wheels(self):
html = index_html(
[
"torchvision-0.30.0.dev20261008%2Bcu132-py3-none-win_amd64.whl",
"torchvision-0.30.0.dev20261008%2Bcu132-py3-none-manylinux_2_28_x86_64.whl",
"torchvision-0.30.0.dev20261008%2Bcu132-py3-none-manylinux_2_28_aarch64.whl",
]
)
wheels = refresher.parse_index("torchvision", "cu132", html)
self.assertEqual(
sorted(wheels),
[
("0.30.0.dev20261008+cu132", "linux", "any"),
("0.30.0.dev20261008+cu132", "win32", "any"),
],
)


def fake_indexes(data):
"""fetch_index replacement: data[package] = list of wheel triples."""
Expand Down Expand Up @@ -359,6 +376,33 @@ def test_full_coverage_keeps_both_platforms(self):
self.assertEqual(entry["pythonAbis"], ["3.12", "3.13"])
self.assertEqual(entry["packages"]["torch"], f"2.14.0.dev{day}+cu132")

def test_py3_none_companions_take_torch_abis(self):
today = utc_today()
day = (today - timedelta(days=1)).strftime("%Y%m%d")
both = {"win32": ["3.12", "3.13"], "linux": ["3.12"]}
universal = {"win32": ["any"], "linux": ["any"]}
data = {
"torch": triples("2.16.0", day, both),
"torchvision": triples("0.30.0", day, universal),
"torchaudio": triples("2.11.0", day, universal),
}
entry, err, _ = self.resolve(data)
self.assertIsNone(err)
self.assertEqual(sorted(entry["platforms"]), ["linux", "win32"])
self.assertEqual(entry["pythonAbis"], ["3.12"])

def test_all_py3_none_has_no_abi_to_advertise(self):
today = utc_today()
day = (today - timedelta(days=1)).strftime("%Y%m%d")
universal = {"win32": ["any"]}
data = {
pkg: triples(ver, day, universal)
for pkg, ver in (("torch", "2.16.0"), ("torchvision", "0.30.0"), ("torchaudio", "2.11.0"))
}
entry, err, _ = self.resolve(data)
self.assertIsNone(entry)
self.assertIsNotNone(err)

def test_one_platform_abi_gap_shrinks_global_abis(self):
# torchaudio missing linux 3.12: 3.12 must NOT be advertised
# globally, but both platforms stay listed via the shared 3.13.
Expand Down
Loading