Skip to content

Commit 230538c

Browse files
committed
refactor: drive from original file
1 parent 1de0718 commit 230538c

File tree

1 file changed

+106
-63
lines changed

1 file changed

+106
-63
lines changed

bin/update_pythons.py

+106-63
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,21 @@
11
#!/usr/bin/env python3
22

3+
import copy
34
import logging
4-
from itertools import groupby
55
from pathlib import Path
6-
from typing import Dict, List, Optional
6+
from typing import Dict, Optional, Union
77

88
import click
99
import requests
1010
import rich
11-
import tomlkit
12-
from packaging.specifiers import SpecifierSet
11+
import toml
12+
from packaging.specifiers import Specifier, SpecifierSet
1313
from packaging.version import Version
1414
from rich.logging import RichHandler
1515
from rich.syntax import Syntax
1616

1717
from cibuildwheel.extra import InlineArrayDictEncoder
18-
from cibuildwheel.typing import Final, Literal, PlatformName, TypedDict
18+
from cibuildwheel.typing import Final, Literal, TypedDict
1919

2020
log = logging.getLogger("cibw")
2121

@@ -32,20 +32,20 @@
3232

3333
class ConfigWinCP(TypedDict):
3434
identifier: str
35-
version: Version
35+
version: str
3636
arch: str
3737

3838

3939
class ConfigWinPP(TypedDict):
4040
identifier: str
41-
version: Version
41+
version: str
4242
arch: str
4343
url: str
4444

4545

4646
class ConfigMacOS(TypedDict):
4747
identifier: str
48-
version: Version
48+
version: str
4949
url: str
5050

5151

@@ -66,7 +66,8 @@ def __init__(self, arch_str: ArchStr) -> None:
6666
ARCH_DICT = {"32": "win32", "64": "win_amd64"}
6767
PACKAGE_DICT = {"32": "pythonx86", "64": "python"}
6868

69-
arch = ARCH_DICT[arch_str]
69+
self.arch_str = arch_str
70+
self.arch = ARCH_DICT[arch_str]
7071
package = PACKAGE_DICT[arch_str]
7172

7273
response = requests.get(f"{endpoint}{package}/index.json")
@@ -76,21 +77,21 @@ def __init__(self, arch_str: ArchStr) -> None:
7677
versions = (Version(v) for v in cp_info["versions"])
7778
self.versions = sorted(v for v in versions if not v.is_devrelease)
7879

79-
def update_version(self, spec: Specifier) -> Optional[ConfigWinCP]:
80+
def update_version_windows(self, spec: Specifier) -> Optional[ConfigWinCP]:
8081
versions = sorted(v for v in self.versions if spec.contains(v))
8182
if not all(v.is_prerelease for v in versions):
8283
versions = [v for v in versions if not v.is_prerelease]
83-
log.debug(versions)
84+
log.debug(f"Windows {self.arch} {spec} has {', '.join(str(v) for v in versions)}")
8485

8586
if not versions:
8687
return None
8788

8889
version = versions[-1]
89-
identifier = f"cp{version.major}{version.minor}-{ARCH_DICT[arch]}"
90+
identifier = f"cp{version.major}{version.minor}-{self.arch}"
9091
result = ConfigWinCP(
9192
identifier=identifier,
92-
version=version,
93-
arch=arch,
93+
version=str(version),
94+
arch=self.arch_str,
9495
)
9596
return result
9697

@@ -106,47 +107,56 @@ def __init__(self, arch_str: ArchStr):
106107
release["pypy_version"] = Version(release["pypy_version"])
107108
release["python_version"] = Version(release["python_version"])
108109

109-
self.releases = [r for r in releases if not r["pypy_version"].is_prerelease and r["pypy_version"].is_devrelease]
110-
self.arch == arch_str
110+
self.releases = [
111+
r for r in releases if not r["pypy_version"].is_prerelease and not r["pypy_version"].is_devrelease
112+
]
113+
self.arch = arch_str
111114

112-
def update_version_windows(self, spec: Specifier) -> Optional[ConfigWinCP]:
115+
def update_version_windows(self, spec: Specifier) -> ConfigWinCP:
113116
if self.arch != "32":
114117
raise RuntimeError("64 bit releases not supported yet on Windows")
115118

