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 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: str = "cpython",
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, build_dir)
149
+ unpack_zst(filename, destination)
141
150
  elif original_filename.endswith(".zip"):
142
- unpack_zip(filename, destination, build_dir)
151
+ unpack_zip(filename, destination)
143
152
  else:
144
- unpack_tar(filename, destination, build_dir)
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: str = "cpython",
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, build_dir: bool = False) -> None:
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
- if build_dir or not has_build:
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, build_dir: bool = False) -> None:
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, build_dir=build_dir)
70
+ _unpack_tar(z, destination)
79
71
 
80
72
 
81
- def unpack_zst(filename: str, destination: StrPath, build_dir: bool = False) -> None:
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
- import zstandard as zstd
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, build_dir=build_dir)
88
+ _unpack_tar(z, destination)
94
89
 
95
90
 
96
- def unpack_zip(filename: str, destination: StrPath, build_dir: bool = False) -> None:
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
- if (build_dir or not has_build) and len(parts) > 1:
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