Skip to content

Commit e4b0aa8

Browse files
committed
feat: support macos too, logging output
1 parent 051c684 commit e4b0aa8

File tree

3 files changed

+126
-12
lines changed

3 files changed

+126
-12
lines changed

.pre-commit-config.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ repos:
2929
- id: mypy
3030
files: ^(cibuildwheel/|test/|bin/projects.py|bin/update_pythons.py|unit_test/)
3131
pass_filenames: false
32-
additional_dependencies: [packaging]
32+
additional_dependencies: [packaging, click]
3333

3434
- repo: https://github.com/asottile/pyupgrade
3535
rev: v2.7.4

bin/update_pythons.py

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

3+
import logging
34
from itertools import groupby
45
from pathlib import Path
56
from typing import List
67

78
import click
89
import requests
10+
import rich
911
import toml
12+
from packaging.specifiers import SpecifierSet
1013
from packaging.version import Version
14+
from rich.logging import RichHandler
15+
from rich.syntax import Syntax
1116

1217
from cibuildwheel.extra import InlineArrayDictEncoder
13-
from cibuildwheel.typing import PlatformName, TypedDict
18+
from cibuildwheel.typing import Final, PlatformName, TypedDict
19+
20+
log = logging.getLogger("cibw")
1421

1522
# Looking up the dir instead of using utils.resources_dir
1623
# since we want to write to it.
17-
DIR = Path(__file__).parent.parent.resolve()
18-
RESOURCES_DIR = DIR / "cibuildwheel/resources"
24+
DIR: Final[Path] = Path(__file__).parent.parent.resolve()
25+
RESOURCES_DIR: Final[Path] = DIR / "cibuildwheel/resources"
26+
27+
CIBW_SUPPORTED_PYTHONS: Final[SpecifierSet] = SpecifierSet(">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*")
1928

2029

2130
class AnyConfig(TypedDict):
@@ -32,7 +41,12 @@ class ConfigWinPP(AnyConfig):
3241
url: str
3342

3443

44+
class ConfigMacOS(AnyConfig):
45+
url: str
46+
47+
3548
def get_cpython_windows() -> List[ConfigWinCP]:
49+
log.info("[bold]Collecting Windows CPython from nuget")
3650
ARCH_DICT = {"32": "win32", "64": "win_amd64"}
3751

3852
response = requests.get("https://api.nuget.org/v3/index.json")
@@ -65,10 +79,12 @@ def get_cpython_windows() -> List[ConfigWinCP]:
6579
arch=arch,
6680
)
6781
)
82+
log.debug(items[-1])
6883
return items
6984

7085

7186
def get_pypy(platform: PlatformName) -> List[AnyConfig]:
87+
log.info("[bold]Collecting PyPy from python.org")
7288

7389
response = requests.get("https://downloads.python.org/pypy/versions.json")
7490
response.raise_for_status()
@@ -98,11 +114,71 @@ def get_pypy(platform: PlatformName) -> List[AnyConfig]:
98114
url=rf["download_url"],
99115
)
100116
)
117+
log.debug(items[-1])
118+
break
119+
elif platform == "macos":
120+
if rf["platform"] == "darwin" and rf["arch"] == "x64":
121+
identifier = f"pp{version.major}{version.minor}-macosx_x86_64"
122+
items.append(
123+
ConfigMacOS(
124+
identifier=identifier,
125+
version=Version(f"{version.major}.{version.minor}"),
126+
url=rf["download_url"],
127+
)
128+
)
129+
log.debug(items[-1])
101130
break
102131

103132
return items
104133

105134