116-
releases = [r for r in releases if spec.contains(r["python_verison"])]
119+
releases = [r for r in self.releases if spec.contains(r["python_version"])]
117120
releases = sorted(releases, key=lambda r: r["pypy_version"])
118121

119122
if not releases:
120-
return None
123+
raise RuntimeError(f"PyPy Win {self.arch} not found for {spec}! {self.releases}")
121124

122125
release = releases[-1]
123126
version = release["python_version"]
124127
identifier = f"pp{version.major}{version.minor}-win32"
128+
129+
(url,) = [rf["download_url"] for rf in release["files"] if "" in rf["platform"] == "win32"]
130+
125131
return ConfigWinPP(
126132
identifier=identifier,
127-
version=Version(f"{version.major}.{version.minor}"),
133+
version=f"{version.major}.{version.minor}",
128134
arch="32",
129-
url=r["download_url"],
135+
url=url,
130136
)
131137

132-
def update_version_macos(self, spec: Specifier) -> Optional[ConfigMacOS]:
138+
def update_version_macos(self, spec: Specifier) -> ConfigMacOS:
133139
if self.arch != "64":
134140
raise RuntimeError("Other archs not supported yet on macOS")
135141

136-
releases = [r for r in releases if spec.contains(r["python_verison"])]
142+
releases = [r for r in self.releases if spec.contains(r["python_version"])]
137143
releases = sorted(releases, key=lambda r: r["pypy_version"])
138144

139145
if not releases:
140-
return None
146+
raise RuntimeError(f"PyPy macOS {self.arch} not found for {spec}!")
141147

142148
release = releases[-1]
143149
version = release["python_version"]
144-
identifier = f"pp{version.major}{version.minor}-win32"
150+
identifier = f"pp{version.major}{version.minor}-macosx_x86_64"
151+
152+
(url,) = [
153+
rf["download_url"] for rf in release["files"] if "" in rf["platform"] == "darwin" and rf["arch"] == "x64"
154+
]
145155

146156
return ConfigMacOS(
147157
identifier=identifier,
148-
version=Version(f"{version.major}.{version.minor}"),
149-
url=rf["download_url"],
158+
version=f"{version.major}.{version.minor}",
159+
url=url,
150160
)
151161

152162

@@ -162,36 +172,89 @@ def __init__(self, plat_arch: str, file_ident: str) -> None:
162172

163173
releases_info = response.json()
164174

165-
# Removing the prefix, Python 3.9 would use: release["name"].removeprefix("Python ")
166-
known_versions = {Version(release["name"][7:]): _get_id(release["resource_uri"]) for release in releases_info}
167-
self.versions = sorted(v for v in known_versions if not (v.is_prerelease or v.is_devrelease))
175+
self.versions_dict: Dict[Version, int] = {}
176+
for release in releases_info:
177+
# Removing the prefix, Python 3.9 would use: release["name"].removeprefix("Python ")
178+
version = Version(release["name"][7:])
179+
180+
if not version.is_prerelease and not version.is_devrelease:
181+
uri = int(release["resource_uri"].rstrip("/").split("/")[-1])
182+
self.versions_dict[version] = uri
168183

169-
def update_python_macos(self, spec: Specifier) -> Optional[ConfigMacOS]:
184+
self.file_ident = file_ident
185+
self.plat_arch = plat_arch
186+
187+
def update_version_macos(self, spec: Specifier) -> Optional[ConfigMacOS]:
170188

171-
sorted_versions = [v for v in self.versions if spec.contains(v)]
189+
sorted_versions = sorted(v for v in self.versions_dict if spec.contains(v))
172190

173-
for version in reversed(versions):
191+
for version in reversed(sorted_versions):
174192
# Find the first patch version that contains the requested file
175-
uri = self.versions[version]
193+
uri = self.versions_dict[version]
176194
response = requests.get(f"https://www.python.org/api/v2/downloads/release_file/?release={uri}")
177195
response.raise_for_status()
178196
file_info = response.json()
179197

180-
canidate_files = [rf["url"] for rf in file_info if file_ident in rf["url"]]
181-
if canidate_files:
198+
urls = [rf["url"] for rf in file_info if self.file_ident in rf["url"]]
199+
if urls:
182200
return ConfigMacOS(
183-
identifier=f"cp{version.major}{version.minor}-{plat_arch}",
184-
version=version,
185-
url=canidate_files[0],
201+
identifier=f"cp{version.major}{version.minor}-{self.plat_arch}",
202+
version=f"{version.major}.{version.minor}",
203+
url=urls[0],
186204
)
187205

