pbs-installer 2024.3.27__py3-none-any.whl → 2024.4.24__py3-none-any.whl
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/__main__.py +2 -2
- pbs_installer/_install.py +17 -8
- pbs_installer/_utils.py +12 -23
- pbs_installer/_versions.py +1810 -456
- pbs_installer/py.typed +0 -0
- {pbs_installer-2024.3.27.dist-info → pbs_installer-2024.4.24.dist-info}/METADATA +3 -1
- pbs_installer-2024.4.24.dist-info/RECORD +11 -0
- {pbs_installer-2024.3.27.dist-info → pbs_installer-2024.4.24.dist-info}/WHEEL +1 -1
- pbs_installer-2024.3.27.dist-info/RECORD +0 -10
- {pbs_installer-2024.3.27.dist-info → pbs_installer-2024.4.24.dist-info}/entry_points.txt +0 -0
- {pbs_installer-2024.3.27.dist-info → pbs_installer-2024.4.24.dist-info}/licenses/LICENSE +0 -0
pbs_installer/__main__.py
CHANGED
|
@@ -9,7 +9,7 @@ from ._install import install
|
|
|
9
9
|
from ._utils import get_available_arch_platforms
|
|
10
10
|
|
|
11
11
|
|
|
12
|
-
def _setup_logger(verbose: bool):
|
|
12
|
+
def _setup_logger(verbose: bool) -> None:
|
|
13
13
|
logger = logging.getLogger("pbs_installer")
|
|
14
14
|
handler = logging.StreamHandler()
|
|
15
15
|
handler.setFormatter(logging.Formatter("%(levelname)s: %(message)s"))
|
|
@@ -46,7 +46,7 @@ class ListAction(Action):
|
|
|
46
46
|
print(f"- {version}")
|
|
47
47
|
|
|
48
48
|
|
|
49
|
-
def main():
|
|
49
|
+
def main() -> None:
|
|
50
50
|
archs, platforms = get_available_arch_platforms()
|
|
51
51
|
parser = ArgumentParser("pbs-install", description="Installer for Python Build Standalone")
|
|
52
52
|
install_group = parser.add_argument_group("Install Arguments")
|
pbs_installer/_install.py
CHANGED
|
@@ -4,7 +4,7 @@ import hashlib
|
|
|
4
4
|
import logging
|
|
5
5
|
import os
|
|
6
6
|
import tempfile
|
|
7
|
-
from typing import TYPE_CHECKING, Optional, Tuple
|
|
7
|
+
from typing import TYPE_CHECKING, Optional, Tuple, cast
|
|
8
8
|
from urllib.parse import unquote
|
|
9
9
|
|
|
10
10
|
from ._utils import PythonVersion, get_arch_platform
|
|
@@ -13,6 +13,10 @@ if TYPE_CHECKING:
|
|
|
13
13
|
import httpx
|
|
14
14
|
from _typeshed import StrPath
|
|
15
15
|
|
|
16
|
+
from typing import Literal
|
|
17
|
+
|
|
18
|
+
PythonImplementation = Literal["cpython", "pypy"]
|
|
19
|
+
|
|
16
20
|
logger = logging.getLogger(__name__)
|
|
17
21
|
THIS_ARCH, THIS_PLATFORM = get_arch_platform()
|
|
18
22
|
PythonFile = Tuple[str, Optional[str]]
|
|
@@ -32,7 +36,8 @@ def get_download_link(
|
|
|
32
36
|
request: str,
|
|
33
37
|
arch: str = THIS_ARCH,
|
|
34
38
|
platform: str = THIS_PLATFORM,
|
|
35
|
-
implementation:
|
|
39
|
+
implementation: PythonImplementation = "cpython",
|
|
40
|
+
build_dir: bool = False,
|
|
36
41
|
) -> tuple[PythonVersion, PythonFile]:
|
|
37
42
|
"""Get the download URL matching the given requested version.
|
|
38
43
|
|
|
@@ -41,6 +46,7 @@ def get_download_link(
|
|
|
41
46
|
arch: The architecture to install, e.g. x86_64, arm64
|
|
42
47
|
platform: The platform to install, e.g. linux, macos
|
|
43
48
|
implementation: The implementation of Python to install, allowed values are 'cpython' and 'pypy'
|
|
49
|
+
build_dir: Whether to include the `build/` directory from indygreg builds
|
|
44
50
|
|
|
45
51
|
Returns:
|
|
46
52
|
A tuple of the PythonVersion and the download URL
|
|
@@ -56,9 +62,11 @@ def get_download_link(
|
|
|
56
62
|
if not py_ver.matches(request, implementation):
|
|
57
63
|
continue
|
|
58
64
|
|
|
59
|
-
matched = urls.get((platform, arch))
|
|
65
|
+
matched = urls.get((platform, arch, not build_dir))
|
|
60
66
|
if matched is not None:
|
|
61
67
|
return py_ver, matched
|
|
68
|
+
if build_dir and (matched := urls.get((platform, arch, False))) is not None:
|
|
69
|
+
return py_ver, matched
|
|
62
70
|
raise ValueError(
|
|
63
71
|
f"Could not find a version matching version={request!r}, implementation={implementation}"
|
|
64
72
|
)
|
|
@@ -136,12 +144,13 @@ def install_file(
|
|
|
136
144
|
destination,
|
|
137
145
|
original_filename,
|
|
138
146
|
)
|
|
147
|
+
filename = cast(str, filename)
|
|
139
148
|
if original_filename.endswith(".zst"):
|
|
140
|
-
unpack_zst(filename, destination
|
|
149
|
+
unpack_zst(filename, destination)
|
|
141
150
|
elif original_filename.endswith(".zip"):
|
|
142
|
-
unpack_zip(filename, destination
|
|
151
|
+
unpack_zip(filename, destination)
|
|
143
152
|
else:
|
|
144
|
-
unpack_tar(filename, destination
|
|
153
|
+
unpack_tar(filename, destination)
|
|
145
154
|
|
|
146
155
|
|
|
147
156
|
def install(
|
|
@@ -151,7 +160,7 @@ def install(
|
|
|
151
160
|
client: httpx.Client | None = None,
|
|
152
161
|
arch: str | None = None,
|
|
153
162
|
platform: str | None = None,
|
|
154
|
-
implementation:
|
|
163
|
+
implementation: PythonImplementation = "cpython",
|
|
155
164
|
build_dir: bool = False,
|
|
156
165
|
) -> None:
|
|
157
166
|
"""Download and install the requested python version.
|
|
@@ -181,7 +190,7 @@ def install(
|
|
|
181
190
|
arch = THIS_ARCH
|
|
182
191
|
|
|
183
192
|
ver, python_file = get_download_link(
|
|
184
|
-
request, arch=arch, platform=platform, implementation=implementation
|
|
193
|
+
request, arch=arch, platform=platform, implementation=implementation, build_dir=build_dir
|
|
185
194
|
)
|
|
186
195
|
if version_dir:
|
|
187
196
|
destination = os.path.join(destination, str(ver))
|
pbs_installer/_utils.py
CHANGED
|
@@ -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
|
|