feat: add SHA256 verification for direct downloads
Store SHA256 hashes when running update scripts and verify them
when downloading files at build time. This improves security by
detecting unexpected changes to downloaded artifacts.
Platforms covered: macOS (CPython, PyPy, GraalPy), iOS, Android,
virtualenv, and python-build-standalone. Windows (nuget) and
Linux (Docker) are excluded.
SHA256 sources per platform:
- macOS/iOS/Android CPython (python.org): sha256_sum from API
- GraalPy: .sha256 sidecar assets from GitHub releases
- python-build-standalone: SHA256SUMS file in release
- PyPy, BeeWare iOS, Maven (Chaquopy): stream-download and compute
Changes:
- cibuildwheel/util/file.py: add sha256 param to download()
- cibuildwheel/platforms/{macos,ios,android}.py: add sha256 to
PythonConfiguration and pass to download()
- cibuildwheel/venv.py: read sha256 from toml and pass to download()
- cibuildwheel/util/python_build_standalone.py: add sha256 to
PythonBuildStandaloneAsset and pass to download()
- cibuildwheel/resources/build-platforms.toml: add sha256 fields
- cibuildwheel/resources/virtualenv.toml: add sha256 field
- cibuildwheel/resources/python-build-standalone-releases.json: add sha256
- bin/update_pythons.py: compute/store sha256 per source strategy
- bin/update_virtualenv.py: compute sha256 by streaming download
- bin/update_python_build_standalone.py: parse SHA256SUMS file
Closes #908
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Assisted-by: copilot-cli:claude-sonnet-4.6
This commit is contained in:
co-authored by
Copilot
parent
e3e7cc9e07
commit
97537fe937
@@ -3,6 +3,7 @@
|
|||||||
# /// script
|
# /// script
|
||||||
# dependencies = [
|
# dependencies = [
|
||||||
# "cibuildwheel",
|
# "cibuildwheel",
|
||||||
|
# "requests",
|
||||||
# ]
|
# ]
|
||||||
#
|
#
|
||||||
# [tool.uv.sources]
|
# [tool.uv.sources]
|
||||||
@@ -11,6 +12,8 @@
|
|||||||
|
|
||||||
import json
|
import json
|
||||||
|
|
||||||
|
import requests
|
||||||
|
|
||||||
from cibuildwheel.extra import github_api_request
|
from cibuildwheel.extra import github_api_request
|
||||||
from cibuildwheel.util.python_build_standalone import (
|
from cibuildwheel.util.python_build_standalone import (
|
||||||
PythonBuildStandaloneAsset,
|
PythonBuildStandaloneAsset,
|
||||||
@@ -34,8 +37,27 @@ def main() -> None:
|
|||||||
f"repos/astral-sh/python-build-standalone/releases/tags/{latest_tag}"
|
f"repos/astral-sh/python-build-standalone/releases/tags/{latest_tag}"
|
||||||
)["assets"]
|
)["assets"]
|
||||||
|
|
||||||
|
# Build a sha256 map from the SHA256SUMS file in the release
|
||||||
|
sha256_sums_urls = [
|
||||||
|
ga["browser_download_url"] for ga in github_assets if ga["name"] == "SHA256SUMS"
|
||||||
|
]
|
||||||
|
name_to_sha256: dict[str, str] = {}
|
||||||
|
if sha256_sums_urls:
|
||||||
|
response = requests.get(sha256_sums_urls[0])
|
||||||
|
response.raise_for_status()
|
||||||
|
for line in response.text.splitlines():
|
||||||
|
parts = line.split()
|
||||||
|
if len(parts) == 2:
|
||||||
|
sha256_hex, filename = parts
|
||||||
|
# The filename may have a leading "./" or spaces - strip it
|
||||||
|
name_to_sha256[filename.lstrip("./")] = sha256_hex
|
||||||
|
|
||||||
assets = [
|
assets = [
|
||||||
PythonBuildStandaloneAsset(name=ga["name"], url=ga["browser_download_url"])
|
PythonBuildStandaloneAsset(
|
||||||
|
name=ga["name"],
|
||||||
|
url=ga["browser_download_url"],
|
||||||
|
sha256=name_to_sha256.get(ga["name"], ""),
|
||||||
|
)
|
||||||
for ga in github_assets
|
for ga in github_assets
|
||||||
if ga["name"].endswith("install_only.tar.gz")
|
if ga["name"].endswith("install_only.tar.gz")
|
||||||
]
|
]
|
||||||
|
|||||||
+45
-4
@@ -15,12 +15,13 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import difflib
|
import difflib
|
||||||
|
import hashlib
|
||||||
import logging
|
import logging
|
||||||
import operator
|
import operator
|
||||||
import re
|
import re
|
||||||
import tomllib
|
import tomllib
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any, Final, Literal, TypedDict
|
from typing import Any, Final, Literal, NotRequired, TypedDict
|
||||||
from xml.etree import ElementTree as ET
|
from xml.etree import ElementTree as ET
|
||||||
|
|
||||||
import click
|
import click
|
||||||
@@ -56,6 +57,7 @@ class Config(TypedDict):
|
|||||||
|
|
||||||
class ConfigUrl(Config):
|
class ConfigUrl(Config):
|
||||||
url: str
|
url: str
|
||||||
|
sha256: NotRequired[str]
|
||||||
|
|
||||||
|
|
||||||
class ConfigPyodide(Config):
|
class ConfigPyodide(Config):
|
||||||
@@ -179,10 +181,23 @@ class GraalPyVersions:
|
|||||||
]
|
]
|
||||||
if urls:
|
if urls:
|
||||||
(url,) = urls
|
(url,) = urls
|
||||||
|
# Fetch sha256 from the ".sha256" sidecar asset in the same release
|
||||||
|
sha256 = ""
|
||||||
|
sha256_asset_name = url.rsplit("/", 1)[-1] + ".sha256"
|
||||||
|
sha256_urls = [
|
||||||
|
rf["browser_download_url"]
|
||||||
|
for rf in release["assets"]
|
||||||
|
if rf["name"] == sha256_asset_name
|
||||||
|
]
|
||||||
|
if sha256_urls:
|
||||||
|
sha256_response = requests.get(sha256_urls[0])
|
||||||
|
sha256_response.raise_for_status()
|
||||||
|
sha256 = sha256_response.text.strip().split()[0]
|
||||||
return ConfigUrl(
|
return ConfigUrl(
|
||||||
identifier=identifier,
|
identifier=identifier,
|
||||||
version=f"{version.major}.{version.minor}",
|
version=f"{version.major}.{version.minor}",
|
||||||
url=url,
|
url=url,
|
||||||
|
sha256=sha256,
|
||||||
)
|
)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@@ -298,12 +313,14 @@ class CPythonVersions:
|
|||||||
uri = self.versions_dict[new_version]
|
uri = self.versions_dict[new_version]
|
||||||
files = [rf for rf in self.files_info if rf["release"] == uri]
|
files = [rf for rf in self.files_info if rf["release"] == uri]
|
||||||
|
|
||||||
urls = [rf["url"] for rf in files if file_ident in rf["url"]]
|
matching = [rf for rf in files if file_ident in rf["url"]]
|
||||||
if urls:
|
if matching:
|
||||||
|
rf = matching[0]
|
||||||
return ConfigUrl(
|
return ConfigUrl(
|
||||||
identifier=identifier,
|
identifier=identifier,
|
||||||
version=f"{new_version.major}.{new_version.minor}",
|
version=f"{new_version.major}.{new_version.minor}",
|
||||||
url=urls[0],
|
url=rf["url"],
|
||||||
|
sha256=rf.get("sha256_sum", ""),
|
||||||
)
|
)
|
||||||
|
|
||||||
return None
|
return None
|
||||||
@@ -448,6 +465,16 @@ class AllVersions:
|
|||||||
|
|
||||||
self.pyodide = PyodideVersions()
|
self.pyodide = PyodideVersions()
|
||||||
|
|
||||||
|
def _stream_sha256(self, url: str) -> str:
|
||||||
|
"""Download a file (streaming) and return its SHA256 hex digest."""
|
||||||
|
log.debug("Computing sha256 for %s by streaming download...", url)
|
||||||
|
response = requests.get(url, stream=True)
|
||||||
|
response.raise_for_status()
|
||||||
|
hasher = hashlib.sha256()
|
||||||
|
for chunk in response.iter_content(65536):
|
||||||
|
hasher.update(chunk)
|
||||||
|
return hasher.hexdigest()
|
||||||
|
|
||||||
def update_config(self, config: MutableMapping[str, str]) -> None:
|
def update_config(self, config: MutableMapping[str, str]) -> None:
|
||||||
identifier = config["identifier"]
|
identifier = config["identifier"]
|
||||||
version = Version(config["version"])
|
version = Version(config["version"])
|
||||||
@@ -505,6 +532,20 @@ class AllVersions:
|
|||||||
)
|
)
|
||||||
|
|
||||||
assert config_update is not None, f"{identifier} not found!"
|
assert config_update is not None, f"{identifier} not found!"
|
||||||
|
|
||||||
|
# Fill in sha256 for URL-based configs if not already provided by the
|
||||||
|
# update_version_* method (e.g. PyPy, BeeWare iOS, Maven have no sidecar).
|
||||||
|
# Widen the type to allow arbitrary key access on the underlying dict.
|
||||||
|
config_update_dict: dict[str, str] = config_update # type: ignore[assignment]
|
||||||
|
if "url" in config_update_dict and "sha256" not in config_update_dict:
|
||||||
|
url = config_update_dict["url"]
|
||||||
|
existing_sha256 = config.get("sha256", "")
|
||||||
|
if url == config.get("url") and existing_sha256:
|
||||||
|
# URL unchanged — preserve the existing sha256
|
||||||
|
config_update_dict["sha256"] = existing_sha256
|
||||||
|
else:
|
||||||
|
config_update_dict["sha256"] = self._stream_sha256(url)
|
||||||
|
|
||||||
if config_update != config:
|
if config_update != config:
|
||||||
log.info(" Updated %s to %s", config, config_update)
|
log.info(" Updated %s to %s", config, config_update)
|
||||||
config.clear()
|
config.clear()
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
# dependencies = [
|
# dependencies = [
|
||||||
# "click",
|
# "click",
|
||||||
# "packaging",
|
# "packaging",
|
||||||
|
# "requests",
|
||||||
# "rich",
|
# "rich",
|
||||||
# "cibuildwheel",
|
# "cibuildwheel",
|
||||||
# ]
|
# ]
|
||||||
@@ -15,12 +16,14 @@
|
|||||||
|
|
||||||
import dataclasses
|
import dataclasses
|
||||||
import difflib
|
import difflib
|
||||||
|
import hashlib
|
||||||
import logging
|
import logging
|
||||||
import tomllib
|
import tomllib
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Final
|
from typing import Final
|
||||||
|
|
||||||
import click
|
import click
|
||||||
|
import requests
|
||||||
import rich
|
import rich
|
||||||
from packaging.version import Version
|
from packaging.version import Version
|
||||||
from rich.logging import RichHandler
|
from rich.logging import RichHandler
|
||||||
@@ -92,16 +95,26 @@ def update_virtualenv(force: bool, level: str) -> None:
|
|||||||
if latest_release.version > Version(local_version):
|
if latest_release.version > Version(local_version):
|
||||||
version = latest_release.name
|
version = latest_release.name
|
||||||
url = latest_release.download_url
|
url = latest_release.download_url
|
||||||
|
# Compute sha256 by streaming the new download
|
||||||
|
log.info("Computing sha256 for %s...", url)
|
||||||
|
response = requests.get(url, stream=True)
|
||||||
|
response.raise_for_status()
|
||||||
|
hasher = hashlib.sha256()
|
||||||
|
for chunk in response.iter_content(65536):
|
||||||
|
hasher.update(chunk)
|
||||||
|
sha256 = hasher.hexdigest()
|
||||||
else:
|
else:
|
||||||
version = local_version
|
version = local_version
|
||||||
url = default["url"]
|
url = default["url"]
|
||||||
|
sha256 = default.get("sha256", "")
|
||||||
|
|
||||||
configurations["default"] = {
|
configurations["default"] = {
|
||||||
"version": version,
|
"version": version,
|
||||||
"url": url,
|
"url": url,
|
||||||
|
"sha256": sha256,
|
||||||
}
|
}
|
||||||
result_toml = "".join(
|
result_toml = "".join(
|
||||||
f'{key} = {{ version = "{value["version"]}", url = "{value["url"]}" }}\n'
|
f'{key} = {{ version = "{value["version"]}", url = "{value["url"]}", sha256 = "{value.get("sha256", "")}" }}\n'
|
||||||
for key, value in configurations.items()
|
for key, value in configurations.items()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -72,6 +72,7 @@ class PythonConfiguration:
|
|||||||
version: str
|
version: str
|
||||||
identifier: str
|
identifier: str
|
||||||
url: str
|
url: str
|
||||||
|
sha256: str = ""
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def arch(self) -> str:
|
def arch(self) -> str:
|
||||||
@@ -184,7 +185,7 @@ def setup_target_python(config: PythonConfiguration, build_path: Path) -> Path:
|
|||||||
python_tgz = CIBW_CACHE_PATH / config.url.rpartition("/")[-1]
|
python_tgz = CIBW_CACHE_PATH / config.url.rpartition("/")[-1]
|
||||||
with FileLock(f"{python_tgz}.lock"):
|
with FileLock(f"{python_tgz}.lock"):
|
||||||
if not python_tgz.exists():
|
if not python_tgz.exists():
|
||||||
download(config.url, python_tgz)
|
download(config.url, python_tgz, sha256=config.sha256 or None)
|
||||||
|
|
||||||
python_dir = build_path / "python"
|
python_dir = build_path / "python"
|
||||||
python_dir.mkdir()
|
python_dir.mkdir()
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ class PythonConfiguration:
|
|||||||
identifier: str
|
identifier: str
|
||||||
url: str
|
url: str
|
||||||
build_url: str
|
build_url: str
|
||||||
|
sha256: str = ""
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def sdk(self) -> str:
|
def sdk(self) -> str:
|
||||||
@@ -133,7 +134,7 @@ def install_target_cpython(tmp: Path, config: PythonConfiguration, free_threadin
|
|||||||
with FileLock(str(installation_path) + ".lock"):
|
with FileLock(str(installation_path) + ".lock"):
|
||||||
if not installation_path.exists():
|
if not installation_path.exists():
|
||||||
downloaded_tar_gz = tmp / ios_python_tar_gz
|
downloaded_tar_gz = tmp / ios_python_tar_gz
|
||||||
download(config.url, downloaded_tar_gz)
|
download(config.url, downloaded_tar_gz, sha256=config.sha256 or None)
|
||||||
installation_path.mkdir(parents=True, exist_ok=True)
|
installation_path.mkdir(parents=True, exist_ok=True)
|
||||||
call("tar", "-C", installation_path, "-xf", downloaded_tar_gz)
|
call("tar", "-C", installation_path, "-xf", downloaded_tar_gz)
|
||||||
downloaded_tar_gz.unlink()
|
downloaded_tar_gz.unlink()
|
||||||
|
|||||||
@@ -88,6 +88,7 @@ class PythonConfiguration:
|
|||||||
version: str
|
version: str
|
||||||
identifier: str
|
identifier: str
|
||||||
url: str
|
url: str
|
||||||
|
sha256: str = ""
|
||||||
|
|
||||||
|
|
||||||
def all_python_configurations() -> list[PythonConfiguration]:
|
def all_python_configurations() -> list[PythonConfiguration]:
|
||||||
@@ -135,7 +136,9 @@ def get_python_configurations(
|
|||||||
return python_configurations
|
return python_configurations
|
||||||
|
|
||||||
|
|
||||||
def install_cpython(_tmp: Path, version: str, url: str, free_threading: bool) -> Path:
|
def install_cpython(
|
||||||
|
_tmp: Path, version: str, url: str, free_threading: bool, sha256: str = ""
|
||||||
|
) -> Path:
|
||||||
ft = "T" if free_threading else ""
|
ft = "T" if free_threading else ""
|
||||||
installation_path = Path(f"/Library/Frameworks/Python{ft}.framework/Versions/{version}")
|
installation_path = Path(f"/Library/Frameworks/Python{ft}.framework/Versions/{version}")
|
||||||
with FileLock(CIBW_CACHE_PATH / f"cpython{version}.lock"):
|
with FileLock(CIBW_CACHE_PATH / f"cpython{version}.lock"):
|
||||||
@@ -161,7 +164,7 @@ def install_cpython(_tmp: Path, version: str, url: str, free_threading: bool) ->
|
|||||||
python_filename = url.rsplit("/", maxsplit=1)[-1]
|
python_filename = url.rsplit("/", maxsplit=1)[-1]
|
||||||
pkg_path = CIBW_CACHE_PATH / "cpython-installer" / python_filename
|
pkg_path = CIBW_CACHE_PATH / "cpython-installer" / python_filename
|
||||||
if not pkg_path.exists():
|
if not pkg_path.exists():
|
||||||
download(url, pkg_path)
|
download(url, pkg_path, sha256=sha256 or None)
|
||||||
args = []
|
args = []
|
||||||
if version.startswith("3.14"):
|
if version.startswith("3.14"):
|
||||||
args += ["-applyChoiceChangesXML", str(resources.FREE_THREAD_ENABLE_314.resolve())]
|
args += ["-applyChoiceChangesXML", str(resources.FREE_THREAD_ENABLE_314.resolve())]
|
||||||
@@ -185,7 +188,7 @@ def install_cpython(_tmp: Path, version: str, url: str, free_threading: bool) ->
|
|||||||
return installation_path / "bin" / (f"python{version}t" if free_threading else "python3")
|
return installation_path / "bin" / (f"python{version}t" if free_threading else "python3")
|
||||||
|
|
||||||
|
|
||||||
def install_pypy(tmp: Path, url: str) -> Path:
|
def install_pypy(tmp: Path, url: str, sha256: str = "") -> Path:
|
||||||
pypy_tar_bz2 = url.rsplit("/", 1)[-1]
|
pypy_tar_bz2 = url.rsplit("/", 1)[-1]
|
||||||
extension = ".tar.bz2"
|
extension = ".tar.bz2"
|
||||||
assert pypy_tar_bz2.endswith(extension)
|
assert pypy_tar_bz2.endswith(extension)
|
||||||
@@ -193,14 +196,14 @@ def install_pypy(tmp: Path, url: str) -> Path:
|
|||||||
with FileLock(str(installation_path) + ".lock"):
|
with FileLock(str(installation_path) + ".lock"):
|
||||||
if not installation_path.exists():
|
if not installation_path.exists():
|
||||||
downloaded_tar_bz2 = tmp / pypy_tar_bz2
|
downloaded_tar_bz2 = tmp / pypy_tar_bz2
|
||||||
download(url, downloaded_tar_bz2)
|
download(url, downloaded_tar_bz2, sha256=sha256 or None)
|
||||||
installation_path.parent.mkdir(parents=True, exist_ok=True)
|
installation_path.parent.mkdir(parents=True, exist_ok=True)
|
||||||
call("tar", "-C", installation_path.parent, "-xf", downloaded_tar_bz2)
|
call("tar", "-C", installation_path.parent, "-xf", downloaded_tar_bz2)
|
||||||
downloaded_tar_bz2.unlink()
|
downloaded_tar_bz2.unlink()
|
||||||
return installation_path / "bin" / "pypy3"
|
return installation_path / "bin" / "pypy3"
|
||||||
|
|
||||||
|
|
||||||
def install_graalpy(tmp: Path, url: str) -> Path:
|
def install_graalpy(tmp: Path, url: str, sha256: str = "") -> Path:
|
||||||
graalpy_archive = url.rsplit("/", 1)[-1]
|
graalpy_archive = url.rsplit("/", 1)[-1]
|
||||||
extension = ".tar.gz"
|
extension = ".tar.gz"
|
||||||
assert graalpy_archive.endswith(extension)
|
assert graalpy_archive.endswith(extension)
|
||||||
@@ -208,7 +211,7 @@ def install_graalpy(tmp: Path, url: str) -> Path:
|
|||||||
with FileLock(str(installation_path) + ".lock"):
|
with FileLock(str(installation_path) + ".lock"):
|
||||||
if not installation_path.exists():
|
if not installation_path.exists():
|
||||||
downloaded_archive = tmp / graalpy_archive
|
downloaded_archive = tmp / graalpy_archive
|
||||||
download(url, downloaded_archive)
|
download(url, downloaded_archive, sha256=sha256 or None)
|
||||||
installation_path.mkdir(parents=True)
|
installation_path.mkdir(parents=True)
|
||||||
# GraalPy top-folder name is inconsistent with archive name
|
# GraalPy top-folder name is inconsistent with archive name
|
||||||
call("tar", "-C", installation_path, "--strip-components=1", "-xzf", downloaded_archive)
|
call("tar", "-C", installation_path, "--strip-components=1", "-xzf", downloaded_archive)
|
||||||
@@ -232,13 +235,17 @@ def setup_python(
|
|||||||
if implementation_id.startswith("cp"):
|
if implementation_id.startswith("cp"):
|
||||||
free_threading = "t-macos" in python_configuration.identifier
|
free_threading = "t-macos" in python_configuration.identifier
|
||||||
base_python = install_cpython(
|
base_python = install_cpython(
|
||||||
tmp, python_configuration.version, python_configuration.url, free_threading
|
tmp,
|
||||||
|
python_configuration.version,
|
||||||
|
python_configuration.url,
|
||||||
|
free_threading,
|
||||||
|
python_configuration.sha256,
|
||||||
)
|
)
|
||||||
|
|
||||||
elif implementation_id.startswith("pp"):
|
elif implementation_id.startswith("pp"):
|
||||||
base_python = install_pypy(tmp, python_configuration.url)
|
base_python = install_pypy(tmp, python_configuration.url, python_configuration.sha256)
|
||||||
elif implementation_id.startswith("gp"):
|
elif implementation_id.startswith("gp"):
|
||||||
base_python = install_graalpy(tmp, python_configuration.url)
|
base_python = install_graalpy(tmp, python_configuration.url, python_configuration.sha256)
|
||||||
else:
|
else:
|
||||||
msg = "Unknown Python implementation"
|
msg = "Unknown Python implementation"
|
||||||
raise ValueError(msg)
|
raise ValueError(msg)
|
||||||
|
|||||||
@@ -143,43 +143,43 @@ python_configurations = [
|
|||||||
|
|
||||||
[macos]
|
[macos]
|
||||||
python_configurations = [
|
python_configurations = [
|
||||||
{ identifier = "cp39-macosx_x86_64", version = "3.9", url = "https://www.python.org/ftp/python/3.9.13/python-3.9.13-macos11.pkg" },
|
{ identifier = "cp39-macosx_x86_64", version = "3.9", url = "https://www.python.org/ftp/python/3.9.13/python-3.9.13-macos11.pkg", sha256 = "" },
|
||||||
{ identifier = "cp39-macosx_arm64", version = "3.9", url = "https://www.python.org/ftp/python/3.9.13/python-3.9.13-macos11.pkg" },
|
{ identifier = "cp39-macosx_arm64", version = "3.9", url = "https://www.python.org/ftp/python/3.9.13/python-3.9.13-macos11.pkg", sha256 = "" },
|
||||||
{ identifier = "cp39-macosx_universal2", version = "3.9", url = "https://www.python.org/ftp/python/3.9.13/python-3.9.13-macos11.pkg" },
|
{ identifier = "cp39-macosx_universal2", version = "3.9", url = "https://www.python.org/ftp/python/3.9.13/python-3.9.13-macos11.pkg", sha256 = "" },
|
||||||
{ identifier = "cp310-macosx_x86_64", version = "3.10", url = "https://www.python.org/ftp/python/3.10.11/python-3.10.11-macos11.pkg" },
|
{ identifier = "cp310-macosx_x86_64", version = "3.10", url = "https://www.python.org/ftp/python/3.10.11/python-3.10.11-macos11.pkg", sha256 = "" },
|
||||||
{ identifier = "cp310-macosx_arm64", version = "3.10", url = "https://www.python.org/ftp/python/3.10.11/python-3.10.11-macos11.pkg" },
|
{ identifier = "cp310-macosx_arm64", version = "3.10", url = "https://www.python.org/ftp/python/3.10.11/python-3.10.11-macos11.pkg", sha256 = "" },
|
||||||
{ identifier = "cp310-macosx_universal2", version = "3.10", url = "https://www.python.org/ftp/python/3.10.11/python-3.10.11-macos11.pkg" },
|
{ identifier = "cp310-macosx_universal2", version = "3.10", url = "https://www.python.org/ftp/python/3.10.11/python-3.10.11-macos11.pkg", sha256 = "" },
|
||||||
{ identifier = "cp311-macosx_x86_64", version = "3.11", url = "https://www.python.org/ftp/python/3.11.9/python-3.11.9-macos11.pkg" },
|
{ identifier = "cp311-macosx_x86_64", version = "3.11", url = "https://www.python.org/ftp/python/3.11.9/python-3.11.9-macos11.pkg", sha256 = "" },
|
||||||
{ identifier = "cp311-macosx_arm64", version = "3.11", url = "https://www.python.org/ftp/python/3.11.9/python-3.11.9-macos11.pkg" },
|
{ identifier = "cp311-macosx_arm64", version = "3.11", url = "https://www.python.org/ftp/python/3.11.9/python-3.11.9-macos11.pkg", sha256 = "" },
|
||||||
{ identifier = "cp311-macosx_universal2", version = "3.11", url = "https://www.python.org/ftp/python/3.11.9/python-3.11.9-macos11.pkg" },
|
{ identifier = "cp311-macosx_universal2", version = "3.11", url = "https://www.python.org/ftp/python/3.11.9/python-3.11.9-macos11.pkg", sha256 = "" },
|
||||||
{ identifier = "cp312-macosx_x86_64", version = "3.12", url = "https://www.python.org/ftp/python/3.12.10/python-3.12.10-macos11.pkg" },
|
{ identifier = "cp312-macosx_x86_64", version = "3.12", url = "https://www.python.org/ftp/python/3.12.10/python-3.12.10-macos11.pkg", sha256 = "" },
|
||||||
{ identifier = "cp312-macosx_arm64", version = "3.12", url = "https://www.python.org/ftp/python/3.12.10/python-3.12.10-macos11.pkg" },
|
{ identifier = "cp312-macosx_arm64", version = "3.12", url = "https://www.python.org/ftp/python/3.12.10/python-3.12.10-macos11.pkg", sha256 = "" },
|
||||||
{ identifier = "cp312-macosx_universal2", version = "3.12", url = "https://www.python.org/ftp/python/3.12.10/python-3.12.10-macos11.pkg" },
|
{ identifier = "cp312-macosx_universal2", version = "3.12", url = "https://www.python.org/ftp/python/3.12.10/python-3.12.10-macos11.pkg", sha256 = "" },
|
||||||
{ identifier = "cp313-macosx_x86_64", version = "3.13", url = "https://www.python.org/ftp/python/3.13.13/python-3.13.13-macos11.pkg" },
|
{ identifier = "cp313-macosx_x86_64", version = "3.13", url = "https://www.python.org/ftp/python/3.13.13/python-3.13.13-macos11.pkg", sha256 = "" },
|
||||||
{ identifier = "cp313-macosx_arm64", version = "3.13", url = "https://www.python.org/ftp/python/3.13.13/python-3.13.13-macos11.pkg" },
|
{ identifier = "cp313-macosx_arm64", version = "3.13", url = "https://www.python.org/ftp/python/3.13.13/python-3.13.13-macos11.pkg", sha256 = "" },
|
||||||
{ identifier = "cp313-macosx_universal2", version = "3.13", url = "https://www.python.org/ftp/python/3.13.13/python-3.13.13-macos11.pkg" },
|
{ identifier = "cp313-macosx_universal2", version = "3.13", url = "https://www.python.org/ftp/python/3.13.13/python-3.13.13-macos11.pkg", sha256 = "" },
|
||||||
{ identifier = "cp314-macosx_x86_64", version = "3.14", url = "https://www.python.org/ftp/python/3.14.5/python-3.14.5-macos11.pkg" },
|
{ identifier = "cp314-macosx_x86_64", version = "3.14", url = "https://www.python.org/ftp/python/3.14.5/python-3.14.5-macos11.pkg", sha256 = "" },
|
||||||
{ identifier = "cp314-macosx_arm64", version = "3.14", url = "https://www.python.org/ftp/python/3.14.5/python-3.14.5-macos11.pkg" },
|
{ identifier = "cp314-macosx_arm64", version = "3.14", url = "https://www.python.org/ftp/python/3.14.5/python-3.14.5-macos11.pkg", sha256 = "" },
|
||||||
{ identifier = "cp314-macosx_universal2", version = "3.14", url = "https://www.python.org/ftp/python/3.14.5/python-3.14.5-macos11.pkg" },
|
{ identifier = "cp314-macosx_universal2", version = "3.14", url = "https://www.python.org/ftp/python/3.14.5/python-3.14.5-macos11.pkg", sha256 = "" },
|
||||||
{ identifier = "cp314t-macosx_x86_64", version = "3.14", url = "https://www.python.org/ftp/python/3.14.5/python-3.14.5-macos11.pkg" },
|
{ identifier = "cp314t-macosx_x86_64", version = "3.14", url = "https://www.python.org/ftp/python/3.14.5/python-3.14.5-macos11.pkg", sha256 = "" },
|
||||||
{ identifier = "cp314t-macosx_arm64", version = "3.14", url = "https://www.python.org/ftp/python/3.14.5/python-3.14.5-macos11.pkg" },
|
{ identifier = "cp314t-macosx_arm64", version = "3.14", url = "https://www.python.org/ftp/python/3.14.5/python-3.14.5-macos11.pkg", sha256 = "" },
|
||||||
{ identifier = "cp314t-macosx_universal2", version = "3.14", url = "https://www.python.org/ftp/python/3.14.5/python-3.14.5-macos11.pkg" },
|
{ identifier = "cp314t-macosx_universal2", version = "3.14", url = "https://www.python.org/ftp/python/3.14.5/python-3.14.5-macos11.pkg", sha256 = "" },
|
||||||
{ identifier = "cp315-macosx_x86_64", version = "3.15", url = "https://www.python.org/ftp/python/3.15.0/python-3.15.0b1-macos11.pkg" },
|
{ identifier = "cp315-macosx_x86_64", version = "3.15", url = "https://www.python.org/ftp/python/3.15.0/python-3.15.0b1-macos11.pkg", sha256 = "" },
|
||||||
{ identifier = "cp315-macosx_arm64", version = "3.15", url = "https://www.python.org/ftp/python/3.15.0/python-3.15.0b1-macos11.pkg" },
|
{ identifier = "cp315-macosx_arm64", version = "3.15", url = "https://www.python.org/ftp/python/3.15.0/python-3.15.0b1-macos11.pkg", sha256 = "" },
|
||||||
{ identifier = "cp315-macosx_universal2", version = "3.15", url = "https://www.python.org/ftp/python/3.15.0/python-3.15.0b1-macos11.pkg" },
|
{ identifier = "cp315-macosx_universal2", version = "3.15", url = "https://www.python.org/ftp/python/3.15.0/python-3.15.0b1-macos11.pkg", sha256 = "" },
|
||||||
{ identifier = "cp315t-macosx_x86_64", version = "3.15", url = "https://www.python.org/ftp/python/3.15.0/python-3.15.0b1-macos11.pkg" },
|
{ identifier = "cp315t-macosx_x86_64", version = "3.15", url = "https://www.python.org/ftp/python/3.15.0/python-3.15.0b1-macos11.pkg", sha256 = "" },
|
||||||
{ identifier = "cp315t-macosx_arm64", version = "3.15", url = "https://www.python.org/ftp/python/3.15.0/python-3.15.0b1-macos11.pkg" },
|
{ identifier = "cp315t-macosx_arm64", version = "3.15", url = "https://www.python.org/ftp/python/3.15.0/python-3.15.0b1-macos11.pkg", sha256 = "" },
|
||||||
{ identifier = "cp315t-macosx_universal2", version = "3.15", url = "https://www.python.org/ftp/python/3.15.0/python-3.15.0b1-macos11.pkg" },
|
{ identifier = "cp315t-macosx_universal2", version = "3.15", url = "https://www.python.org/ftp/python/3.15.0/python-3.15.0b1-macos11.pkg", sha256 = "" },
|
||||||
{ identifier = "pp39-macosx_x86_64", version = "3.9", url = "https://downloads.python.org/pypy/pypy3.9-v7.3.16-macos_x86_64.tar.bz2" },
|
{ identifier = "pp39-macosx_x86_64", version = "3.9", url = "https://downloads.python.org/pypy/pypy3.9-v7.3.16-macos_x86_64.tar.bz2", sha256 = "" },
|
||||||
{ identifier = "pp39-macosx_arm64", version = "3.9", url = "https://downloads.python.org/pypy/pypy3.9-v7.3.16-macos_arm64.tar.bz2" },
|
{ identifier = "pp39-macosx_arm64", version = "3.9", url = "https://downloads.python.org/pypy/pypy3.9-v7.3.16-macos_arm64.tar.bz2", sha256 = "" },
|
||||||
{ identifier = "pp310-macosx_x86_64", version = "3.10", url = "https://downloads.python.org/pypy/pypy3.10-v7.3.19-macos_x86_64.tar.bz2" },
|
{ identifier = "pp310-macosx_x86_64", version = "3.10", url = "https://downloads.python.org/pypy/pypy3.10-v7.3.19-macos_x86_64.tar.bz2", sha256 = "" },
|
||||||
{ identifier = "pp310-macosx_arm64", version = "3.10", url = "https://downloads.python.org/pypy/pypy3.10-v7.3.19-macos_arm64.tar.bz2" },
|
{ identifier = "pp310-macosx_arm64", version = "3.10", url = "https://downloads.python.org/pypy/pypy3.10-v7.3.19-macos_arm64.tar.bz2", sha256 = "" },
|
||||||
{ identifier = "pp311-macosx_x86_64", version = "3.11", url = "https://downloads.python.org/pypy/pypy3.11-v7.3.23-macos_x86_64.tar.bz2" },
|
{ identifier = "pp311-macosx_x86_64", version = "3.11", url = "https://downloads.python.org/pypy/pypy3.11-v7.3.23-macos_x86_64.tar.bz2", sha256 = "" },
|
||||||
{ identifier = "pp311-macosx_arm64", version = "3.11", url = "https://downloads.python.org/pypy/pypy3.11-v7.3.23-macos_arm64.tar.bz2" },
|
{ identifier = "pp311-macosx_arm64", version = "3.11", url = "https://downloads.python.org/pypy/pypy3.11-v7.3.23-macos_arm64.tar.bz2", sha256 = "" },
|
||||||
{ identifier = "gp311_242-macosx_x86_64", version = "3.11", url = "https://github.com/oracle/graalpython/releases/download/graal-24.2.2/graalpy-24.2.2-macos-amd64.tar.gz" },
|
{ identifier = "gp311_242-macosx_x86_64", version = "3.11", url = "https://github.com/oracle/graalpython/releases/download/graal-24.2.2/graalpy-24.2.2-macos-amd64.tar.gz", sha256 = "" },
|
||||||
{ identifier = "gp311_242-macosx_arm64", version = "3.11", url = "https://github.com/oracle/graalpython/releases/download/graal-24.2.2/graalpy-24.2.2-macos-aarch64.tar.gz" },
|
{ identifier = "gp311_242-macosx_arm64", version = "3.11", url = "https://github.com/oracle/graalpython/releases/download/graal-24.2.2/graalpy-24.2.2-macos-aarch64.tar.gz", sha256 = "" },
|
||||||
{ identifier = "gp312_250-macosx_x86_64", version = "3.12", url = "https://github.com/oracle/graalpython/releases/download/graal-25.0.1/graalpy-25.0.1-macos-amd64.tar.gz" },
|
{ identifier = "gp312_250-macosx_x86_64", version = "3.12", url = "https://github.com/oracle/graalpython/releases/download/graal-25.0.1/graalpy-25.0.1-macos-amd64.tar.gz", sha256 = "" },
|
||||||
{ identifier = "gp312_250-macosx_arm64", version = "3.12", url = "https://github.com/oracle/graalpython/releases/download/graal-25.0.3/graalpy-25.0.3-macos-aarch64.tar.gz" },
|
{ identifier = "gp312_250-macosx_arm64", version = "3.12", url = "https://github.com/oracle/graalpython/releases/download/graal-25.0.3/graalpy-25.0.3-macos-aarch64.tar.gz", sha256 = "" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[windows]
|
[windows]
|
||||||
@@ -227,23 +227,23 @@ python_configurations = [
|
|||||||
|
|
||||||
[android]
|
[android]
|
||||||
python_configurations = [
|
python_configurations = [
|
||||||
{ identifier = "cp313-android_arm64_v8a", version = "3.13", url = "https://repo.maven.apache.org/maven2/com/chaquo/python/python/3.13.13/python-3.13.13-aarch64-linux-android.tar.gz" },
|
{ identifier = "cp313-android_arm64_v8a", version = "3.13", url = "https://repo.maven.apache.org/maven2/com/chaquo/python/python/3.13.13/python-3.13.13-aarch64-linux-android.tar.gz", sha256 = "" },
|
||||||
{ identifier = "cp313-android_x86_64", version = "3.13", url = "https://repo.maven.apache.org/maven2/com/chaquo/python/python/3.13.13/python-3.13.13-x86_64-linux-android.tar.gz" },
|
{ identifier = "cp313-android_x86_64", version = "3.13", url = "https://repo.maven.apache.org/maven2/com/chaquo/python/python/3.13.13/python-3.13.13-x86_64-linux-android.tar.gz", sha256 = "" },
|
||||||
{ identifier = "cp314-android_arm64_v8a", version = "3.14", url = "https://www.python.org/ftp/python/3.14.5/python-3.14.5-aarch64-linux-android.tar.gz" },
|
{ identifier = "cp314-android_arm64_v8a", version = "3.14", url = "https://www.python.org/ftp/python/3.14.5/python-3.14.5-aarch64-linux-android.tar.gz", sha256 = "" },
|
||||||
{ identifier = "cp314-android_x86_64", version = "3.14", url = "https://www.python.org/ftp/python/3.14.5/python-3.14.5-x86_64-linux-android.tar.gz" },
|
{ identifier = "cp314-android_x86_64", version = "3.14", url = "https://www.python.org/ftp/python/3.14.5/python-3.14.5-x86_64-linux-android.tar.gz", sha256 = "" },
|
||||||
{ identifier = "cp315-android_arm64_v8a", version = "3.15", url = "https://www.python.org/ftp/python/3.15.0/python-3.15.0b1-aarch64-linux-android.tar.gz" },
|
{ identifier = "cp315-android_arm64_v8a", version = "3.15", url = "https://www.python.org/ftp/python/3.15.0/python-3.15.0b1-aarch64-linux-android.tar.gz", sha256 = "" },
|
||||||
{ identifier = "cp315-android_x86_64", version = "3.15", url = "https://www.python.org/ftp/python/3.15.0/python-3.15.0b1-x86_64-linux-android.tar.gz" },
|
{ identifier = "cp315-android_x86_64", version = "3.15", url = "https://www.python.org/ftp/python/3.15.0/python-3.15.0b1-x86_64-linux-android.tar.gz", sha256 = "" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[ios]
|
[ios]
|
||||||
python_configurations = [
|
python_configurations = [
|
||||||
{ identifier = "cp313-ios_arm64_iphoneos", version = "3.13", url = "https://github.com/beeware/Python-Apple-support/releases/download/3.13-b13/Python-3.13-iOS-support.b13.tar.gz" },
|
{ identifier = "cp313-ios_arm64_iphoneos", version = "3.13", url = "https://github.com/beeware/Python-Apple-support/releases/download/3.13-b13/Python-3.13-iOS-support.b13.tar.gz", sha256 = "" },
|
||||||
{ identifier = "cp313-ios_x86_64_iphonesimulator", version = "3.13", url = "https://github.com/beeware/Python-Apple-support/releases/download/3.13-b13/Python-3.13-iOS-support.b13.tar.gz" },
|
{ identifier = "cp313-ios_x86_64_iphonesimulator", version = "3.13", url = "https://github.com/beeware/Python-Apple-support/releases/download/3.13-b13/Python-3.13-iOS-support.b13.tar.gz", sha256 = "" },
|
||||||
{ identifier = "cp313-ios_arm64_iphonesimulator", version = "3.13", url = "https://github.com/beeware/Python-Apple-support/releases/download/3.13-b13/Python-3.13-iOS-support.b13.tar.gz" },
|
{ identifier = "cp313-ios_arm64_iphonesimulator", version = "3.13", url = "https://github.com/beeware/Python-Apple-support/releases/download/3.13-b13/Python-3.13-iOS-support.b13.tar.gz", sha256 = "" },
|
||||||
{ identifier = "cp314-ios_arm64_iphoneos", version = "3.14", url = "https://github.com/beeware/Python-Apple-support/releases/download/3.14-b9/Python-3.14-iOS-support.b9.tar.gz" },
|
{ identifier = "cp314-ios_arm64_iphoneos", version = "3.14", url = "https://github.com/beeware/Python-Apple-support/releases/download/3.14-b9/Python-3.14-iOS-support.b9.tar.gz", sha256 = "" },
|
||||||
{ identifier = "cp314-ios_x86_64_iphonesimulator", version = "3.14", url = "https://github.com/beeware/Python-Apple-support/releases/download/3.14-b9/Python-3.14-iOS-support.b9.tar.gz" },
|
{ identifier = "cp314-ios_x86_64_iphonesimulator", version = "3.14", url = "https://github.com/beeware/Python-Apple-support/releases/download/3.14-b9/Python-3.14-iOS-support.b9.tar.gz", sha256 = "" },
|
||||||
{ identifier = "cp314-ios_arm64_iphonesimulator", version = "3.14", url = "https://github.com/beeware/Python-Apple-support/releases/download/3.14-b9/Python-3.14-iOS-support.b9.tar.gz" },
|
{ identifier = "cp314-ios_arm64_iphonesimulator", version = "3.14", url = "https://github.com/beeware/Python-Apple-support/releases/download/3.14-b9/Python-3.14-iOS-support.b9.tar.gz", sha256 = "" },
|
||||||
{ identifier = "cp315-ios_arm64_iphoneos", version = "3.15", url = "https://www.python.org/ftp/python/3.15.0/python-3.15.0b1-iOS-XCframework.tar.gz" },
|
{ identifier = "cp315-ios_arm64_iphoneos", version = "3.15", url = "https://www.python.org/ftp/python/3.15.0/python-3.15.0b1-iOS-XCframework.tar.gz", sha256 = "" },
|
||||||
{ identifier = "cp315-ios_x86_64_iphonesimulator", version = "3.15", url = "https://www.python.org/ftp/python/3.15.0/python-3.15.0b1-iOS-XCframework.tar.gz" },
|
{ identifier = "cp315-ios_x86_64_iphonesimulator", version = "3.15", url = "https://www.python.org/ftp/python/3.15.0/python-3.15.0b1-iOS-XCframework.tar.gz", sha256 = "" },
|
||||||
{ identifier = "cp315-ios_arm64_iphonesimulator", version = "3.15", url = "https://www.python.org/ftp/python/3.15.0/python-3.15.0b1-iOS-XCframework.tar.gz" },
|
{ identifier = "cp315-ios_arm64_iphonesimulator", version = "3.15", url = "https://www.python.org/ftp/python/3.15.0/python-3.15.0b1-iOS-XCframework.tar.gz", sha256 = "" },
|
||||||
]
|
]
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1 +1 @@
|
|||||||
default = { version = "21.3.3", url = "https://github.com/pypa/get-virtualenv/releases/download/21.3.3/virtualenv.pyz" }
|
default = { version = "21.3.3", url = "https://github.com/pypa/get-virtualenv/releases/download/21.3.3/virtualenv.pyz", sha256 = "" }
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import hashlib
|
||||||
import os
|
import os
|
||||||
import shutil
|
import shutil
|
||||||
import ssl
|
import ssl
|
||||||
@@ -45,7 +46,7 @@ def ensure_cache_sentinel(cache_path: Path) -> None:
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def download(url: str, dest: Path) -> None:
|
def download(url: str, dest: Path, *, sha256: str | None = None) -> None:
|
||||||
print(f"+ Download {url} to {dest}")
|
print(f"+ Download {url} to {dest}")
|
||||||
dest_dir = dest.parent
|
dest_dir = dest.parent
|
||||||
dest_dir.mkdir(parents=True, exist_ok=True)
|
dest_dir.mkdir(parents=True, exist_ok=True)
|
||||||
@@ -59,13 +60,20 @@ def download(url: str, dest: Path) -> None:
|
|||||||
try:
|
try:
|
||||||
with urllib.request.urlopen(url, context=context) as response:
|
with urllib.request.urlopen(url, context=context) as response:
|
||||||
dest.write_bytes(response.read())
|
dest.write_bytes(response.read())
|
||||||
return
|
break
|
||||||
|
|
||||||
except OSError:
|
except OSError:
|
||||||
if i == repeat_num - 1:
|
if i == repeat_num - 1:
|
||||||
raise
|
raise
|
||||||
time.sleep(3)
|
time.sleep(3)
|
||||||
|
|
||||||
|
if sha256:
|
||||||
|
computed = hashlib.sha256(dest.read_bytes()).hexdigest()
|
||||||
|
if computed != sha256:
|
||||||
|
dest.unlink(missing_ok=True)
|
||||||
|
msg = f"SHA256 mismatch for {url}: expected {sha256!r}, got {computed!r}"
|
||||||
|
raise FatalError(msg)
|
||||||
|
|
||||||
|
|
||||||
def extract_zip(zip_src: Path, dest: Path) -> None:
|
def extract_zip(zip_src: Path, dest: Path) -> None:
|
||||||
"""Extracts a zip and correctly sets permissions on extracted files.
|
"""Extracts a zip and correctly sets permissions on extracted files.
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ if TYPE_CHECKING:
|
|||||||
class PythonBuildStandaloneAsset(typing.TypedDict):
|
class PythonBuildStandaloneAsset(typing.TypedDict):
|
||||||
name: str
|
name: str
|
||||||
url: str
|
url: str
|
||||||
|
sha256: str
|
||||||
|
|
||||||
|
|
||||||
class PythonBuildStandaloneRelease(typing.TypedDict):
|
class PythonBuildStandaloneRelease(typing.TypedDict):
|
||||||
@@ -86,8 +87,8 @@ def _get_pbs_asset(
|
|||||||
arch_identifier: str,
|
arch_identifier: str,
|
||||||
platform_identifier: str,
|
platform_identifier: str,
|
||||||
libc_identifier: str | None,
|
libc_identifier: str | None,
|
||||||
) -> tuple[str, str, str]:
|
) -> tuple[str, str, str, str]:
|
||||||
"""Finds the asset, returning (tag, filename, url)."""
|
"""Finds the asset, returning (tag, url, filename, sha256)."""
|
||||||
release_data = get_python_build_standalone_release_data()
|
release_data = get_python_build_standalone_release_data()
|
||||||
|
|
||||||
expected_suffix = f"{arch_identifier}-{platform_identifier}"
|
expected_suffix = f"{arch_identifier}-{platform_identifier}"
|
||||||
@@ -105,14 +106,17 @@ def _get_pbs_asset(
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
asset_url = asset["url"]
|
asset_url = asset["url"]
|
||||||
return release["tag"], asset_url, asset_name
|
asset_sha256 = asset.get("sha256", "")
|
||||||
|
return release["tag"], asset_url, asset_name, asset_sha256
|
||||||
|
|
||||||
# If loop completes without finding a match
|
# If loop completes without finding a match
|
||||||
msg = f"Could not find python-build-standalone release asset matching {asset_pattern!r}."
|
msg = f"Could not find python-build-standalone release asset matching {asset_pattern!r}."
|
||||||
raise PythonBuildStandaloneError(msg)
|
raise PythonBuildStandaloneError(msg)
|
||||||
|
|
||||||
|
|
||||||
def _download_or_get_from_cache(asset_url: str, asset_filename: str, cache_dir: Path) -> Path:
|
def _download_or_get_from_cache(
|
||||||
|
asset_url: str, asset_filename: str, cache_dir: Path, sha256: str = ""
|
||||||
|
) -> Path:
|
||||||
with FileLock(cache_dir / (asset_filename + ".lock")):
|
with FileLock(cache_dir / (asset_filename + ".lock")):
|
||||||
asset_cache_path = cache_dir / asset_filename
|
asset_cache_path = cache_dir / asset_filename
|
||||||
if asset_cache_path.is_file():
|
if asset_cache_path.is_file():
|
||||||
@@ -120,7 +124,7 @@ def _download_or_get_from_cache(asset_url: str, asset_filename: str, cache_dir:
|
|||||||
return asset_cache_path
|
return asset_cache_path
|
||||||
|
|
||||||
print(f"Downloading python_build_standalone: {asset_url} to {asset_cache_path}")
|
print(f"Downloading python_build_standalone: {asset_url} to {asset_cache_path}")
|
||||||
download(asset_url, asset_cache_path)
|
download(asset_url, asset_cache_path, sha256=sha256 or None)
|
||||||
return asset_cache_path
|
return asset_cache_path
|
||||||
|
|
||||||
|
|
||||||
@@ -166,7 +170,7 @@ def create_python_build_standalone_environment(
|
|||||||
|
|
||||||
arch_id, platform_id, libc_id = _get_platform_identifiers()
|
arch_id, platform_id, libc_id = _get_platform_identifiers()
|
||||||
|
|
||||||
pbs_tag, asset_url, asset_filename = _get_pbs_asset(
|
pbs_tag, asset_url, asset_filename, asset_sha256 = _get_pbs_asset(
|
||||||
python_version=python_version,
|
python_version=python_version,
|
||||||
arch_identifier=arch_id,
|
arch_identifier=arch_id,
|
||||||
platform_identifier=platform_id,
|
platform_identifier=platform_id,
|
||||||
@@ -176,7 +180,7 @@ def create_python_build_standalone_environment(
|
|||||||
print(f"Using python-build-standalone release: {pbs_tag}")
|
print(f"Using python-build-standalone release: {pbs_tag}")
|
||||||
|
|
||||||
archive_path = _download_or_get_from_cache(
|
archive_path = _download_or_get_from_cache(
|
||||||
asset_url=asset_url, asset_filename=asset_filename, cache_dir=cache_dir
|
asset_url=asset_url, asset_filename=asset_filename, cache_dir=cache_dir, sha256=asset_sha256
|
||||||
)
|
)
|
||||||
|
|
||||||
python_base_dir = temp_dir / "pbs"
|
python_base_dir = temp_dir / "pbs"
|
||||||
|
|||||||
@@ -53,10 +53,11 @@ def _ensure_virtualenv(version: str) -> tuple[Path, Version]:
|
|||||||
configuration = loaded_file.get(key, loaded_file["default"])
|
configuration = loaded_file.get(key, loaded_file["default"])
|
||||||
version = str(configuration["version"])
|
version = str(configuration["version"])
|
||||||
url = str(configuration["url"])
|
url = str(configuration["url"])
|
||||||
|
sha256 = str(configuration.get("sha256", ""))
|
||||||
path = CIBW_CACHE_PATH / f"virtualenv-{version}.pyz"
|
path = CIBW_CACHE_PATH / f"virtualenv-{version}.pyz"
|
||||||
with FileLock(str(path) + ".lock"):
|
with FileLock(str(path) + ".lock"):
|
||||||
if not path.exists():
|
if not path.exists():
|
||||||
download(url, path)
|
download(url, path, sha256=sha256 or None)
|
||||||
return (path, Version(version))
|
return (path, Version(version))
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user