188206
return None
189207

190208

209+
class AllVersions:
210+
def __init__(self) -> None:
211+
self.windows_32 = WindowsVersions("32")
212+
self.windows_64 = WindowsVersions("64")
213+
self.windows_pypy = PyPyVersions("32")
214+
215+
self.macos_6 = CPythonVersions(plat_arch="macosx_x86_64", file_ident="macosx10.6.pkg")
216+
self.macos_9 = CPythonVersions(plat_arch="macosx_x86_64", file_ident="macosx10.9.pkg")
217+
self.macos_u2 = CPythonVersions(
218+
plat_arch="macosx_universal2",
219+
file_ident="macos11.0.pkg",
220+
)
221+
self.macos_pypy = PyPyVersions("64")
222+
223+
def update_config(self, config: Dict[str, str]) -> None:
224+
identifier = config["identifier"]
225+
version = Version(config["version"])
226+
spec = Specifier(f"=={version.major}.{version.minor}.*")
227+
log.info(f"Reading in '{identifier}' -> {spec} @ {version}")
228+
orig_config = copy.copy(config)
229+
config_update: Optional[AnyConfig]
230+
231+
if "macosx_x86_64" in identifier:
232+
if identifier.startswith("pp"):
233+
config_update = self.macos_pypy.update_version_macos(spec)
234+
else:
235+
config_update = self.macos_9.update_version_macos(spec) or self.macos_6.update_version_macos(spec)
236+
assert config_update is not None, f"MacOS {spec} not found!"
237+
config.update(**config_update)
238+
elif "win32" in identifier:
239+
if identifier.startswith("pp"):
240+
config.update(**self.windows_pypy.update_version_windows(spec))
241+
else:
242+
config_update = self.windows_32.update_version_windows(spec)
243+
if config_update:
244+
config.update(**config_update)
245+
elif "win_amd64" in identifier:
246+
config_update = self.windows_64.update_version_windows(spec)
247+
if config_update:
248+
config.update(**config_update)
249+
250+
if config != orig_config:
251+
log.info(f" Updated {orig_config} to {config}")
252+
253+
191254
@click.command()
192255
@click.option("--inplace", is_flag=True)
193256
@click.option("--level", default="INFO", type=click.Choice(["INFO", "DEBUG", "TRACE"], case_sensitive=False))
194-
def update_pythons(inplace: bool, prereleases: bool, level: str) -> None:
257+
def update_pythons(inplace: bool, level: str) -> None:
195258

196259
logging.basicConfig(
197260
level="INFO",
@@ -201,34 +264,14 @@ def update_pythons(inplace: bool, prereleases: bool, level: str) -> None:
201264
)
202265
log.setLevel(level)
203266

204-
windows_32 = WindowsVersions("32")
205-
windows_64 = WindowsVersions("64")
206-
windows_pypy = PyPyVersions("32")
207-
208-
macos_6 = CPythonVersions(plat_arch="macosx_x86_64", file_ident="macosx10.6.pkg")
209-
210-
macos_9 = CPythonVersions(plat_arch="macosx_x86_64", file_ident="macosx10.9.pkg")
211-
212-
macos_u2 = CPythonVersions(
213-
plat_arch="macosx_universal2",
214-
file_ident="macos11.0.pkg",
215-
)
216-
217-
macos_pypy = PyPyVersions("64")
218-
267+
all_versions = AllVersions()
219268
configs = toml.load(RESOURCES_DIR / "build-platforms.toml")
220269

221270
for config in configs["windows"]["python_configurations"]:
222-
version = Version(config["version"])
223-
spec = Specifier(f"=={version.major}.{version.minor}.*")
224-
arch = config["arch"]
225-
cpython = config["identifier"].startswith("cp")
271+
all_versions.update_config(config)
226272

227273
for config in configs["macos"]["python_configurations"]:
228-
version = Version(config["version"])
229-
spec = Specifier(f"=={version.major}.{version.minor}.*")
230-
arch = "64"
231-
pypy = config["identifier"].startswith("pp")
274+
all_versions.update_config(config)
232275

233276
if inplace:
234277
with open(RESOURCES_DIR / "build-platforms.toml", "w") as f:

0 commit comments

Comments
 (0)