135+
def _get_id(resource_uri: str) -> int:
136+
return int(resource_uri.rstrip("/").split("/")[-1])
137+
138+
139+
def get_cpython(
140+
plat_arch: str,
141+
file_ident: str,
142+
versions: SpecifierSet = CIBW_SUPPORTED_PYTHONS,
143+
) -> List[ConfigMacOS]:
144+
log.info(f"[bold]Collecting {plat_arch} CPython from Python.org")
145+
146+
response = requests.get("https://www.python.org/api/v2/downloads/release/?is_published=true")
147+
response.raise_for_status()
148+
149+
releases_info = response.json()
150+
# Removing the prefix, Python 3.9 would use: release["name"].removeprefix("Python ")
151+
known_versions = {Version(release["name"][7:]): _get_id(release["resource_uri"]) for release in releases_info}
152+
153+
items: List[ConfigMacOS] = []
154+
155+
sorted_versions = sorted((v for v in known_versions if versions.contains(v) and not v.is_prerelease), reverse=True)
156+
# Group is a list of sorted patch versions
157+
for pair, group in groupby(sorted_versions, lambda x: (x.major, x.minor)):
158+
log.info(f"[bold]Working on {pair[0]}.{pair[1]}")
159+
# Find the first patch version that contains the requested file
160+
for version in group:
161+
uri = known_versions[version]
162+
163+
log.info(f" Checking {version}")
164+
response = requests.get(f"https://www.python.org/api/v2/downloads/release_file/?release={uri}")
165+
response.raise_for_status()
166+
file_info = response.json()
167+
168+
canidate_files = [rf["url"] for rf in file_info if file_ident in rf["url"]]
169+
if canidate_files:
170+
items.append(
171+
ConfigMacOS(
172+
identifier=f"cp{version.major}{version.minor}-{plat_arch}",
173+
version=version,
174+
url=canidate_files[0],
175+
)
176+
)
177+
log.info("[green] Found!")
178+
break
179+
return items
180+
181+
106182
def sort_and_filter_configs(
107183
orig_items: List[AnyConfig],
108184
*,
@@ -154,28 +230,65 @@ def sort_and_filter_configs(
154230
@click.command()
155231
@click.option("--inplace", is_flag=True)
156232
@click.option("--prereleases", is_flag=True)
157-
@click.option("--all", is_flag=True)
158-
def update_pythons(inplace: bool, prereleases: bool, all: bool) -> None:
233+
@click.option("--level", default="INFO", type=click.Choice(["INFO", "DEBUG", "TRACE"], case_sensitive=False))
234+
def update_pythons(inplace: bool, prereleases: bool, level: str) -> None:
235+
236+
logging.basicConfig(
237+
level="INFO",
238+
format="%(message)s",
239+
datefmt="[%X]",
240+
handlers=[RichHandler(rich_tracebacks=True, markup=True)],
241+
)
242+
log.setLevel(level)
243+
159244
windows_configs: List[AnyConfig] = [
160245
*CLASSIC_WINDOWS,
161246
*get_cpython_windows(),
162247
*get_pypy("windows"),
163248
]
164249

165-
if not all:
166-
windows_configs = sort_and_filter_configs(
167-
windows_configs,
168-
prereleases=prereleases,
169-
)
250+
windows_configs = sort_and_filter_configs(
251+
windows_configs,
252+
prereleases=prereleases,
253+
)
254+
255+
macos_configs = [
256+
*get_cpython(
257+
plat_arch="macosx_x86_64",
258+
file_ident="macosx10.9.pkg",
259+
),
260+
*get_cpython(
261+
plat_arch="macosx_x86_64",
262+
file_ident="macosx10.6.pkg",
263+
versions=SpecifierSet("==3.5.*"),
264+
),
265+
*get_pypy("macos"),
266+
]
267+
268+
# For universal2:
269+
# plat_arch="macosx_universal2",
270+
# file_ident="macos11.0.pkg",
271+
# versions=SpecifierSet(">=3.8"),
272+
273+
macos_configs = sort_and_filter_configs(
274+
macos_configs,
275+
prereleases=prereleases,
276+
)
277+
278+
for config in macos_configs:
279+
config["version"] = Version("{0.major}.{0.minor}".format(config["version"]))
170280

171281
configs = toml.load(RESOURCES_DIR / "build-platforms.toml")
172282
configs["windows"]["python_configurations"] = windows_configs
283+
configs["macos"]["python_configurations"] = macos_configs
173284

174285
if inplace:
175286
with open(RESOURCES_DIR / "build-platforms.toml", "w") as f:
176287
toml.dump(configs, f, encoder=InlineArrayDictEncoder()) # type: ignore
177288
else:
178-
print(toml.dumps(configs, encoder=InlineArrayDictEncoder())) # type: ignore
289+
output = toml.dumps(configs, encoder=InlineArrayDictEncoder()) # type: ignore
290+
rich.print(Syntax(output, "toml", theme="ansi_light"))
291+
log.info("File not changed, use --inplace flag to update.")
179292

180293

181294
if __name__ == "__main__":

setup.cfg

+1
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ dev =
5252
requests
5353
typing-extensions
5454
packaging>=20.8
55+
rich>=9.6
5556

5657
[options.packages.find]
5758
include =

0 commit comments

Comments
 (0)