From 029a02374d56ee6129df1c0d1519eb819d660d54 Mon Sep 17 00:00:00 2001 From: Agriya Khetarpal <74401230+agriyakhetarpal@users.noreply.github.com> Date: Thu, 7 May 2026 05:01:41 +0530 Subject: [PATCH 01/28] Add `--exclude-newer` for constraints updates --- noxfile.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/noxfile.py b/noxfile.py index 5644369bc..a862615bb 100755 --- a/noxfile.py +++ b/noxfile.py @@ -18,6 +18,7 @@ import os import shutil import sys +from datetime import UTC, datetime, timedelta from pathlib import Path import nox @@ -79,6 +80,8 @@ def update_constraints(session: nox.Session) -> None: env = os.environ.copy() env["UV_CUSTOM_COMPILE_COMMAND"] = f"nox -s {session.name}" + exclude_newer = (datetime.now(tz=UTC).date() - timedelta(days=7)).isoformat() + for minor_version in range(8, 15): python_version = f"3.{minor_version}" output_file = resources / f"constraints-python{python_version.replace('.', '')}.txt" @@ -88,6 +91,7 @@ def update_constraints(session: nox.Session) -> None: "compile", f"--python-version={python_version}", "--upgrade", + f"--exclude-newer={exclude_newer}", resources / "constraints.in", f"--output-file={output_file}", env=env, From 3f9d9445012aaf536a3903451326f6cec2dfda71 Mon Sep 17 00:00:00 2001 From: Agriya Khetarpal <74401230+agriyakhetarpal@users.noreply.github.com> Date: Thu, 7 May 2026 05:08:31 +0530 Subject: [PATCH 02/28] Add a cooldown for `virtualenv` updates --- bin/update_virtualenv.py | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/bin/update_virtualenv.py b/bin/update_virtualenv.py index c51c87250..c4147b746 100755 --- a/bin/update_virtualenv.py +++ b/bin/update_virtualenv.py @@ -17,6 +17,7 @@ import difflib import logging import tomllib +from datetime import UTC, datetime, timedelta from pathlib import Path from typing import Final @@ -41,11 +42,15 @@ ) +COOLDOWN_DAYS = 7 + + @dataclasses.dataclass(frozen=True, order=True) class VersionTuple: name: str download_url: str version: Version + published_at: datetime = dataclasses.field(compare=False) def get_latest_virtualenv_release() -> VersionTuple: @@ -60,8 +65,12 @@ def get_latest_virtualenv_release() -> VersionTuple: msg = "No asset named 'virtualenv.pyz' found in the latest release of get-virtualenv." raise RuntimeError(msg) + published_at = datetime.fromisoformat(response["published_at"]) return VersionTuple( - version=Version(tag_name), name=tag_name, download_url=asset["browser_download_url"] + version=Version(tag_name), + name=tag_name, + download_url=asset["browser_download_url"], + published_at=published_at, ) @@ -89,6 +98,13 @@ def update_virtualenv(force: bool, level: str) -> None: latest_release = get_latest_virtualenv_release() + if datetime.now(tz=UTC) - latest_release.published_at < timedelta(days=COOLDOWN_DAYS): + rich.print( + f"[yellow]Skipping update: latest release {latest_release.name!r} was published " + f"less than {COOLDOWN_DAYS} cooldown days ago." + ) + return + if latest_release.version > Version(local_version): version = latest_release.name url = latest_release.download_url From c4a4a46318b7eb9c2f27804fbd6a6c7757fb33af Mon Sep 17 00:00:00 2001 From: Agriya Khetarpal <74401230+agriyakhetarpal@users.noreply.github.com> Date: Thu, 7 May 2026 05:09:41 +0530 Subject: [PATCH 03/28] Add a cooldown for Node.js --- bin/update_nodejs.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/bin/update_nodejs.py b/bin/update_nodejs.py index f45bb4139..a83ee05ec 100755 --- a/bin/update_nodejs.py +++ b/bin/update_nodejs.py @@ -14,6 +14,7 @@ import difflib import logging import tomllib +from datetime import UTC, date, datetime, timedelta from pathlib import Path from typing import Final @@ -34,6 +35,7 @@ NODEJS_DIST: Final[str] = "https://nodejs.org/dist/" NODEJS_INDEX: Final[str] = f"{NODEJS_DIST}index.json" +COOLDOWN_DAYS = 7 @dataclasses.dataclass(frozen=True, order=True) @@ -47,6 +49,7 @@ def parse_nodejs_index() -> list[VersionTuple]: response = requests.get(NODEJS_INDEX) response.raise_for_status() versions_info = response.json() + cutoff_date: date = (datetime.now(tz=UTC) - timedelta(days=COOLDOWN_DAYS)).date() for version_info in versions_info: version_string = version_info.get("version", "???") if not version_info.get("lts", False): @@ -57,6 +60,9 @@ def parse_nodejs_index() -> list[VersionTuple]: "Ignoring release %r which does not include a linux-x64 binary", version_string ) continue + if date.fromisoformat(version_info["date"]) > cutoff_date: + log.info("Ignoring release %r within cooldown period", version_string) + continue try: version = Version(version_string) if version.is_devrelease: From 24975da48de559c4f88714de068fd5f7b4fb1856 Mon Sep 17 00:00:00 2001 From: Agriya Khetarpal <74401230+agriyakhetarpal@users.noreply.github.com> Date: Thu, 7 May 2026 05:10:02 +0530 Subject: [PATCH 04/28] Add a cooldown for PBS --- bin/update_python_build_standalone.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/bin/update_python_build_standalone.py b/bin/update_python_build_standalone.py index 15b425df0..02af2d75c 100755 --- a/bin/update_python_build_standalone.py +++ b/bin/update_python_build_standalone.py @@ -10,6 +10,7 @@ # /// import json +from datetime import UTC, datetime, timedelta from cibuildwheel.extra import github_api_request from cibuildwheel.util.python_build_standalone import ( @@ -18,6 +19,8 @@ ) from cibuildwheel.util.resources import PYTHON_BUILD_STANDALONE_RELEASES +COOLDOWN_DAYS = 7 + def main() -> None: """ @@ -29,6 +32,14 @@ def main() -> None: latest_release = github_api_request("repos/astral-sh/python-build-standalone/releases/latest") latest_tag = latest_release["tag_name"] + published_at = datetime.fromisoformat(latest_release["published_at"]) + if datetime.now(tz=UTC) - published_at < timedelta(days=COOLDOWN_DAYS): + print( + f"Skipping update: latest release {latest_tag!r} was published " + f"less than {COOLDOWN_DAYS} days ago." + ) + return + # Get the list of assets for the latest release github_assets = github_api_request( f"repos/astral-sh/python-build-standalone/releases/tags/{latest_tag}" From 0bd49eb135c30511d33200c279469900c3002ef9 Mon Sep 17 00:00:00 2001 From: Agriya Khetarpal <74401230+agriyakhetarpal@users.noreply.github.com> Date: Thu, 7 May 2026 05:34:36 +0530 Subject: [PATCH 05/28] Add cooldown for GraalPy --- bin/update_pythons.py | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/bin/update_pythons.py b/bin/update_pythons.py index b99a079ef..e1078be58 100755 --- a/bin/update_pythons.py +++ b/bin/update_pythons.py @@ -20,6 +20,7 @@ import re import tomllib from collections.abc import Mapping, MutableMapping +from datetime import UTC, date, datetime, timedelta from pathlib import Path from typing import Any, Final, Literal, TypedDict from xml.etree import ElementTree as ET @@ -41,6 +42,7 @@ # since we want to write to it. DIR: Final[Path] = Path(__file__).parent.parent.resolve() RESOURCES_DIR: Final[Path] = DIR / "cibuildwheel/resources" +COOLDOWN_DAYS = 7 ArchStr = Literal["32", "64", "ARM64"] @@ -113,7 +115,7 @@ def update_version_windows(self, spec: Specifier) -> Config | None: class GraalPyVersions: - def __init__(self) -> None: + def __init__(self, cutoff_date: date) -> None: response = requests.get("https://api.github.com/repos/oracle/graalpython/releases") response.raise_for_status() @@ -128,7 +130,13 @@ def __init__(self) -> None: if m: release["python_version"] = Version(m.group(1)) - self.releases = [r for r in releases if "graalpy_version" in r and "python_version" in r] + self.releases = [ + r + for r in releases + if "graalpy_version" in r + and "python_version" in r + and datetime.fromisoformat(r["published_at"]).date() <= cutoff_date + ] def update_version(self, identifier: str, spec: Specifier) -> ConfigUrl | None: if "x86_64" in identifier or "amd64" in identifier: @@ -426,6 +434,8 @@ def update_version_pyodide( class AllVersions: def __init__(self) -> None: + cutoff_date: date = (datetime.now(tz=UTC) - timedelta(days=COOLDOWN_DAYS)).date() + self.windows_32 = WindowsVersions("32", False) self.windows_t_32 = WindowsVersions("32", True) self.windows_64 = WindowsVersions("64", False) @@ -441,7 +451,7 @@ def __init__(self) -> None: self.maven = MavenVersions() self.ios_cpython = CPythonIOSVersions() - self.graalpy = GraalPyVersions() + self.graalpy = GraalPyVersions(cutoff_date) self.pyodide = PyodideVersions() From 88efa8b5bfed288155e85d20b489adfe3890b49f Mon Sep 17 00:00:00 2001 From: Agriya Khetarpal <74401230+agriyakhetarpal@users.noreply.github.com> Date: Thu, 7 May 2026 05:35:11 +0530 Subject: [PATCH 06/28] Add cooldown for PyPy --- bin/update_pythons.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/bin/update_pythons.py b/bin/update_pythons.py index e1078be58..fcf3a67df 100755 --- a/bin/update_pythons.py +++ b/bin/update_pythons.py @@ -193,7 +193,7 @@ def update_version(self, identifier: str, spec: Specifier) -> ConfigUrl | None: class PyPyVersions: - def __init__(self, arch_str: ArchStr): + def __init__(self, arch_str: ArchStr, cutoff_date: date): response = requests.get("https://downloads.python.org/pypy/versions.json") response.raise_for_status() @@ -205,7 +205,9 @@ def __init__(self, arch_str: ArchStr): self.releases = [ r for r in releases - if not r["pypy_version"].is_prerelease and not r["pypy_version"].is_devrelease + if not r["pypy_version"].is_prerelease + and not r["pypy_version"].is_devrelease + and date.fromisoformat(r["date"]) <= cutoff_date ] self.arch = arch_str @@ -442,11 +444,11 @@ def __init__(self) -> None: self.windows_t_64 = WindowsVersions("64", True) self.windows_arm64 = WindowsVersions("ARM64", False) self.windows_t_arm64 = WindowsVersions("ARM64", True) - self.windows_pypy_64 = PyPyVersions("64") + self.windows_pypy_64 = PyPyVersions("64", cutoff_date) self.cpython = CPythonVersions() - self.macos_pypy = PyPyVersions("64") - self.macos_pypy_arm64 = PyPyVersions("ARM64") + self.macos_pypy = PyPyVersions("64", cutoff_date) + self.macos_pypy_arm64 = PyPyVersions("ARM64", cutoff_date) self.maven = MavenVersions() self.ios_cpython = CPythonIOSVersions() From ac39bb272e602d2c9e1bf93cfc0ed3620685c505 Mon Sep 17 00:00:00 2001 From: Agriya Khetarpal <74401230+agriyakhetarpal@users.noreply.github.com> Date: Thu, 7 May 2026 05:35:34 +0530 Subject: [PATCH 07/28] Add cooldown for CPython --- bin/update_pythons.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/bin/update_pythons.py b/bin/update_pythons.py index fcf3a67df..5dca00530 100755 --- a/bin/update_pythons.py +++ b/bin/update_pythons.py @@ -273,7 +273,7 @@ def update_version_macos(self, spec: Specifier) -> ConfigUrl: class CPythonVersions: - def __init__(self) -> None: + def __init__(self, cutoff_date: date) -> None: response = requests.get( "https://www.python.org/api/v2/downloads/release/?is_published=true" ) @@ -287,6 +287,10 @@ def __init__(self) -> None: if not release["slug"].startswith("python"): continue + release_date_str = release.get("release_date") + if release_date_str and datetime.fromisoformat(release_date_str).date() > cutoff_date: + continue + # Removing the prefix version = Version(release["name"].removeprefix("Python ")) self.versions_dict[version] = release["resource_uri"] @@ -446,7 +450,7 @@ def __init__(self) -> None: self.windows_t_arm64 = WindowsVersions("ARM64", True) self.windows_pypy_64 = PyPyVersions("64", cutoff_date) - self.cpython = CPythonVersions() + self.cpython = CPythonVersions(cutoff_date) self.macos_pypy = PyPyVersions("64", cutoff_date) self.macos_pypy_arm64 = PyPyVersions("ARM64", cutoff_date) From 0d39d197c3096a80fee0a5f6fb9cb3ca5cb3a11e Mon Sep 17 00:00:00 2001 From: Agriya Khetarpal <74401230+agriyakhetarpal@users.noreply.github.com> Date: Thu, 7 May 2026 05:35:49 +0530 Subject: [PATCH 08/28] Add cooldown for CPythonIOS --- bin/update_pythons.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/bin/update_pythons.py b/bin/update_pythons.py index 5dca00530..3178af53b 100755 --- a/bin/update_pythons.py +++ b/bin/update_pythons.py @@ -360,7 +360,7 @@ def update_version_android(self, identifier: str, spec: Specifier) -> ConfigUrl class CPythonIOSVersions: - def __init__(self) -> None: + def __init__(self, cutoff_date: date) -> None: response = requests.get( "https://api.github.com/repos/beeware/Python-Apple-support/releases", headers={ @@ -375,6 +375,9 @@ def __init__(self) -> None: # Each release has a name like "3.13-b4" for release in releases_info: + if datetime.fromisoformat(release["published_at"]).date() > cutoff_date: + continue + py_version, build = release["name"].split("-") version = Version(py_version) self.versions_dict.setdefault(version, {}) @@ -455,7 +458,7 @@ def __init__(self) -> None: self.macos_pypy_arm64 = PyPyVersions("ARM64", cutoff_date) self.maven = MavenVersions() - self.ios_cpython = CPythonIOSVersions() + self.ios_cpython = CPythonIOSVersions(cutoff_date) self.graalpy = GraalPyVersions(cutoff_date) From 38af4dbd44e44d502654219ac326d07ebc78ac29 Mon Sep 17 00:00:00 2001 From: Agriya Khetarpal <74401230+agriyakhetarpal@users.noreply.github.com> Date: Thu, 7 May 2026 05:44:34 +0530 Subject: [PATCH 09/28] Add cooldown for NuGet --- bin/update_pythons.py | 44 ++++++++++++++++++++++++++++--------------- 1 file changed, 29 insertions(+), 15 deletions(-) diff --git a/bin/update_pythons.py b/bin/update_pythons.py index 3178af53b..2e8865926 100755 --- a/bin/update_pythons.py +++ b/bin/update_pythons.py @@ -67,14 +67,14 @@ class ConfigPyodide(Config): class WindowsVersions: - def __init__(self, arch_str: ArchStr, free_threaded: bool) -> None: + def __init__(self, arch_str: ArchStr, free_threaded: bool, cutoff_date: date) -> None: response = requests.get("https://api.nuget.org/v3/index.json") response.raise_for_status() api_info = response.json() - for resource in api_info["resources"]: - if resource["@type"] == "PackageBaseAddress/3.0.0": - endpoint = resource["@id"] + reg_endpoint = next( + r["@id"] for r in api_info["resources"] if r["@type"] == "RegistrationsBaseUrl/3.6.0" + ) ARCH_DICT = {"32": "win32", "64": "win_amd64", "ARM64": "win_arm64"} PACKAGE_DICT = {"32": "pythonx86", "64": "python", "ARM64": "pythonarm64"} @@ -87,11 +87,25 @@ def __init__(self, arch_str: ArchStr, free_threaded: bool) -> None: if free_threaded: package = f"{package}-freethreaded" - response = requests.get(f"{endpoint}{package}/index.json") - response.raise_for_status() - cp_info = response.json() - - self.version_dict = {Version(v): v for v in cp_info["versions"]} + # NuGet serves registration responses gzip-compressed; requests decompresses + # automatically via Accept-Encoding negotiation + reg_response = requests.get(f"{reg_endpoint}{package}/index.json") + reg_response.raise_for_status() + reg_data = reg_response.json() + + # NuGet uses 1900-01-01 as a sentinel for packages whose publish date was + # not recorded; treat those as old enough to pass the cooldown + NUGET_DATE_SENTINEL = date(1900, 1, 1) + + self.version_dict: dict[Version, str] = {} + for page_meta in reg_data["items"]: + page = requests.get(page_meta["@id"]).json() + for item in page["items"]: + entry = item["catalogEntry"] + published = datetime.fromisoformat(entry["published"]).date() + if published != NUGET_DATE_SENTINEL and published > cutoff_date: + continue + self.version_dict[Version(entry["version"])] = entry["version"] def update_version_windows(self, spec: Specifier) -> Config | None: # Specifier.filter selects all non pre-releases that match the spec, @@ -445,12 +459,12 @@ class AllVersions: def __init__(self) -> None: cutoff_date: date = (datetime.now(tz=UTC) - timedelta(days=COOLDOWN_DAYS)).date() - self.windows_32 = WindowsVersions("32", False) - self.windows_t_32 = WindowsVersions("32", True) - self.windows_64 = WindowsVersions("64", False) - self.windows_t_64 = WindowsVersions("64", True) - self.windows_arm64 = WindowsVersions("ARM64", False) - self.windows_t_arm64 = WindowsVersions("ARM64", True) + self.windows_32 = WindowsVersions("32", False, cutoff_date) + self.windows_t_32 = WindowsVersions("32", True, cutoff_date) + self.windows_64 = WindowsVersions("64", False, cutoff_date) + self.windows_t_64 = WindowsVersions("64", True, cutoff_date) + self.windows_arm64 = WindowsVersions("ARM64", False, cutoff_date) + self.windows_t_arm64 = WindowsVersions("ARM64", True, cutoff_date) self.windows_pypy_64 = PyPyVersions("64", cutoff_date) self.cpython = CPythonVersions(cutoff_date) From 82289698e24c700981892b7dbed84d07e63ae5ec Mon Sep 17 00:00:00 2001 From: Agriya Khetarpal <74401230+agriyakhetarpal@users.noreply.github.com> Date: Thu, 7 May 2026 05:46:31 +0530 Subject: [PATCH 10/28] Add cooldown for Maven --- bin/update_pythons.py | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/bin/update_pythons.py b/bin/update_pythons.py index 2e8865926..2d33905b7 100755 --- a/bin/update_pythons.py +++ b/bin/update_pythons.py @@ -21,6 +21,7 @@ import tomllib from collections.abc import Mapping, MutableMapping from datetime import UTC, date, datetime, timedelta +from email.utils import parsedate_to_datetime from pathlib import Path from typing import Any, Final, Literal, TypedDict from xml.etree import ElementTree as ET @@ -346,7 +347,7 @@ def update_version_android(self, identifier: str, spec: Specifier) -> ConfigUrl class MavenVersions: MAVEN_URL = "https://repo.maven.apache.org/maven2/com/chaquo/python/python" - def __init__(self) -> None: + def __init__(self, cutoff_date: date) -> None: response = requests.get(f"{self.MAVEN_URL}/maven-metadata.xml") response.raise_for_status() root = ET.fromstring(response.text) @@ -357,20 +358,30 @@ def __init__(self) -> None: assert isinstance(version_str, str), version_str self.versions.append(Version(version_str)) + self.cutoff_date = cutoff_date + def update_version_android(self, identifier: str, spec: Specifier) -> ConfigUrl | None: sorted_versions = sorted(spec.filter(self.versions), reverse=True) - # Return a config using the highest version for the given specifier. - if sorted_versions: - max_version = sorted_versions[0] + # maven-metadata.xml only carries a package-level timestamp, not per-version + # dates, so we check the Last-Modified header on each candidate's POM file + for max_version in sorted_versions: triplet = android_triplet(identifier) + pom_url = f"{self.MAVEN_URL}/{max_version}/python-{max_version}.pom" + head_response = requests.head(pom_url) + head_response.raise_for_status() + last_modified_str = head_response.headers.get("Last-Modified") + if last_modified_str: + published = parsedate_to_datetime(last_modified_str).date() + if published > self.cutoff_date: + log.info("Skipping %s: published %s is within cooldown", max_version, published) + continue return ConfigUrl( identifier=identifier, version=f"{max_version.major}.{max_version.minor}", url=f"{self.MAVEN_URL}/{max_version}/python-{max_version}-{triplet}.tar.gz", ) - else: - return None + return None class CPythonIOSVersions: @@ -471,7 +482,7 @@ def __init__(self) -> None: self.macos_pypy = PyPyVersions("64", cutoff_date) self.macos_pypy_arm64 = PyPyVersions("ARM64", cutoff_date) - self.maven = MavenVersions() + self.maven = MavenVersions(cutoff_date) self.ios_cpython = CPythonIOSVersions(cutoff_date) self.graalpy = GraalPyVersions(cutoff_date) From ae4750c0e35f2437da09ed526408d1ac244d513d Mon Sep 17 00:00:00 2001 From: Agriya Khetarpal <74401230+agriyakhetarpal@users.noreply.github.com> Date: Thu, 7 May 2026 06:06:09 +0530 Subject: [PATCH 11/28] NuGet: add some explainer comments + reduce requests --- bin/update_pythons.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/bin/update_pythons.py b/bin/update_pythons.py index 2d33905b7..d3f2b9030 100755 --- a/bin/update_pythons.py +++ b/bin/update_pythons.py @@ -95,13 +95,18 @@ def __init__(self, arch_str: ArchStr, free_threaded: bool, cutoff_date: date) -> reg_data = reg_response.json() # NuGet uses 1900-01-01 as a sentinel for packages whose publish date was - # not recorded; treat those as old enough to pass the cooldown + # not recorded. The NuGet SDK looks at this as null and treats it as + # "allow" (and dependabot-core does the same): + # https://github.com/dependabot/dependabot-core/blob/b0090acfa61b7541c040e302c760a84c702217a3/nuget/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/Cooldown.cs#L70-L75 NUGET_DATE_SENTINEL = date(1900, 1, 1) self.version_dict: dict[Version, str] = {} for page_meta in reg_data["items"]: - page = requests.get(page_meta["@id"]).json() - for item in page["items"]: + # Registration pages may be inlined in the index response or kept external + # See: https://github.com/microsoft/NativeAOTDependencyHelper/blob/9ad3f7be5b919f6166d60a228691e1c802797909/NativeAOTDependencyHelper.Core/Checks/NuGetRecentlyUpdatedCheck.cs#L36-L45 + # pythonarm64 and all freethreaded packages have fully-inlined pages and need no extra fetches. + items = page_meta.get("items") or requests.get(page_meta["@id"]).json()["items"] + for item in items: entry = item["catalogEntry"] published = datetime.fromisoformat(entry["published"]).date() if published != NUGET_DATE_SENTINEL and published > cutoff_date: From bd0c7260fc391b84c003bfb4ff797164d2ac524b Mon Sep 17 00:00:00 2001 From: Agriya Khetarpal <74401230+agriyakhetarpal@users.noreply.github.com> Date: Thu, 7 May 2026 06:29:18 +0530 Subject: [PATCH 12/28] Add `CIBW_IGNORE_COOLDOWN` --- .github/workflows/update-dependencies.yml | 9 +++++++++ bin/update_nodejs.py | 8 +++++++- bin/update_python_build_standalone.py | 4 +++- bin/update_pythons.py | 8 +++++++- bin/update_virtualenv.py | 6 +++++- noxfile.py | 10 ++++++++-- 6 files changed, 39 insertions(+), 6 deletions(-) diff --git a/.github/workflows/update-dependencies.yml b/.github/workflows/update-dependencies.yml index 74e274f13..bb6c2991b 100644 --- a/.github/workflows/update-dependencies.yml +++ b/.github/workflows/update-dependencies.yml @@ -11,6 +11,11 @@ on: - 'docs/data/projects.yml' - 'noxfile.py' workflow_dispatch: + inputs: + ignore-cooldown: + description: 'Ignore the cooldown window and pick up the very latest releases' + type: boolean + default: false schedule: - cron: '0 6 * * 1' # "At 06:00 on Monday." @@ -45,8 +50,12 @@ jobs: - name: "Run update: dependencies" run: uvx nox --force-color -s update_constraints + env: + CIBW_IGNORE_COOLDOWN: ${{ inputs.ignore-cooldown }} - name: "Run update: python configs" run: uvx nox --force-color -s update_pins + env: + CIBW_IGNORE_COOLDOWN: ${{ inputs.ignore-cooldown }} - name: "Run update: docs user projects" run: uvx nox --force-color -s update_proj -- --auth=${{ secrets.GITHUB_TOKEN }} diff --git a/bin/update_nodejs.py b/bin/update_nodejs.py index a83ee05ec..2519607d3 100755 --- a/bin/update_nodejs.py +++ b/bin/update_nodejs.py @@ -13,6 +13,7 @@ import dataclasses import difflib import logging +import os import tomllib from datetime import UTC, date, datetime, timedelta from pathlib import Path @@ -36,6 +37,7 @@ NODEJS_DIST: Final[str] = "https://nodejs.org/dist/" NODEJS_INDEX: Final[str] = f"{NODEJS_DIST}index.json" COOLDOWN_DAYS = 7 +IGNORE_COOLDOWN = os.environ.get("CIBW_IGNORE_COOLDOWN", "").lower() in ("1", "true") @dataclasses.dataclass(frozen=True, order=True) @@ -49,7 +51,11 @@ def parse_nodejs_index() -> list[VersionTuple]: response = requests.get(NODEJS_INDEX) response.raise_for_status() versions_info = response.json() - cutoff_date: date = (datetime.now(tz=UTC) - timedelta(days=COOLDOWN_DAYS)).date() + cutoff_date: date = ( + date.max + if IGNORE_COOLDOWN + else (datetime.now(tz=UTC) - timedelta(days=COOLDOWN_DAYS)).date() + ) for version_info in versions_info: version_string = version_info.get("version", "???") if not version_info.get("lts", False): diff --git a/bin/update_python_build_standalone.py b/bin/update_python_build_standalone.py index 02af2d75c..e64174727 100755 --- a/bin/update_python_build_standalone.py +++ b/bin/update_python_build_standalone.py @@ -10,6 +10,7 @@ # /// import json +import os from datetime import UTC, datetime, timedelta from cibuildwheel.extra import github_api_request @@ -20,6 +21,7 @@ from cibuildwheel.util.resources import PYTHON_BUILD_STANDALONE_RELEASES COOLDOWN_DAYS = 7 +IGNORE_COOLDOWN = os.environ.get("CIBW_IGNORE_COOLDOWN", "").lower() in ("1", "true") def main() -> None: @@ -33,7 +35,7 @@ def main() -> None: latest_tag = latest_release["tag_name"] published_at = datetime.fromisoformat(latest_release["published_at"]) - if datetime.now(tz=UTC) - published_at < timedelta(days=COOLDOWN_DAYS): + if not IGNORE_COOLDOWN and datetime.now(tz=UTC) - published_at < timedelta(days=COOLDOWN_DAYS): print( f"Skipping update: latest release {latest_tag!r} was published " f"less than {COOLDOWN_DAYS} days ago." diff --git a/bin/update_pythons.py b/bin/update_pythons.py index d3f2b9030..a68fd4493 100755 --- a/bin/update_pythons.py +++ b/bin/update_pythons.py @@ -17,6 +17,7 @@ import difflib import logging import operator +import os import re import tomllib from collections.abc import Mapping, MutableMapping @@ -44,6 +45,7 @@ DIR: Final[Path] = Path(__file__).parent.parent.resolve() RESOURCES_DIR: Final[Path] = DIR / "cibuildwheel/resources" COOLDOWN_DAYS = 7 +IGNORE_COOLDOWN = os.environ.get("CIBW_IGNORE_COOLDOWN", "").lower() in ("1", "true") ArchStr = Literal["32", "64", "ARM64"] @@ -473,7 +475,11 @@ def update_version_pyodide( class AllVersions: def __init__(self) -> None: - cutoff_date: date = (datetime.now(tz=UTC) - timedelta(days=COOLDOWN_DAYS)).date() + cutoff_date: date = ( + date.max + if IGNORE_COOLDOWN + else (datetime.now(tz=UTC) - timedelta(days=COOLDOWN_DAYS)).date() + ) self.windows_32 = WindowsVersions("32", False, cutoff_date) self.windows_t_32 = WindowsVersions("32", True, cutoff_date) diff --git a/bin/update_virtualenv.py b/bin/update_virtualenv.py index c4147b746..1b394845b 100755 --- a/bin/update_virtualenv.py +++ b/bin/update_virtualenv.py @@ -16,6 +16,7 @@ import dataclasses import difflib import logging +import os import tomllib from datetime import UTC, datetime, timedelta from pathlib import Path @@ -43,6 +44,7 @@ COOLDOWN_DAYS = 7 +IGNORE_COOLDOWN = os.environ.get("CIBW_IGNORE_COOLDOWN", "").lower() in ("1", "true") @dataclasses.dataclass(frozen=True, order=True) @@ -98,7 +100,9 @@ def update_virtualenv(force: bool, level: str) -> None: latest_release = get_latest_virtualenv_release() - if datetime.now(tz=UTC) - latest_release.published_at < timedelta(days=COOLDOWN_DAYS): + if not IGNORE_COOLDOWN and datetime.now(tz=UTC) - latest_release.published_at < timedelta( + days=COOLDOWN_DAYS + ): rich.print( f"[yellow]Skipping update: latest release {latest_release.name!r} was published " f"less than {COOLDOWN_DAYS} cooldown days ago." diff --git a/noxfile.py b/noxfile.py index a862615bb..af64329fe 100755 --- a/noxfile.py +++ b/noxfile.py @@ -80,7 +80,12 @@ def update_constraints(session: nox.Session) -> None: env = os.environ.copy() env["UV_CUSTOM_COMPILE_COMMAND"] = f"nox -s {session.name}" - exclude_newer = (datetime.now(tz=UTC).date() - timedelta(days=7)).isoformat() + ignore_cooldown = os.environ.get("CIBW_IGNORE_COOLDOWN", "").lower() in ("1", "true") + exclude_newer_args = ( + [] + if ignore_cooldown + else [f"--exclude-newer={(datetime.now(tz=UTC).date() - timedelta(days=7)).isoformat()}"] + ) for minor_version in range(8, 15): python_version = f"3.{minor_version}" @@ -91,7 +96,7 @@ def update_constraints(session: nox.Session) -> None: "compile", f"--python-version={python_version}", "--upgrade", - f"--exclude-newer={exclude_newer}", + *exclude_newer_args, resources / "constraints.in", f"--output-file={output_file}", env=env, @@ -124,6 +129,7 @@ def update_constraints(session: nox.Session) -> None: "compile", f"--python-version={python_version}", "--upgrade", + *exclude_newer_args, tmp_file, f"--output-file={output_file}", env=env, From 71364351718a0721473633181c4b6b44afb17132 Mon Sep 17 00:00:00 2001 From: Agriya Khetarpal <74401230+agriyakhetarpal@users.noreply.github.com> Date: Thu, 7 May 2026 06:44:17 +0530 Subject: [PATCH 13/28] Refactor cooldown constants into a separate file --- .github/dependabot.yml | 1 + .pre-commit-config.yaml | 3 ++- bin/_cooldown.py | 9 +++++++++ bin/update_nodejs.py | 5 ++--- bin/update_python_build_standalone.py | 5 +---- bin/update_pythons.py | 4 +--- bin/update_virtualenv.py | 6 +----- noxfile.py | 9 ++++++--- pyproject.toml | 2 +- 9 files changed, 24 insertions(+), 20 deletions(-) create mode 100644 bin/_cooldown.py diff --git a/.github/dependabot.yml b/.github/dependabot.yml index bea489321..f1e513c65 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -9,5 +9,6 @@ updates: actions: patterns: - "*" + # NOTE: keep this in sync with bin/_cooldown.py cooldown: default-days: 7 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 4188fd65f..88a8154c1 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -29,7 +29,7 @@ repos: - id: mypy name: mypy 3.11 on cibuildwheel/ args: ["--python-version=3.11"] - exclude: ^cibuildwheel/resources/android/_cross_venv.py$ # Requires Python 3.13 or later + exclude: (^cibuildwheel/resources/android/_cross_venv.py$|^bin/_cooldown\.py$) # _cross_venv.py requires Python 3.13 or later. _cooldown.py is a support module, not a top-level file additional_dependencies: &mypy-dependencies - bracex - build @@ -54,6 +54,7 @@ repos: - id: mypy name: mypy 3.14 args: ["--python-version=3.14"] + exclude: ^bin/_cooldown\.py$ # support module, not a top-level file additional_dependencies: *mypy-dependencies - repo: https://github.com/shellcheck-py/shellcheck-py diff --git a/bin/_cooldown.py b/bin/_cooldown.py new file mode 100644 index 000000000..73c425621 --- /dev/null +++ b/bin/_cooldown.py @@ -0,0 +1,9 @@ +import os + +# Number of days a release must age before our update scripts will pick it up. +# NOTE: keep this in sync with the Dependabot configuration in .github/dependabot.yml. +COOLDOWN_DAYS = 7 + +# Set CIBW_IGNORE_COOLDOWN to a truthy value to bypass the cooldown and always pick +# up the very latest releases regardless of age. +IGNORE_COOLDOWN = os.environ.get("CIBW_IGNORE_COOLDOWN", "").lower() in ("1", "true") diff --git a/bin/update_nodejs.py b/bin/update_nodejs.py index 2519607d3..dbe1c52c1 100755 --- a/bin/update_nodejs.py +++ b/bin/update_nodejs.py @@ -13,7 +13,6 @@ import dataclasses import difflib import logging -import os import tomllib from datetime import UTC, date, datetime, timedelta from pathlib import Path @@ -27,6 +26,8 @@ from rich.logging import RichHandler from rich.syntax import Syntax +from bin._cooldown import COOLDOWN_DAYS, IGNORE_COOLDOWN + log = logging.getLogger("cibw") # Looking up the dir instead of using utils.resources_dir @@ -36,8 +37,6 @@ NODEJS_DIST: Final[str] = "https://nodejs.org/dist/" NODEJS_INDEX: Final[str] = f"{NODEJS_DIST}index.json" -COOLDOWN_DAYS = 7 -IGNORE_COOLDOWN = os.environ.get("CIBW_IGNORE_COOLDOWN", "").lower() in ("1", "true") @dataclasses.dataclass(frozen=True, order=True) diff --git a/bin/update_python_build_standalone.py b/bin/update_python_build_standalone.py index e64174727..f70bbef39 100755 --- a/bin/update_python_build_standalone.py +++ b/bin/update_python_build_standalone.py @@ -10,9 +10,9 @@ # /// import json -import os from datetime import UTC, datetime, timedelta +from bin._cooldown import COOLDOWN_DAYS, IGNORE_COOLDOWN from cibuildwheel.extra import github_api_request from cibuildwheel.util.python_build_standalone import ( PythonBuildStandaloneAsset, @@ -20,9 +20,6 @@ ) from cibuildwheel.util.resources import PYTHON_BUILD_STANDALONE_RELEASES -COOLDOWN_DAYS = 7 -IGNORE_COOLDOWN = os.environ.get("CIBW_IGNORE_COOLDOWN", "").lower() in ("1", "true") - def main() -> None: """ diff --git a/bin/update_pythons.py b/bin/update_pythons.py index a68fd4493..60360c8c1 100755 --- a/bin/update_pythons.py +++ b/bin/update_pythons.py @@ -17,7 +17,6 @@ import difflib import logging import operator -import os import re import tomllib from collections.abc import Mapping, MutableMapping @@ -35,6 +34,7 @@ from rich.logging import RichHandler from rich.syntax import Syntax +from bin._cooldown import COOLDOWN_DAYS, IGNORE_COOLDOWN from cibuildwheel.extra import dump_python_configurations, get_pyodide_xbuildenv_info from cibuildwheel.platforms.android import android_triplet @@ -44,8 +44,6 @@ # since we want to write to it. DIR: Final[Path] = Path(__file__).parent.parent.resolve() RESOURCES_DIR: Final[Path] = DIR / "cibuildwheel/resources" -COOLDOWN_DAYS = 7 -IGNORE_COOLDOWN = os.environ.get("CIBW_IGNORE_COOLDOWN", "").lower() in ("1", "true") ArchStr = Literal["32", "64", "ARM64"] diff --git a/bin/update_virtualenv.py b/bin/update_virtualenv.py index 1b394845b..fdd04924f 100755 --- a/bin/update_virtualenv.py +++ b/bin/update_virtualenv.py @@ -16,7 +16,6 @@ import dataclasses import difflib import logging -import os import tomllib from datetime import UTC, datetime, timedelta from pathlib import Path @@ -28,6 +27,7 @@ from rich.logging import RichHandler from rich.syntax import Syntax +from bin._cooldown import COOLDOWN_DAYS, IGNORE_COOLDOWN from cibuildwheel.extra import github_api_request log = logging.getLogger("cibw") @@ -43,10 +43,6 @@ ) -COOLDOWN_DAYS = 7 -IGNORE_COOLDOWN = os.environ.get("CIBW_IGNORE_COOLDOWN", "").lower() in ("1", "true") - - @dataclasses.dataclass(frozen=True, order=True) class VersionTuple: name: str diff --git a/noxfile.py b/noxfile.py index af64329fe..828e2c8cc 100755 --- a/noxfile.py +++ b/noxfile.py @@ -23,6 +23,8 @@ import nox +from bin._cooldown import COOLDOWN_DAYS, IGNORE_COOLDOWN + nox.needs_version = ">=2025.2.9" nox.options.default_venv_backend = "uv|virtualenv" @@ -80,11 +82,12 @@ def update_constraints(session: nox.Session) -> None: env = os.environ.copy() env["UV_CUSTOM_COMPILE_COMMAND"] = f"nox -s {session.name}" - ignore_cooldown = os.environ.get("CIBW_IGNORE_COOLDOWN", "").lower() in ("1", "true") exclude_newer_args = ( [] - if ignore_cooldown - else [f"--exclude-newer={(datetime.now(tz=UTC).date() - timedelta(days=7)).isoformat()}"] + if IGNORE_COOLDOWN + else [ + f"--exclude-newer={(datetime.now(tz=UTC).date() - timedelta(days=COOLDOWN_DAYS)).isoformat()}" + ] ) for minor_version in range(8, 15): diff --git a/pyproject.toml b/pyproject.toml index d5c92213e..0f40a1ec5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -114,7 +114,7 @@ files = [ "cibuildwheel/*.py", "test/**/*.py", "unit_test/**/*.py", - "bin/*.py", + "bin/[!_]*.py", "noxfile.py", ] warn_unused_configs = true From 2555201bed6378abb79544dcd477bb26daa0e99f Mon Sep 17 00:00:00 2001 From: Agriya Khetarpal <74401230+agriyakhetarpal@users.noreply.github.com> Date: Thu, 7 May 2026 07:08:03 +0530 Subject: [PATCH 14/28] Move `_cooldown` under `cibuildwheel.util` (eas --- .pre-commit-config.yaml | 2 +- bin/update_nodejs.py | 6 +++++- bin/update_python_build_standalone.py | 2 +- bin/update_pythons.py | 2 +- bin/update_virtualenv.py | 2 +- {bin => cibuildwheel/util}/_cooldown.py | 2 +- noxfile.py | 2 +- pyproject.toml | 2 +- 8 files changed, 12 insertions(+), 8 deletions(-) rename {bin => cibuildwheel/util}/_cooldown.py (84%) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 88a8154c1..e233c12f5 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -29,7 +29,7 @@ repos: - id: mypy name: mypy 3.11 on cibuildwheel/ args: ["--python-version=3.11"] - exclude: (^cibuildwheel/resources/android/_cross_venv.py$|^bin/_cooldown\.py$) # _cross_venv.py requires Python 3.13 or later. _cooldown.py is a support module, not a top-level file + exclude: ^cibuildwheel/resources/android/_cross_venv.py$ # Requires Python 3.13 or later additional_dependencies: &mypy-dependencies - bracex - build diff --git a/bin/update_nodejs.py b/bin/update_nodejs.py index dbe1c52c1..11a563324 100755 --- a/bin/update_nodejs.py +++ b/bin/update_nodejs.py @@ -6,7 +6,11 @@ # "packaging", # "requests", # "rich", +# "cibuildwheel", # ] +# +# [tool.uv.sources] +# cibuildwheel = { path = ".." } # /// @@ -26,7 +30,7 @@ from rich.logging import RichHandler from rich.syntax import Syntax -from bin._cooldown import COOLDOWN_DAYS, IGNORE_COOLDOWN +from cibuildwheel.util._cooldown import COOLDOWN_DAYS, IGNORE_COOLDOWN log = logging.getLogger("cibw") diff --git a/bin/update_python_build_standalone.py b/bin/update_python_build_standalone.py index f70bbef39..f7ac910f1 100755 --- a/bin/update_python_build_standalone.py +++ b/bin/update_python_build_standalone.py @@ -12,8 +12,8 @@ import json from datetime import UTC, datetime, timedelta -from bin._cooldown import COOLDOWN_DAYS, IGNORE_COOLDOWN from cibuildwheel.extra import github_api_request +from cibuildwheel.util._cooldown import COOLDOWN_DAYS, IGNORE_COOLDOWN from cibuildwheel.util.python_build_standalone import ( PythonBuildStandaloneAsset, PythonBuildStandaloneReleaseData, diff --git a/bin/update_pythons.py b/bin/update_pythons.py index 60360c8c1..be332360e 100755 --- a/bin/update_pythons.py +++ b/bin/update_pythons.py @@ -34,9 +34,9 @@ from rich.logging import RichHandler from rich.syntax import Syntax -from bin._cooldown import COOLDOWN_DAYS, IGNORE_COOLDOWN from cibuildwheel.extra import dump_python_configurations, get_pyodide_xbuildenv_info from cibuildwheel.platforms.android import android_triplet +from cibuildwheel.util._cooldown import COOLDOWN_DAYS, IGNORE_COOLDOWN log = logging.getLogger("cibw") diff --git a/bin/update_virtualenv.py b/bin/update_virtualenv.py index fdd04924f..eb89c1be9 100755 --- a/bin/update_virtualenv.py +++ b/bin/update_virtualenv.py @@ -27,8 +27,8 @@ from rich.logging import RichHandler from rich.syntax import Syntax -from bin._cooldown import COOLDOWN_DAYS, IGNORE_COOLDOWN from cibuildwheel.extra import github_api_request +from cibuildwheel.util._cooldown import COOLDOWN_DAYS, IGNORE_COOLDOWN log = logging.getLogger("cibw") diff --git a/bin/_cooldown.py b/cibuildwheel/util/_cooldown.py similarity index 84% rename from bin/_cooldown.py rename to cibuildwheel/util/_cooldown.py index 73c425621..c14bd9946 100644 --- a/bin/_cooldown.py +++ b/cibuildwheel/util/_cooldown.py @@ -1,6 +1,6 @@ import os -# Number of days a release must age before our update scripts will pick it up. +# Number of days a release must age before the update scripts will pick it up. # NOTE: keep this in sync with the Dependabot configuration in .github/dependabot.yml. COOLDOWN_DAYS = 7 diff --git a/noxfile.py b/noxfile.py index 828e2c8cc..78e102df0 100755 --- a/noxfile.py +++ b/noxfile.py @@ -23,7 +23,7 @@ import nox -from bin._cooldown import COOLDOWN_DAYS, IGNORE_COOLDOWN +from cibuildwheel.util._cooldown import COOLDOWN_DAYS, IGNORE_COOLDOWN nox.needs_version = ">=2025.2.9" nox.options.default_venv_backend = "uv|virtualenv" diff --git a/pyproject.toml b/pyproject.toml index 0f40a1ec5..d5c92213e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -114,7 +114,7 @@ files = [ "cibuildwheel/*.py", "test/**/*.py", "unit_test/**/*.py", - "bin/[!_]*.py", + "bin/*.py", "noxfile.py", ] warn_unused_configs = true From 90df56e3e9bc8c6a09722e3165b06a6912ad6737 Mon Sep 17 00:00:00 2001 From: Agriya Khetarpal <74401230+agriyakhetarpal@users.noreply.github.com> Date: Thu, 7 May 2026 07:09:26 +0530 Subject: [PATCH 15/28] Handle cases with undefined ignore-cooldown input --- .github/workflows/update-dependencies.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/update-dependencies.yml b/.github/workflows/update-dependencies.yml index bb6c2991b..59bdf83c9 100644 --- a/.github/workflows/update-dependencies.yml +++ b/.github/workflows/update-dependencies.yml @@ -51,11 +51,11 @@ jobs: - name: "Run update: dependencies" run: uvx nox --force-color -s update_constraints env: - CIBW_IGNORE_COOLDOWN: ${{ inputs.ignore-cooldown }} + CIBW_IGNORE_COOLDOWN: ${{ github.event.inputs.ignore-cooldown || 'false' }} - name: "Run update: python configs" run: uvx nox --force-color -s update_pins env: - CIBW_IGNORE_COOLDOWN: ${{ inputs.ignore-cooldown }} + CIBW_IGNORE_COOLDOWN: ${{ github.event.inputs.ignore-cooldown || 'false' }} - name: "Run update: docs user projects" run: uvx nox --force-color -s update_proj -- --auth=${{ secrets.GITHUB_TOKEN }} From 4e7ead66e35e10e4e8a91563a988069991fe46c7 Mon Sep 17 00:00:00 2001 From: Agriya Khetarpal <74401230+agriyakhetarpal@users.noreply.github.com> Date: Thu, 7 May 2026 07:12:22 +0530 Subject: [PATCH 16/28] Give up trying to make noxfile happy --- noxfile.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/noxfile.py b/noxfile.py index 78e102df0..ffd193d3d 100755 --- a/noxfile.py +++ b/noxfile.py @@ -23,8 +23,6 @@ import nox -from cibuildwheel.util._cooldown import COOLDOWN_DAYS, IGNORE_COOLDOWN - nox.needs_version = ">=2025.2.9" nox.options.default_venv_backend = "uv|virtualenv" @@ -82,11 +80,14 @@ def update_constraints(session: nox.Session) -> None: env = os.environ.copy() env["UV_CUSTOM_COMPILE_COMMAND"] = f"nox -s {session.name}" + # NOTE: Keep this in sync with cibuildwheel/util/_cooldown.py + ignore_cooldown = os.environ.get("CIBW_IGNORE_COOLDOWN", "").lower() in ("1", "true") + cooldown_days = 7 exclude_newer_args = ( [] - if IGNORE_COOLDOWN + if ignore_cooldown else [ - f"--exclude-newer={(datetime.now(tz=UTC).date() - timedelta(days=COOLDOWN_DAYS)).isoformat()}" + f"--exclude-newer={(datetime.now(tz=UTC).date() - timedelta(days=cooldown_days)).isoformat()}" ] ) From 29c578d258d0388145f6e6fcb14049558dcb19fa Mon Sep 17 00:00:00 2001 From: Agriya Khetarpal <74401230+agriyakhetarpal@users.noreply.github.com> Date: Thu, 7 May 2026 07:23:53 +0530 Subject: [PATCH 17/28] Bring things back to `bin/_cooldown.py` --- .pre-commit-config.yaml | 2 +- {cibuildwheel/util => bin}/_cooldown.py | 0 bin/update_nodejs.py | 3 +-- bin/update_python_build_standalone.py | 3 ++- bin/update_pythons.py | 2 +- bin/update_virtualenv.py | 2 +- pyproject.toml | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) rename {cibuildwheel/util => bin}/_cooldown.py (100%) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index e233c12f5..39f3c5783 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -29,7 +29,7 @@ repos: - id: mypy name: mypy 3.11 on cibuildwheel/ args: ["--python-version=3.11"] - exclude: ^cibuildwheel/resources/android/_cross_venv.py$ # Requires Python 3.13 or later + exclude: (^cibuildwheel/resources/android/_cross_venv.py$|^bin/_cooldown\.py$) # _cross_venv.py requires Python 3.13 or later and _cooldown.py is a support module for bin/ scripts additional_dependencies: &mypy-dependencies - bracex - build diff --git a/cibuildwheel/util/_cooldown.py b/bin/_cooldown.py similarity index 100% rename from cibuildwheel/util/_cooldown.py rename to bin/_cooldown.py diff --git a/bin/update_nodejs.py b/bin/update_nodejs.py index 11a563324..01068667f 100755 --- a/bin/update_nodejs.py +++ b/bin/update_nodejs.py @@ -26,12 +26,11 @@ import packaging.specifiers import requests import rich +from _cooldown import COOLDOWN_DAYS, IGNORE_COOLDOWN from packaging.version import InvalidVersion, Version from rich.logging import RichHandler from rich.syntax import Syntax -from cibuildwheel.util._cooldown import COOLDOWN_DAYS, IGNORE_COOLDOWN - log = logging.getLogger("cibw") # Looking up the dir instead of using utils.resources_dir diff --git a/bin/update_python_build_standalone.py b/bin/update_python_build_standalone.py index f7ac910f1..f630e6a13 100755 --- a/bin/update_python_build_standalone.py +++ b/bin/update_python_build_standalone.py @@ -12,8 +12,9 @@ import json from datetime import UTC, datetime, timedelta +from _cooldown import COOLDOWN_DAYS, IGNORE_COOLDOWN + from cibuildwheel.extra import github_api_request -from cibuildwheel.util._cooldown import COOLDOWN_DAYS, IGNORE_COOLDOWN from cibuildwheel.util.python_build_standalone import ( PythonBuildStandaloneAsset, PythonBuildStandaloneReleaseData, diff --git a/bin/update_pythons.py b/bin/update_pythons.py index be332360e..29fc47cb9 100755 --- a/bin/update_pythons.py +++ b/bin/update_pythons.py @@ -29,6 +29,7 @@ import click import requests import rich +from _cooldown import COOLDOWN_DAYS, IGNORE_COOLDOWN from packaging.specifiers import Specifier from packaging.version import Version from rich.logging import RichHandler @@ -36,7 +37,6 @@ from cibuildwheel.extra import dump_python_configurations, get_pyodide_xbuildenv_info from cibuildwheel.platforms.android import android_triplet -from cibuildwheel.util._cooldown import COOLDOWN_DAYS, IGNORE_COOLDOWN log = logging.getLogger("cibw") diff --git a/bin/update_virtualenv.py b/bin/update_virtualenv.py index eb89c1be9..7d5c69bd2 100755 --- a/bin/update_virtualenv.py +++ b/bin/update_virtualenv.py @@ -23,12 +23,12 @@ import click import rich +from _cooldown import COOLDOWN_DAYS, IGNORE_COOLDOWN from packaging.version import Version from rich.logging import RichHandler from rich.syntax import Syntax from cibuildwheel.extra import github_api_request -from cibuildwheel.util._cooldown import COOLDOWN_DAYS, IGNORE_COOLDOWN log = logging.getLogger("cibw") diff --git a/pyproject.toml b/pyproject.toml index d5c92213e..0f40a1ec5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -114,7 +114,7 @@ files = [ "cibuildwheel/*.py", "test/**/*.py", "unit_test/**/*.py", - "bin/*.py", + "bin/[!_]*.py", "noxfile.py", ] warn_unused_configs = true From 71e27aaa772fe1c6a04e709cd4de5806617b6cd9 Mon Sep 17 00:00:00 2001 From: Agriya Khetarpal <74401230+agriyakhetarpal@users.noreply.github.com> Date: Thu, 7 May 2026 07:50:57 +0530 Subject: [PATCH 18/28] Update comment --- noxfile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/noxfile.py b/noxfile.py index ffd193d3d..a83168aa2 100755 --- a/noxfile.py +++ b/noxfile.py @@ -80,7 +80,7 @@ def update_constraints(session: nox.Session) -> None: env = os.environ.copy() env["UV_CUSTOM_COMPILE_COMMAND"] = f"nox -s {session.name}" - # NOTE: Keep this in sync with cibuildwheel/util/_cooldown.py + # NOTE: Keep this in sync with bin/_cooldown.py and the Dependabot config. ignore_cooldown = os.environ.get("CIBW_IGNORE_COOLDOWN", "").lower() in ("1", "true") cooldown_days = 7 exclude_newer_args = ( From 35d044bc6c1a45df205048343e732fba8c72e007 Mon Sep 17 00:00:00 2001 From: Agriya Khetarpal <74401230+agriyakhetarpal@users.noreply.github.com> Date: Thu, 7 May 2026 16:55:20 +0530 Subject: [PATCH 19/28] Drop cooldown for everything minus Dependabot to 2 days --- bin/_cooldown.py | 5 +++-- noxfile.py | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/bin/_cooldown.py b/bin/_cooldown.py index c14bd9946..10480feb4 100644 --- a/bin/_cooldown.py +++ b/bin/_cooldown.py @@ -1,8 +1,9 @@ import os # Number of days a release must age before the update scripts will pick it up. -# NOTE: keep this in sync with the Dependabot configuration in .github/dependabot.yml. -COOLDOWN_DAYS = 7 +# This is intentionally different from Dependabot because we want to have these +# updates coming faster to align with the latest releases. +COOLDOWN_DAYS = 2 # Set CIBW_IGNORE_COOLDOWN to a truthy value to bypass the cooldown and always pick # up the very latest releases regardless of age. diff --git a/noxfile.py b/noxfile.py index a83168aa2..c601904c7 100755 --- a/noxfile.py +++ b/noxfile.py @@ -80,9 +80,9 @@ def update_constraints(session: nox.Session) -> None: env = os.environ.copy() env["UV_CUSTOM_COMPILE_COMMAND"] = f"nox -s {session.name}" - # NOTE: Keep this in sync with bin/_cooldown.py and the Dependabot config. + # NOTE: Keep this in sync with bin/_cooldown.py ignore_cooldown = os.environ.get("CIBW_IGNORE_COOLDOWN", "").lower() in ("1", "true") - cooldown_days = 7 + cooldown_days = 2 exclude_newer_args = ( [] if ignore_cooldown From da1b6ed041606db418781a082039b3e3a82732db Mon Sep 17 00:00:00 2001 From: Agriya Khetarpal <74401230+agriyakhetarpal@users.noreply.github.com> Date: Thu, 7 May 2026 20:35:40 +0530 Subject: [PATCH 20/28] `uv`'s `--exclude-newer` supports friendly times Co-Authored-By: Grzegorz Bokota <3826210+Czaki@users.noreply.github.com> --- noxfile.py | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/noxfile.py b/noxfile.py index c601904c7..56d0009f7 100755 --- a/noxfile.py +++ b/noxfile.py @@ -18,7 +18,6 @@ import os import shutil import sys -from datetime import UTC, datetime, timedelta from pathlib import Path import nox @@ -82,14 +81,7 @@ def update_constraints(session: nox.Session) -> None: # NOTE: Keep this in sync with bin/_cooldown.py ignore_cooldown = os.environ.get("CIBW_IGNORE_COOLDOWN", "").lower() in ("1", "true") - cooldown_days = 2 - exclude_newer_args = ( - [] - if ignore_cooldown - else [ - f"--exclude-newer={(datetime.now(tz=UTC).date() - timedelta(days=cooldown_days)).isoformat()}" - ] - ) + exclude_newer_args = [] if ignore_cooldown else ["--exclude-newer=2 days"] for minor_version in range(8, 15): python_version = f"3.{minor_version}" From f7195d6e0db08e032e9de35ff3cdddbe17116df3 Mon Sep 17 00:00:00 2001 From: Agriya Khetarpal <74401230+agriyakhetarpal@users.noreply.github.com> Date: Thu, 21 May 2026 20:09:13 +0530 Subject: [PATCH 21/28] Add cooldowns for Pyodide release --- bin/update_pythons.py | 11 ++++++++--- cibuildwheel/extra.py | 5 ++++- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/bin/update_pythons.py b/bin/update_pythons.py index 29fc47cb9..d7c54bfd9 100755 --- a/bin/update_pythons.py +++ b/bin/update_pythons.py @@ -435,9 +435,14 @@ def update_version_ios(self, identifier: str, version: Version) -> ConfigUrl | N class PyodideVersions: - def __init__(self) -> None: + def __init__(self, cutoff_date: date) -> None: xbuildenv_info = get_pyodide_xbuildenv_info() - self.releases = xbuildenv_info["releases"] + all_releases = xbuildenv_info["releases"] + self.releases = { + version_str: release + for version_str, release in all_releases.items() + if datetime.fromisoformat(release["published_at"]).date() <= cutoff_date + } def update_version_pyodide( self, identifier: str, version: Version, spec: Specifier, node_version: str @@ -496,7 +501,7 @@ def __init__(self) -> None: self.graalpy = GraalPyVersions(cutoff_date) - self.pyodide = PyodideVersions() + self.pyodide = PyodideVersions(cutoff_date) def update_config(self, config: MutableMapping[str, str]) -> None: identifier = config["identifier"] diff --git a/cibuildwheel/extra.py b/cibuildwheel/extra.py index d075d3217..a32155a23 100644 --- a/cibuildwheel/extra.py +++ b/cibuildwheel/extra.py @@ -85,6 +85,9 @@ class PyodideXBuildEnvRelease(typing.TypedDict): version: str python_version: str emscripten_version: str + published_at: str + url: NotRequired[str] + sha256: NotRequired[str] min_pyodide_build_version: NotRequired[str] max_pyodide_build_version: NotRequired[str] @@ -95,7 +98,7 @@ class PyodideXBuildEnvInfo(typing.TypedDict): def get_pyodide_xbuildenv_info() -> PyodideXBuildEnvInfo: xbuildenv_info_url = ( - "https://pyodide.github.io/pyodide/api/pyodide-cross-build-environments.json" + "https://pyodide.github.io/pyodide/api/v2/pyodide-cross-build-environments.json" ) with urllib.request.urlopen(xbuildenv_info_url) as response: return typing.cast("PyodideXBuildEnvInfo", json.loads(response.read().decode("utf-8"))) From 7a694c87af25f8bc72c8bdf7cee687b322186ece Mon Sep 17 00:00:00 2001 From: Agriya Khetarpal <74401230+agriyakhetarpal@users.noreply.github.com> Date: Thu, 21 May 2026 20:14:52 +0530 Subject: [PATCH 22/28] Update `COOLDOWN_DAYS` to 3 --- bin/_cooldown.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/_cooldown.py b/bin/_cooldown.py index 10480feb4..072810242 100644 --- a/bin/_cooldown.py +++ b/bin/_cooldown.py @@ -3,7 +3,7 @@ # Number of days a release must age before the update scripts will pick it up. # This is intentionally different from Dependabot because we want to have these # updates coming faster to align with the latest releases. -COOLDOWN_DAYS = 2 +COOLDOWN_DAYS = 3 # Set CIBW_IGNORE_COOLDOWN to a truthy value to bypass the cooldown and always pick # up the very latest releases regardless of age. From d83985ae0bbbe84dd3a801a7f9dc24a06d09c61f Mon Sep 17 00:00:00 2001 From: Agriya Khetarpal <74401230+agriyakhetarpal@users.noreply.github.com> Date: Thu, 21 May 2026 20:15:00 +0530 Subject: [PATCH 23/28] Drop cooldowns from Node.js --- bin/update_nodejs.py | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/bin/update_nodejs.py b/bin/update_nodejs.py index 01068667f..f45bb4139 100755 --- a/bin/update_nodejs.py +++ b/bin/update_nodejs.py @@ -6,11 +6,7 @@ # "packaging", # "requests", # "rich", -# "cibuildwheel", # ] -# -# [tool.uv.sources] -# cibuildwheel = { path = ".." } # /// @@ -18,7 +14,6 @@ import difflib import logging import tomllib -from datetime import UTC, date, datetime, timedelta from pathlib import Path from typing import Final @@ -26,7 +21,6 @@ import packaging.specifiers import requests import rich -from _cooldown import COOLDOWN_DAYS, IGNORE_COOLDOWN from packaging.version import InvalidVersion, Version from rich.logging import RichHandler from rich.syntax import Syntax @@ -53,11 +47,6 @@ def parse_nodejs_index() -> list[VersionTuple]: response = requests.get(NODEJS_INDEX) response.raise_for_status() versions_info = response.json() - cutoff_date: date = ( - date.max - if IGNORE_COOLDOWN - else (datetime.now(tz=UTC) - timedelta(days=COOLDOWN_DAYS)).date() - ) for version_info in versions_info: version_string = version_info.get("version", "???") if not version_info.get("lts", False): @@ -68,9 +57,6 @@ def parse_nodejs_index() -> list[VersionTuple]: "Ignoring release %r which does not include a linux-x64 binary", version_string ) continue - if date.fromisoformat(version_info["date"]) > cutoff_date: - log.info("Ignoring release %r within cooldown period", version_string) - continue try: version = Version(version_string) if version.is_devrelease: From 31507d3afb62fa8dc7a57c62f2f6169f85b664e1 Mon Sep 17 00:00:00 2001 From: Agriya Khetarpal <74401230+agriyakhetarpal@users.noreply.github.com> Date: Thu, 21 May 2026 20:15:09 +0530 Subject: [PATCH 24/28] Drop cooldowns for PBS --- bin/update_python_build_standalone.py | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/bin/update_python_build_standalone.py b/bin/update_python_build_standalone.py index f630e6a13..15b425df0 100755 --- a/bin/update_python_build_standalone.py +++ b/bin/update_python_build_standalone.py @@ -10,9 +10,6 @@ # /// import json -from datetime import UTC, datetime, timedelta - -from _cooldown import COOLDOWN_DAYS, IGNORE_COOLDOWN from cibuildwheel.extra import github_api_request from cibuildwheel.util.python_build_standalone import ( @@ -32,14 +29,6 @@ def main() -> None: latest_release = github_api_request("repos/astral-sh/python-build-standalone/releases/latest") latest_tag = latest_release["tag_name"] - published_at = datetime.fromisoformat(latest_release["published_at"]) - if not IGNORE_COOLDOWN and datetime.now(tz=UTC) - published_at < timedelta(days=COOLDOWN_DAYS): - print( - f"Skipping update: latest release {latest_tag!r} was published " - f"less than {COOLDOWN_DAYS} days ago." - ) - return - # Get the list of assets for the latest release github_assets = github_api_request( f"repos/astral-sh/python-build-standalone/releases/tags/{latest_tag}" From 02f4615665f0936016efbcea68ae0f3604ba79fc Mon Sep 17 00:00:00 2001 From: Agriya Khetarpal <74401230+agriyakhetarpal@users.noreply.github.com> Date: Thu, 21 May 2026 20:15:49 +0530 Subject: [PATCH 25/28] Drop cooldowns from all Pythons, except Pyodide --- bin/update_pythons.py | 113 +++++++++++++----------------------------- 1 file changed, 34 insertions(+), 79 deletions(-) diff --git a/bin/update_pythons.py b/bin/update_pythons.py index d7c54bfd9..b1561806c 100755 --- a/bin/update_pythons.py +++ b/bin/update_pythons.py @@ -21,7 +21,6 @@ import tomllib from collections.abc import Mapping, MutableMapping from datetime import UTC, date, datetime, timedelta -from email.utils import parsedate_to_datetime from pathlib import Path from typing import Any, Final, Literal, TypedDict from xml.etree import ElementTree as ET @@ -68,14 +67,14 @@ class ConfigPyodide(Config): class WindowsVersions: - def __init__(self, arch_str: ArchStr, free_threaded: bool, cutoff_date: date) -> None: + def __init__(self, arch_str: ArchStr, free_threaded: bool) -> None: response = requests.get("https://api.nuget.org/v3/index.json") response.raise_for_status() api_info = response.json() - reg_endpoint = next( - r["@id"] for r in api_info["resources"] if r["@type"] == "RegistrationsBaseUrl/3.6.0" - ) + for resource in api_info["resources"]: + if resource["@type"] == "PackageBaseAddress/3.0.0": + endpoint = resource["@id"] ARCH_DICT = {"32": "win32", "64": "win_amd64", "ARM64": "win_arm64"} PACKAGE_DICT = {"32": "pythonx86", "64": "python", "ARM64": "pythonarm64"} @@ -88,30 +87,11 @@ def __init__(self, arch_str: ArchStr, free_threaded: bool, cutoff_date: date) -> if free_threaded: package = f"{package}-freethreaded" - # NuGet serves registration responses gzip-compressed; requests decompresses - # automatically via Accept-Encoding negotiation - reg_response = requests.get(f"{reg_endpoint}{package}/index.json") - reg_response.raise_for_status() - reg_data = reg_response.json() - - # NuGet uses 1900-01-01 as a sentinel for packages whose publish date was - # not recorded. The NuGet SDK looks at this as null and treats it as - # "allow" (and dependabot-core does the same): - # https://github.com/dependabot/dependabot-core/blob/b0090acfa61b7541c040e302c760a84c702217a3/nuget/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/Cooldown.cs#L70-L75 - NUGET_DATE_SENTINEL = date(1900, 1, 1) - - self.version_dict: dict[Version, str] = {} - for page_meta in reg_data["items"]: - # Registration pages may be inlined in the index response or kept external - # See: https://github.com/microsoft/NativeAOTDependencyHelper/blob/9ad3f7be5b919f6166d60a228691e1c802797909/NativeAOTDependencyHelper.Core/Checks/NuGetRecentlyUpdatedCheck.cs#L36-L45 - # pythonarm64 and all freethreaded packages have fully-inlined pages and need no extra fetches. - items = page_meta.get("items") or requests.get(page_meta["@id"]).json()["items"] - for item in items: - entry = item["catalogEntry"] - published = datetime.fromisoformat(entry["published"]).date() - if published != NUGET_DATE_SENTINEL and published > cutoff_date: - continue - self.version_dict[Version(entry["version"])] = entry["version"] + response = requests.get(f"{endpoint}{package}/index.json") + response.raise_for_status() + cp_info = response.json() + + self.version_dict = {Version(v): v for v in cp_info["versions"]} def update_version_windows(self, spec: Specifier) -> Config | None: # Specifier.filter selects all non pre-releases that match the spec, @@ -135,7 +115,7 @@ def update_version_windows(self, spec: Specifier) -> Config | None: class GraalPyVersions: - def __init__(self, cutoff_date: date) -> None: + def __init__(self) -> None: response = requests.get("https://api.github.com/repos/oracle/graalpython/releases") response.raise_for_status() @@ -150,13 +130,7 @@ def __init__(self, cutoff_date: date) -> None: if m: release["python_version"] = Version(m.group(1)) - self.releases = [ - r - for r in releases - if "graalpy_version" in r - and "python_version" in r - and datetime.fromisoformat(r["published_at"]).date() <= cutoff_date - ] + self.releases = [r for r in releases if "graalpy_version" in r and "python_version" in r] def update_version(self, identifier: str, spec: Specifier) -> ConfigUrl | None: if "x86_64" in identifier or "amd64" in identifier: @@ -213,7 +187,7 @@ def update_version(self, identifier: str, spec: Specifier) -> ConfigUrl | None: class PyPyVersions: - def __init__(self, arch_str: ArchStr, cutoff_date: date): + def __init__(self, arch_str: ArchStr): response = requests.get("https://downloads.python.org/pypy/versions.json") response.raise_for_status() @@ -225,9 +199,7 @@ def __init__(self, arch_str: ArchStr, cutoff_date: date): self.releases = [ r for r in releases - if not r["pypy_version"].is_prerelease - and not r["pypy_version"].is_devrelease - and date.fromisoformat(r["date"]) <= cutoff_date + if not r["pypy_version"].is_prerelease and not r["pypy_version"].is_devrelease ] self.arch = arch_str @@ -293,7 +265,7 @@ def update_version_macos(self, spec: Specifier) -> ConfigUrl: class CPythonVersions: - def __init__(self, cutoff_date: date) -> None: + def __init__(self) -> None: response = requests.get( "https://www.python.org/api/v2/downloads/release/?is_published=true" ) @@ -307,10 +279,6 @@ def __init__(self, cutoff_date: date) -> None: if not release["slug"].startswith("python"): continue - release_date_str = release.get("release_date") - if release_date_str and datetime.fromisoformat(release_date_str).date() > cutoff_date: - continue - # Removing the prefix version = Version(release["name"].removeprefix("Python ")) self.versions_dict[version] = release["resource_uri"] @@ -352,7 +320,7 @@ def update_version_android(self, identifier: str, spec: Specifier) -> ConfigUrl class MavenVersions: MAVEN_URL = "https://repo.maven.apache.org/maven2/com/chaquo/python/python" - def __init__(self, cutoff_date: date) -> None: + def __init__(self) -> None: response = requests.get(f"{self.MAVEN_URL}/maven-metadata.xml") response.raise_for_status() root = ET.fromstring(response.text) @@ -363,34 +331,24 @@ def __init__(self, cutoff_date: date) -> None: assert isinstance(version_str, str), version_str self.versions.append(Version(version_str)) - self.cutoff_date = cutoff_date - def update_version_android(self, identifier: str, spec: Specifier) -> ConfigUrl | None: sorted_versions = sorted(spec.filter(self.versions), reverse=True) - # maven-metadata.xml only carries a package-level timestamp, not per-version - # dates, so we check the Last-Modified header on each candidate's POM file - for max_version in sorted_versions: + # Return a config using the highest version for the given specifier. + if sorted_versions: + max_version = sorted_versions[0] triplet = android_triplet(identifier) - pom_url = f"{self.MAVEN_URL}/{max_version}/python-{max_version}.pom" - head_response = requests.head(pom_url) - head_response.raise_for_status() - last_modified_str = head_response.headers.get("Last-Modified") - if last_modified_str: - published = parsedate_to_datetime(last_modified_str).date() - if published > self.cutoff_date: - log.info("Skipping %s: published %s is within cooldown", max_version, published) - continue return ConfigUrl( identifier=identifier, version=f"{max_version.major}.{max_version.minor}", url=f"{self.MAVEN_URL}/{max_version}/python-{max_version}-{triplet}.tar.gz", ) - return None + else: + return None class CPythonIOSVersions: - def __init__(self, cutoff_date: date) -> None: + def __init__(self) -> None: response = requests.get( "https://api.github.com/repos/beeware/Python-Apple-support/releases", headers={ @@ -405,9 +363,6 @@ def __init__(self, cutoff_date: date) -> None: # Each release has a name like "3.13-b4" for release in releases_info: - if datetime.fromisoformat(release["published_at"]).date() > cutoff_date: - continue - py_version, build = release["name"].split("-") version = Version(py_version) self.versions_dict.setdefault(version, {}) @@ -484,22 +439,22 @@ def __init__(self) -> None: else (datetime.now(tz=UTC) - timedelta(days=COOLDOWN_DAYS)).date() ) - self.windows_32 = WindowsVersions("32", False, cutoff_date) - self.windows_t_32 = WindowsVersions("32", True, cutoff_date) - self.windows_64 = WindowsVersions("64", False, cutoff_date) - self.windows_t_64 = WindowsVersions("64", True, cutoff_date) - self.windows_arm64 = WindowsVersions("ARM64", False, cutoff_date) - self.windows_t_arm64 = WindowsVersions("ARM64", True, cutoff_date) - self.windows_pypy_64 = PyPyVersions("64", cutoff_date) + self.windows_32 = WindowsVersions("32", False) + self.windows_t_32 = WindowsVersions("32", True) + self.windows_64 = WindowsVersions("64", False) + self.windows_t_64 = WindowsVersions("64", True) + self.windows_arm64 = WindowsVersions("ARM64", False) + self.windows_t_arm64 = WindowsVersions("ARM64", True) + self.windows_pypy_64 = PyPyVersions("64") - self.cpython = CPythonVersions(cutoff_date) - self.macos_pypy = PyPyVersions("64", cutoff_date) - self.macos_pypy_arm64 = PyPyVersions("ARM64", cutoff_date) + self.cpython = CPythonVersions() + self.macos_pypy = PyPyVersions("64") + self.macos_pypy_arm64 = PyPyVersions("ARM64") - self.maven = MavenVersions(cutoff_date) - self.ios_cpython = CPythonIOSVersions(cutoff_date) + self.maven = MavenVersions() + self.ios_cpython = CPythonIOSVersions() - self.graalpy = GraalPyVersions(cutoff_date) + self.graalpy = GraalPyVersions() self.pyodide = PyodideVersions(cutoff_date) From cc92301abb0bb1e9fc0a49dec7da0a5a4be3b382 Mon Sep 17 00:00:00 2001 From: Agriya Khetarpal <74401230+agriyakhetarpal@users.noreply.github.com> Date: Thu, 21 May 2026 20:23:04 +0530 Subject: [PATCH 26/28] Some duplication :( --- bin/update_pythons.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/bin/update_pythons.py b/bin/update_pythons.py index b1561806c..e40e526d2 100755 --- a/bin/update_pythons.py +++ b/bin/update_pythons.py @@ -34,7 +34,7 @@ from rich.logging import RichHandler from rich.syntax import Syntax -from cibuildwheel.extra import dump_python_configurations, get_pyodide_xbuildenv_info +from cibuildwheel.extra import dump_python_configurations from cibuildwheel.platforms.android import android_triplet log = logging.getLogger("cibw") @@ -391,8 +391,11 @@ def update_version_ios(self, identifier: str, version: Version) -> ConfigUrl | N class PyodideVersions: def __init__(self, cutoff_date: date) -> None: - xbuildenv_info = get_pyodide_xbuildenv_info() - all_releases = xbuildenv_info["releases"] + response = requests.get( + "https://pyodide.github.io/pyodide/api/v2/pyodide-cross-build-environments.json" + ) + response.raise_for_status() + all_releases = response.json()["releases"] self.releases = { version_str: release for version_str, release in all_releases.items() From 77d3f698ba49dd2fa2f1844342c7f1371cac6821 Mon Sep 17 00:00:00 2001 From: Agriya Khetarpal <74401230+agriyakhetarpal@users.noreply.github.com> Date: Thu, 21 May 2026 23:36:11 +0530 Subject: [PATCH 27/28] Remove unneeded comment --- .github/dependabot.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index f1e513c65..bea489321 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -9,6 +9,5 @@ updates: actions: patterns: - "*" - # NOTE: keep this in sync with bin/_cooldown.py cooldown: default-days: 7 From d370e390e5a972de7bdf92d8d48645346f72f80f Mon Sep 17 00:00:00 2001 From: Agriya Khetarpal <74401230+agriyakhetarpal@users.noreply.github.com> Date: Thu, 21 May 2026 23:36:32 +0530 Subject: [PATCH 28/28] Align on package cooldown and constraints updates --- bin/_cooldown.py | 1 + noxfile.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/bin/_cooldown.py b/bin/_cooldown.py index 072810242..36b3df87c 100644 --- a/bin/_cooldown.py +++ b/bin/_cooldown.py @@ -3,6 +3,7 @@ # Number of days a release must age before the update scripts will pick it up. # This is intentionally different from Dependabot because we want to have these # updates coming faster to align with the latest releases. +# NOTE: Keep this in sync with noxfile.py's update_constraints session. COOLDOWN_DAYS = 3 # Set CIBW_IGNORE_COOLDOWN to a truthy value to bypass the cooldown and always pick diff --git a/noxfile.py b/noxfile.py index 973dbf04c..736b545c6 100755 --- a/noxfile.py +++ b/noxfile.py @@ -81,7 +81,7 @@ def update_constraints(session: nox.Session) -> None: # NOTE: Keep this in sync with bin/_cooldown.py ignore_cooldown = os.environ.get("CIBW_IGNORE_COOLDOWN", "").lower() in ("1", "true") - exclude_newer_args = [] if ignore_cooldown else ["--exclude-newer=2 days"] + exclude_newer_args = [] if ignore_cooldown else ["--exclude-newer=3 days"] for minor_version in range(9, 16): python_version = f"3.{minor_version}"