pbs-installer 2024.3.26__tar.gz → 2024.4.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {pbs_installer-2024.3.26 → pbs_installer-2024.4.1}/PKG-INFO +3 -1
- {pbs_installer-2024.3.26 → pbs_installer-2024.4.1}/pyproject.toml +5 -1
- {pbs_installer-2024.3.26 → pbs_installer-2024.4.1}/src/pbs_installer/_install.py +16 -14
- {pbs_installer-2024.3.26 → pbs_installer-2024.4.1}/src/pbs_installer/_utils.py +12 -23
- {pbs_installer-2024.3.26 → pbs_installer-2024.4.1}/src/pbs_installer/_versions.py +1507 -455
- {pbs_installer-2024.3.26 → pbs_installer-2024.4.1}/LICENSE +0 -0
- {pbs_installer-2024.3.26 → pbs_installer-2024.4.1}/README.md +0 -0
- {pbs_installer-2024.3.26 → pbs_installer-2024.4.1}/src/pbs_installer/__init__.py +0 -0
- {pbs_installer-2024.3.26 → pbs_installer-2024.4.1}/src/pbs_installer/__main__.py +0 -0
- {pbs_installer-2024.3.26 → pbs_installer-2024.4.1}/tests/__init__.py +0 -0
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: pbs-installer
|
|
3
|
-
Version: 2024.
|
|
3
|
+
Version: 2024.4.1
|
|
4
4
|
Summary: Installer for Python Build Standalone
|
|
5
5
|
Author-Email: Frost Ming <me@frostming.com>
|
|
6
6
|
License: MIT
|
|
7
|
+
Project-URL: Repository, https://github.com/frostming/pbs-installer
|
|
8
|
+
Project-URL: Documentation, http://pbs-installer.readthedocs.io
|
|
7
9
|
Requires-Python: >=3.8
|
|
8
10
|
Provides-Extra: download
|
|
9
11
|
Provides-Extra: install
|
|
@@ -7,11 +7,15 @@ authors = [
|
|
|
7
7
|
requires-python = ">=3.8"
|
|
8
8
|
readme = "README.md"
|
|
9
9
|
dynamic = []
|
|
10
|
-
version = "2024.
|
|
10
|
+
version = "2024.4.1"
|
|
11
11
|
|
|
12
12
|
[project.license]
|
|
13
13
|
text = "MIT"
|
|
14
14
|
|
|
15
|
+
[project.urls]
|
|
16
|
+
Repository = "https://github.com/frostming/pbs-installer"
|
|
17
|
+
Documentation = "http://pbs-installer.readthedocs.io"
|
|
18
|
+
|
|
15
19
|
[project.optional-dependencies]
|
|
16
20
|
download = [
|
|
17
21
|
"httpx<1,>=0.27.0",
|
|
@@ -33,6 +33,7 @@ def get_download_link(
|
|
|
33
33
|
arch: str = THIS_ARCH,
|
|
34
34
|
platform: str = THIS_PLATFORM,
|
|
35
35
|
implementation: str = "cpython",
|
|
36
|
+
build_dir: bool = False,
|
|
36
37
|
) -> tuple[PythonVersion, PythonFile]:
|
|
37
38
|
"""Get the download URL matching the given requested version.
|
|
38
39
|
|
|
@@ -41,6 +42,7 @@ def get_download_link(
|
|
|
41
42
|
arch: The architecture to install, e.g. x86_64, arm64
|
|
42
43
|
platform: The platform to install, e.g. linux, macos
|
|
43
44
|
implementation: The implementation of Python to install, allowed values are 'cpython' and 'pypy'
|
|
45
|
+
build_dir: Whether to include the `build/` directory from indygreg builds
|
|
44
46
|
|
|
45
47
|
Returns:
|
|
46
48
|
A tuple of the PythonVersion and the download URL
|
|
@@ -56,9 +58,11 @@ def get_download_link(
|
|
|
56
58
|
if not py_ver.matches(request, implementation):
|
|
57
59
|
continue
|
|
58
60
|
|
|
59
|
-
matched = urls.get((platform, arch))
|
|
61
|
+
matched = urls.get((platform, arch, not build_dir))
|
|
60
62
|
if matched is not None:
|
|
61
63
|
return py_ver, matched
|
|
64
|
+
if build_dir and (matched := urls.get((platform, arch, False))) is not None:
|
|
65
|
+
return py_ver, matched
|
|
62
66
|
raise ValueError(
|
|
63
67
|
f"Could not find a version matching version={request!r}, implementation={implementation}"
|
|
64
68
|
)
|
|
@@ -95,15 +99,13 @@ def download(
|
|
|
95
99
|
if not checksum:
|
|
96
100
|
logger.warning("No checksum found for %s, this would be insecure", url)
|
|
97
101
|
|
|
98
|
-
with (
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
hasher.update(chunk)
|
|
106
|
-
f.write(chunk)
|
|
102
|
+
with open(destination, "wb") as f:
|
|
103
|
+
with client.stream("GET", url, headers=_get_headers()) as resp:
|
|
104
|
+
resp.raise_for_status()
|
|
105
|
+
for chunk in resp.iter_bytes(chunk_size=8192):
|
|
106
|
+
if checksum:
|
|
107
|
+
hasher.update(chunk)
|
|
108
|
+
f.write(chunk)
|
|
107
109
|
|
|
108
110
|
if checksum and hasher.hexdigest() != checksum:
|
|
109
111
|
raise RuntimeError(f"Checksum mismatch. Expected {checksum}, got {hasher.hexdigest()}")
|
|
@@ -139,11 +141,11 @@ def install_file(
|
|
|
139
141
|
original_filename,
|
|
140
142
|
)
|
|
141
143
|
if original_filename.endswith(".zst"):
|
|
142
|
-
unpack_zst(filename, destination
|
|
144
|
+
unpack_zst(filename, destination)
|
|
143
145
|
elif original_filename.endswith(".zip"):
|
|
144
|
-
unpack_zip(filename, destination
|
|
146
|
+
unpack_zip(filename, destination)
|
|
145
147
|
else:
|
|
146
|
-
unpack_tar(filename, destination
|
|
148
|
+
unpack_tar(filename, destination)
|
|
147
149
|
|
|
148
150
|
|
|
149
151
|
def install(
|
|
@@ -183,7 +185,7 @@ def install(
|
|
|
183
185
|
arch = THIS_ARCH
|
|
184
186
|
|
|
185
187
|
ver, python_file = get_download_link(
|
|
186
|
-
request, arch=arch, platform=platform, implementation=implementation
|
|
188
|
+
request, arch=arch, platform=platform, implementation=implementation, build_dir=build_dir
|
|
187
189
|
)
|
|
188
190
|
if version_dir:
|
|
189
191
|
destination = os.path.join(destination, str(ver))
|
|
@@ -53,36 +53,31 @@ def get_arch_platform() -> tuple[str, str]:
|
|
|
53
53
|
return ARCH_MAPPING.get(arch, arch), PLATFORM_MAPPING.get(plat, plat)
|
|
54
54
|
|
|
55
55
|
|
|
56
|
-
def _unpack_tar(tf: tarfile.TarFile, destination: StrPath
|
|
56
|
+
def _unpack_tar(tf: tarfile.TarFile, destination: StrPath) -> None:
|
|
57
57
|
"""Unpack the tarfile to the destination, with the first skip_parts parts of the path removed"""
|
|
58
58
|
members: list[tarfile.TarInfo] = []
|
|
59
|
-
has_build = any(
|
|
60
|
-
(p := fn.lstrip("/").split("/")) and len(p) > 1 and p[1] == "build" for fn in tf.getnames()
|
|
61
|
-
)
|
|
62
59
|
for member in tf.getmembers():
|
|
63
60
|
parts = member.name.lstrip("/").split("/")
|
|
64
|
-
|
|
65
|
-
member.name = "/".join(parts[1:])
|
|
66
|
-
elif len(parts) > 1 and parts[1] == "install":
|
|
67
|
-
member.name = "/".join(parts[2:])
|
|
68
|
-
else:
|
|
69
|
-
continue
|
|
61
|
+
member.name = "/".join(parts[1:])
|
|
70
62
|
if member.name:
|
|
71
63
|
members.append(member)
|
|
72
64
|
tf.extractall(destination, members=members)
|
|
73
65
|
|
|
74
66
|
|
|
75
|
-
def unpack_tar(filename: str, destination: StrPath
|
|
67
|
+
def unpack_tar(filename: str, destination: StrPath) -> None:
|
|
76
68
|
"""Unpack the tarfile to the destination"""
|
|
77
69
|
with tarfile.open(filename) as z:
|
|
78
|
-
_unpack_tar(z, destination
|
|
70
|
+
_unpack_tar(z, destination)
|
|
79
71
|
|
|
80
72
|
|
|
81
|
-
def unpack_zst(filename: str, destination: StrPath
|
|
73
|
+
def unpack_zst(filename: str, destination: StrPath) -> None:
|
|
82
74
|
"""Unpack the zstd compressed tarfile to the destination"""
|
|
83
75
|
import tempfile
|
|
84
76
|
|
|
85
|
-
|
|
77
|
+
try:
|
|
78
|
+
import zstandard as zstd
|
|
79
|
+
except ModuleNotFoundError:
|
|
80
|
+
raise ModuleNotFoundError("zstandard is required to unpack .zst files") from None
|
|
86
81
|
|
|
87
82
|
dctx = zstd.ZstdDecompressor()
|
|
88
83
|
with tempfile.TemporaryFile(suffix=".tar") as ofh:
|
|
@@ -90,24 +85,18 @@ def unpack_zst(filename: str, destination: StrPath, build_dir: bool = False) ->
|
|
|
90
85
|
dctx.copy_stream(ifh, ofh)
|
|
91
86
|
ofh.seek(0)
|
|
92
87
|
with tarfile.open(fileobj=ofh) as z:
|
|
93
|
-
_unpack_tar(z, destination
|
|
88
|
+
_unpack_tar(z, destination)
|
|
94
89
|
|
|
95
90
|
|
|
96
|
-
def unpack_zip(filename: str, destination: StrPath
|
|
91
|
+
def unpack_zip(filename: str, destination: StrPath) -> None:
|
|
97
92
|
"""Unpack the zip file to the destination"""
|
|
98
93
|
import zipfile
|
|
99
94
|
|
|
100
95
|
with zipfile.ZipFile(filename) as z:
|
|
101
96
|
members: list[zipfile.ZipInfo] = []
|
|
102
|
-
has_build = any(fn.lstrip("/").split("/")[1] == "build" for fn in z.namelist())
|
|
103
97
|
for member in z.infolist():
|
|
104
98
|
parts = member.filename.lstrip("/").split("/")
|
|
105
|
-
|
|
106
|
-
member.filename = "/".join(parts[1:])
|
|
107
|
-
elif len(parts) > 1 and parts[1] == "install":
|
|
108
|
-
member.filename = "/".join(parts[2:])
|
|
109
|
-
else:
|
|
110
|
-
continue
|
|
99
|
+
member.filename = "/".join(parts[1:])
|
|
111
100
|
if member.filename:
|
|
112
101
|
members.append(member)
|
|
113
102
|
|