pystand 2.15__tar.gz → 2.17__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.
- {pystand-2.15 → pystand-2.17}/Makefile +1 -0
- {pystand-2.15 → pystand-2.17}/PKG-INFO +32 -9
- pystand-2.15/pystand.egg-info/PKG-INFO → pystand-2.17/README.md +29 -24
- {pystand-2.15 → pystand-2.17}/pyproject.toml +2 -1
- pystand-2.15/README.md → pystand-2.17/pystand.egg-info/PKG-INFO +47 -7
- {pystand-2.15 → pystand-2.17}/pystand.egg-info/requires.txt +3 -0
- {pystand-2.15 → pystand-2.17}/pystand.py +123 -32
- {pystand-2.15 → pystand-2.17}/.gitignore +0 -0
- {pystand-2.15 → pystand-2.17}/pystand.egg-info/SOURCES.txt +0 -0
- {pystand-2.15 → pystand-2.17}/pystand.egg-info/dependency_links.txt +0 -0
- {pystand-2.15 → pystand-2.17}/pystand.egg-info/entry_points.txt +0 -0
- {pystand-2.15 → pystand-2.17}/pystand.egg-info/top_level.txt +0 -0
- {pystand-2.15 → pystand-2.17}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: pystand
|
3
|
-
Version: 2.
|
3
|
+
Version: 2.17
|
4
4
|
Summary: Install Python versions from python-build-standalone project
|
5
5
|
Author-email: Mark Blakeney <mark.blakeney@bullet-systems.net>
|
6
6
|
License-Expression: GPL-3.0-or-later
|
@@ -13,7 +13,8 @@ Requires-Dist: argcomplete
|
|
13
13
|
Requires-Dist: packaging
|
14
14
|
Requires-Dist: platformdirs
|
15
15
|
Requires-Dist: pygithub
|
16
|
-
Requires-Dist:
|
16
|
+
Requires-Dist: certifi
|
17
|
+
Requires-Dist: zstandard; python_version < "3.14"
|
17
18
|
|
18
19
|
## PYSTAND - Install Python Versions From The Python-Build-Standalone Project
|
19
20
|
[](https://pypi.org/project/pystand/)
|
@@ -41,7 +42,7 @@ required
|
|
41
42
|
for your machine architecture is normally auto-detected. By default, the
|
42
43
|
_`install_only_stripped`_ build of the distribution is installed but you
|
43
44
|
can choose to [install any other
|
44
|
-
build/distribution](#installing-other-
|
45
|
+
build/distribution](#installing-other-buildsdistributions) instead, or
|
45
46
|
in parallel.
|
46
47
|
|
47
48
|
Some simple usage examples are:
|
@@ -130,8 +131,8 @@ Type `pystand` or `pystand -h` to view the usage summary:
|
|
130
131
|
usage: pystand [-h] [-D DISTRIBUTION] [-P PREFIX_DIR] [-C CACHE_DIR]
|
131
132
|
[-M CACHE_MINUTES] [--purge-days PURGE_DAYS]
|
132
133
|
[--github-access-token GITHUB_ACCESS_TOKEN] [--no-strip]
|
133
|
-
[-V]
|
134
|
-
{install,update,upgrade,remove,uninstall,list,show,path} ...
|
134
|
+
[--cert {system,certifi,none}] [-V]
|
135
|
+
{install,update,upgrade,remove,uninstall,list,show,path,cache} ...
|
135
136
|
|
136
137
|
Command line tool to download, install, and update pre-built Python versions
|
137
138
|
from the python-build-standalone project at https://github.com/astral-
|
@@ -141,8 +142,9 @@ options:
|
|
141
142
|
-h, --help show this help message and exit
|
142
143
|
-D, --distribution DISTRIBUTION
|
143
144
|
python-build-standalone distribution. Default is
|
144
|
-
"x86_64_v3-unknown-linux-gnu-install_only_stripped
|
145
|
-
this host
|
145
|
+
"x86_64_v3-unknown-linux-gnu-install_only_stripped"
|
146
|
+
for this host. Run "pystand show -a" to see all
|
147
|
+
distributions.
|
146
148
|
-P, --prefix-dir PREFIX_DIR
|
147
149
|
specify prefix dir for storing versions. Default is
|
148
150
|
"$HOME/.local/share/pystand"
|
@@ -161,10 +163,13 @@ options:
|
|
161
163
|
optional Github access token. Can specify to reduce
|
162
164
|
rate limiting.
|
163
165
|
--no-strip do not strip downloaded binaries
|
166
|
+
--cert {system,certifi,none}
|
167
|
+
specify which SSL certificates to use for HTTPS
|
168
|
+
requests. Default="system"
|
164
169
|
-V, --version just show pystand version
|
165
170
|
|
166
171
|
Commands:
|
167
|
-
{install,update,upgrade,remove,uninstall,list,show,path}
|
172
|
+
{install,update,upgrade,remove,uninstall,list,show,path,cache}
|
168
173
|
install Install one or more versions from a python-build-
|
169
174
|
standalone release.
|
170
175
|
update (upgrade) Update one, more, or all versions to another release.
|
@@ -173,6 +178,7 @@ Commands:
|
|
173
178
|
available.
|
174
179
|
show Show versions available from a release.
|
175
180
|
path Show path prefix to installed version base directory.
|
181
|
+
cache Show release cache sizes.
|
176
182
|
|
177
183
|
Some commands offer aliases as shown in parentheses above. Note you can set
|
178
184
|
default starting global options in $HOME/.config/pystand-flags.conf.
|
@@ -307,6 +313,23 @@ options:
|
|
307
313
|
-c, --cache-path just show path to cache dir
|
308
314
|
```
|
309
315
|
|
316
|
+
### Command `cache`
|
317
|
+
|
318
|
+
```
|
319
|
+
usage: pystand cache [-h] [-T] [-H] [release ...]
|
320
|
+
|
321
|
+
Show release cache sizes.
|
322
|
+
|
323
|
+
positional arguments:
|
324
|
+
release show cache size for given release[s] only
|
325
|
+
|
326
|
+
options:
|
327
|
+
-h, --help show this help message and exit
|
328
|
+
-T, --no-total do not show total cache size
|
329
|
+
-H, --no-human-readable
|
330
|
+
show sizes in bytes, not human readable format
|
331
|
+
```
|
332
|
+
|
310
333
|
## Installation and Upgrade
|
311
334
|
|
312
335
|
Python 3.8 or later is required. Arch Linux users can install [`pystand`
|
@@ -579,7 +602,7 @@ either version 3 of the License, or any later version. This program is
|
|
579
602
|
distributed in the hope that it will be useful, but WITHOUT ANY
|
580
603
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
581
604
|
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License at
|
582
|
-
<
|
605
|
+
<https://en.wikipedia.org/wiki/GNU_General_Public_License> for more details.
|
583
606
|
|
584
607
|
[pystand]: https://github.com/bulletmark/pystand
|
585
608
|
[pbs]: https://github.com/astral-sh/python-build-standalone
|
@@ -1,20 +1,3 @@
|
|
1
|
-
Metadata-Version: 2.4
|
2
|
-
Name: pystand
|
3
|
-
Version: 2.15
|
4
|
-
Summary: Install Python versions from python-build-standalone project
|
5
|
-
Author-email: Mark Blakeney <mark.blakeney@bullet-systems.net>
|
6
|
-
License-Expression: GPL-3.0-or-later
|
7
|
-
Project-URL: Homepage, https://github.com/bulletmark/pystand
|
8
|
-
Keywords: python-build-standalone,pyenv,hatch,pdm
|
9
|
-
Classifier: Programming Language :: Python :: 3
|
10
|
-
Requires-Python: >=3.8
|
11
|
-
Description-Content-Type: text/markdown
|
12
|
-
Requires-Dist: argcomplete
|
13
|
-
Requires-Dist: packaging
|
14
|
-
Requires-Dist: platformdirs
|
15
|
-
Requires-Dist: pygithub
|
16
|
-
Requires-Dist: zstandard
|
17
|
-
|
18
1
|
## PYSTAND - Install Python Versions From The Python-Build-Standalone Project
|
19
2
|
[](https://pypi.org/project/pystand/)
|
20
3
|
[](https://aur.archlinux.org/packages/pystand/)
|
@@ -41,7 +24,7 @@ required
|
|
41
24
|
for your machine architecture is normally auto-detected. By default, the
|
42
25
|
_`install_only_stripped`_ build of the distribution is installed but you
|
43
26
|
can choose to [install any other
|
44
|
-
build/distribution](#installing-other-
|
27
|
+
build/distribution](#installing-other-buildsdistributions) instead, or
|
45
28
|
in parallel.
|
46
29
|
|
47
30
|
Some simple usage examples are:
|
@@ -130,8 +113,8 @@ Type `pystand` or `pystand -h` to view the usage summary:
|
|
130
113
|
usage: pystand [-h] [-D DISTRIBUTION] [-P PREFIX_DIR] [-C CACHE_DIR]
|
131
114
|
[-M CACHE_MINUTES] [--purge-days PURGE_DAYS]
|
132
115
|
[--github-access-token GITHUB_ACCESS_TOKEN] [--no-strip]
|
133
|
-
[-V]
|
134
|
-
{install,update,upgrade,remove,uninstall,list,show,path} ...
|
116
|
+
[--cert {system,certifi,none}] [-V]
|
117
|
+
{install,update,upgrade,remove,uninstall,list,show,path,cache} ...
|
135
118
|
|
136
119
|
Command line tool to download, install, and update pre-built Python versions
|
137
120
|
from the python-build-standalone project at https://github.com/astral-
|
@@ -141,8 +124,9 @@ options:
|
|
141
124
|
-h, --help show this help message and exit
|
142
125
|
-D, --distribution DISTRIBUTION
|
143
126
|
python-build-standalone distribution. Default is
|
144
|
-
"x86_64_v3-unknown-linux-gnu-install_only_stripped
|
145
|
-
this host
|
127
|
+
"x86_64_v3-unknown-linux-gnu-install_only_stripped"
|
128
|
+
for this host. Run "pystand show -a" to see all
|
129
|
+
distributions.
|
146
130
|
-P, --prefix-dir PREFIX_DIR
|
147
131
|
specify prefix dir for storing versions. Default is
|
148
132
|
"$HOME/.local/share/pystand"
|
@@ -161,10 +145,13 @@ options:
|
|
161
145
|
optional Github access token. Can specify to reduce
|
162
146
|
rate limiting.
|
163
147
|
--no-strip do not strip downloaded binaries
|
148
|
+
--cert {system,certifi,none}
|
149
|
+
specify which SSL certificates to use for HTTPS
|
150
|
+
requests. Default="system"
|
164
151
|
-V, --version just show pystand version
|
165
152
|
|
166
153
|
Commands:
|
167
|
-
{install,update,upgrade,remove,uninstall,list,show,path}
|
154
|
+
{install,update,upgrade,remove,uninstall,list,show,path,cache}
|
168
155
|
install Install one or more versions from a python-build-
|
169
156
|
standalone release.
|
170
157
|
update (upgrade) Update one, more, or all versions to another release.
|
@@ -173,6 +160,7 @@ Commands:
|
|
173
160
|
available.
|
174
161
|
show Show versions available from a release.
|
175
162
|
path Show path prefix to installed version base directory.
|
163
|
+
cache Show release cache sizes.
|
176
164
|
|
177
165
|
Some commands offer aliases as shown in parentheses above. Note you can set
|
178
166
|
default starting global options in $HOME/.config/pystand-flags.conf.
|
@@ -307,6 +295,23 @@ options:
|
|
307
295
|
-c, --cache-path just show path to cache dir
|
308
296
|
```
|
309
297
|
|
298
|
+
### Command `cache`
|
299
|
+
|
300
|
+
```
|
301
|
+
usage: pystand cache [-h] [-T] [-H] [release ...]
|
302
|
+
|
303
|
+
Show release cache sizes.
|
304
|
+
|
305
|
+
positional arguments:
|
306
|
+
release show cache size for given release[s] only
|
307
|
+
|
308
|
+
options:
|
309
|
+
-h, --help show this help message and exit
|
310
|
+
-T, --no-total do not show total cache size
|
311
|
+
-H, --no-human-readable
|
312
|
+
show sizes in bytes, not human readable format
|
313
|
+
```
|
314
|
+
|
310
315
|
## Installation and Upgrade
|
311
316
|
|
312
317
|
Python 3.8 or later is required. Arch Linux users can install [`pystand`
|
@@ -579,7 +584,7 @@ either version 3 of the License, or any later version. This program is
|
|
579
584
|
distributed in the hope that it will be useful, but WITHOUT ANY
|
580
585
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
581
586
|
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License at
|
582
|
-
<
|
587
|
+
<https://en.wikipedia.org/wiki/GNU_General_Public_License> for more details.
|
583
588
|
|
584
589
|
[pystand]: https://github.com/bulletmark/pystand
|
585
590
|
[pbs]: https://github.com/astral-sh/python-build-standalone
|
@@ -1,3 +1,21 @@
|
|
1
|
+
Metadata-Version: 2.4
|
2
|
+
Name: pystand
|
3
|
+
Version: 2.17
|
4
|
+
Summary: Install Python versions from python-build-standalone project
|
5
|
+
Author-email: Mark Blakeney <mark.blakeney@bullet-systems.net>
|
6
|
+
License-Expression: GPL-3.0-or-later
|
7
|
+
Project-URL: Homepage, https://github.com/bulletmark/pystand
|
8
|
+
Keywords: python-build-standalone,pyenv,hatch,pdm
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
10
|
+
Requires-Python: >=3.8
|
11
|
+
Description-Content-Type: text/markdown
|
12
|
+
Requires-Dist: argcomplete
|
13
|
+
Requires-Dist: packaging
|
14
|
+
Requires-Dist: platformdirs
|
15
|
+
Requires-Dist: pygithub
|
16
|
+
Requires-Dist: certifi
|
17
|
+
Requires-Dist: zstandard; python_version < "3.14"
|
18
|
+
|
1
19
|
## PYSTAND - Install Python Versions From The Python-Build-Standalone Project
|
2
20
|
[](https://pypi.org/project/pystand/)
|
3
21
|
[](https://aur.archlinux.org/packages/pystand/)
|
@@ -24,7 +42,7 @@ required
|
|
24
42
|
for your machine architecture is normally auto-detected. By default, the
|
25
43
|
_`install_only_stripped`_ build of the distribution is installed but you
|
26
44
|
can choose to [install any other
|
27
|
-
build/distribution](#installing-other-
|
45
|
+
build/distribution](#installing-other-buildsdistributions) instead, or
|
28
46
|
in parallel.
|
29
47
|
|
30
48
|
Some simple usage examples are:
|
@@ -113,8 +131,8 @@ Type `pystand` or `pystand -h` to view the usage summary:
|
|
113
131
|
usage: pystand [-h] [-D DISTRIBUTION] [-P PREFIX_DIR] [-C CACHE_DIR]
|
114
132
|
[-M CACHE_MINUTES] [--purge-days PURGE_DAYS]
|
115
133
|
[--github-access-token GITHUB_ACCESS_TOKEN] [--no-strip]
|
116
|
-
[-V]
|
117
|
-
{install,update,upgrade,remove,uninstall,list,show,path} ...
|
134
|
+
[--cert {system,certifi,none}] [-V]
|
135
|
+
{install,update,upgrade,remove,uninstall,list,show,path,cache} ...
|
118
136
|
|
119
137
|
Command line tool to download, install, and update pre-built Python versions
|
120
138
|
from the python-build-standalone project at https://github.com/astral-
|
@@ -124,8 +142,9 @@ options:
|
|
124
142
|
-h, --help show this help message and exit
|
125
143
|
-D, --distribution DISTRIBUTION
|
126
144
|
python-build-standalone distribution. Default is
|
127
|
-
"x86_64_v3-unknown-linux-gnu-install_only_stripped
|
128
|
-
this host
|
145
|
+
"x86_64_v3-unknown-linux-gnu-install_only_stripped"
|
146
|
+
for this host. Run "pystand show -a" to see all
|
147
|
+
distributions.
|
129
148
|
-P, --prefix-dir PREFIX_DIR
|
130
149
|
specify prefix dir for storing versions. Default is
|
131
150
|
"$HOME/.local/share/pystand"
|
@@ -144,10 +163,13 @@ options:
|
|
144
163
|
optional Github access token. Can specify to reduce
|
145
164
|
rate limiting.
|
146
165
|
--no-strip do not strip downloaded binaries
|
166
|
+
--cert {system,certifi,none}
|
167
|
+
specify which SSL certificates to use for HTTPS
|
168
|
+
requests. Default="system"
|
147
169
|
-V, --version just show pystand version
|
148
170
|
|
149
171
|
Commands:
|
150
|
-
{install,update,upgrade,remove,uninstall,list,show,path}
|
172
|
+
{install,update,upgrade,remove,uninstall,list,show,path,cache}
|
151
173
|
install Install one or more versions from a python-build-
|
152
174
|
standalone release.
|
153
175
|
update (upgrade) Update one, more, or all versions to another release.
|
@@ -156,6 +178,7 @@ Commands:
|
|
156
178
|
available.
|
157
179
|
show Show versions available from a release.
|
158
180
|
path Show path prefix to installed version base directory.
|
181
|
+
cache Show release cache sizes.
|
159
182
|
|
160
183
|
Some commands offer aliases as shown in parentheses above. Note you can set
|
161
184
|
default starting global options in $HOME/.config/pystand-flags.conf.
|
@@ -290,6 +313,23 @@ options:
|
|
290
313
|
-c, --cache-path just show path to cache dir
|
291
314
|
```
|
292
315
|
|
316
|
+
### Command `cache`
|
317
|
+
|
318
|
+
```
|
319
|
+
usage: pystand cache [-h] [-T] [-H] [release ...]
|
320
|
+
|
321
|
+
Show release cache sizes.
|
322
|
+
|
323
|
+
positional arguments:
|
324
|
+
release show cache size for given release[s] only
|
325
|
+
|
326
|
+
options:
|
327
|
+
-h, --help show this help message and exit
|
328
|
+
-T, --no-total do not show total cache size
|
329
|
+
-H, --no-human-readable
|
330
|
+
show sizes in bytes, not human readable format
|
331
|
+
```
|
332
|
+
|
293
333
|
## Installation and Upgrade
|
294
334
|
|
295
335
|
Python 3.8 or later is required. Arch Linux users can install [`pystand`
|
@@ -562,7 +602,7 @@ either version 3 of the License, or any later version. This program is
|
|
562
602
|
distributed in the hope that it will be useful, but WITHOUT ANY
|
563
603
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
564
604
|
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License at
|
565
|
-
<
|
605
|
+
<https://en.wikipedia.org/wiki/GNU_General_Public_License> for more details.
|
566
606
|
|
567
607
|
[pystand]: https://github.com/bulletmark/pystand
|
568
608
|
[pbs]: https://github.com/astral-sh/python-build-standalone
|
@@ -13,6 +13,7 @@ import platform
|
|
13
13
|
import re
|
14
14
|
import shlex
|
15
15
|
import shutil
|
16
|
+
import ssl
|
16
17
|
import sys
|
17
18
|
import time
|
18
19
|
from argparse import ArgumentParser, Namespace
|
@@ -20,6 +21,7 @@ from collections import defaultdict
|
|
20
21
|
from datetime import date, datetime
|
21
22
|
from pathlib import Path
|
22
23
|
from typing import Any, Iterable, Iterator
|
24
|
+
from urllib.request import urlopen
|
23
25
|
|
24
26
|
import argcomplete
|
25
27
|
import platformdirs
|
@@ -45,10 +47,13 @@ DISTRIBUTIONS = {
|
|
45
47
|
('Linux', 'armv8l'): 'armv7-unknown-linux-gnueabihf-install_only_stripped',
|
46
48
|
('Darwin', 'x86_64'): 'x86_64-apple-darwin-install_only_stripped',
|
47
49
|
('Darwin', 'aarch64'): 'aarch64-apple-darwin-install_only_stripped',
|
50
|
+
('Darwin', 'arm64'): 'aarch64-apple-darwin-install_only_stripped',
|
48
51
|
('Windows', 'x86_64'): 'x86_64-pc-windows-msvc-shared-install_only_stripped',
|
49
52
|
('Windows', 'i686'): 'i686-pc-windows-msvc-shared-install_only_stripped',
|
50
53
|
}
|
51
54
|
|
55
|
+
CERTS = ('system', 'certifi', 'none')
|
56
|
+
|
52
57
|
|
53
58
|
def is_admin() -> bool:
|
54
59
|
"Check if we are running as root"
|
@@ -77,7 +82,7 @@ def fmt(version, release) -> str:
|
|
77
82
|
return f'{version} @ {release}'
|
78
83
|
|
79
84
|
|
80
|
-
def get_json(file: Path) -> dict:
|
85
|
+
def get_json(file: Path) -> dict[str, Any]:
|
81
86
|
from json import load
|
82
87
|
|
83
88
|
'Get JSON data from given file'
|
@@ -90,7 +95,7 @@ def get_json(file: Path) -> dict:
|
|
90
95
|
return {}
|
91
96
|
|
92
97
|
|
93
|
-
def set_json(file: Path, data: dict) -> str | None:
|
98
|
+
def set_json(file: Path, data: dict[str, Any]) -> str | None:
|
94
99
|
"Set JSON data to given file"
|
95
100
|
from json import dump
|
96
101
|
|
@@ -138,23 +143,30 @@ def rm_path(path: Path) -> None:
|
|
138
143
|
path.unlink()
|
139
144
|
|
140
145
|
|
141
|
-
def
|
142
|
-
"
|
143
|
-
|
146
|
+
def register_zst() -> None:
|
147
|
+
"Register custom zstandard unpacker"
|
148
|
+
# Python 3.14+ has built-in support for zstandard so only register custom
|
149
|
+
# handler for earlier versions
|
150
|
+
if sys.version_info < (3, 14):
|
151
|
+
|
152
|
+
def unpack_zst(filename: str, extract_dir: str) -> None:
|
153
|
+
"Unpack a zstandard compressed tar"
|
154
|
+
import tarfile
|
155
|
+
|
156
|
+
import zstandard
|
144
157
|
|
145
|
-
|
158
|
+
with open(filename, 'rb') as compressed:
|
159
|
+
dctx = zstandard.ZstdDecompressor()
|
160
|
+
with dctx.stream_reader(compressed) as reader:
|
161
|
+
with tarfile.open(fileobj=reader, mode='r|') as tar:
|
162
|
+
tar.extractall(path=extract_dir)
|
146
163
|
|
147
|
-
|
148
|
-
dctx = zstandard.ZstdDecompressor()
|
149
|
-
with dctx.stream_reader(compressed) as reader:
|
150
|
-
with tarfile.open(fileobj=reader, mode='r|') as tar:
|
151
|
-
tar.extractall(path=extract_dir)
|
164
|
+
shutil.register_unpack_format('zst', ['.zst'], unpack_zst)
|
152
165
|
|
153
166
|
|
154
167
|
def fetch(args: Namespace, release: str, url: str, tdir: Path) -> str | None:
|
155
168
|
"Fetch and unpack a release file"
|
156
169
|
from urllib.parse import unquote, urlparse
|
157
|
-
from urllib.request import urlretrieve
|
158
170
|
|
159
171
|
error = None
|
160
172
|
tmpdir = tdir.with_name(f'{tdir.name}-tmp')
|
@@ -168,7 +180,8 @@ def fetch(args: Namespace, release: str, url: str, tdir: Path) -> str | None:
|
|
168
180
|
|
169
181
|
if not cache_file.exists():
|
170
182
|
try:
|
171
|
-
|
183
|
+
with urlopen(url, context=args._cert) as urlp, cache_file.open('wb') as fp:
|
184
|
+
shutil.copyfileobj(urlp, fp)
|
172
185
|
except Exception as e:
|
173
186
|
error = f'Failed to fetch "{url}": {e}'
|
174
187
|
|
@@ -176,7 +189,7 @@ def fetch(args: Namespace, release: str, url: str, tdir: Path) -> str | None:
|
|
176
189
|
rm_path(cache_file)
|
177
190
|
else:
|
178
191
|
if filename.endswith('.zst'):
|
179
|
-
|
192
|
+
register_zst()
|
180
193
|
|
181
194
|
try:
|
182
195
|
shutil.unpack_archive(cache_file, tmpdir)
|
@@ -306,13 +319,12 @@ def check_release_tag(release: str) -> str | None:
|
|
306
319
|
# Note we use a simple direct URL fetch to get the latest tag info
|
307
320
|
# because it is much faster than using the GitHub API, and has no
|
308
321
|
# rate-limits.
|
309
|
-
def fetch_tags() -> Iterator[tuple[str, str]]:
|
322
|
+
def fetch_tags(args: Namespace) -> Iterator[tuple[str, str]]:
|
310
323
|
"Fetch the latest release tags from the GitHub release atom feed"
|
311
324
|
import xml.etree.ElementTree as et
|
312
|
-
from urllib.request import urlopen
|
313
325
|
|
314
326
|
try:
|
315
|
-
with urlopen(LATEST_RELEASES) as url:
|
327
|
+
with urlopen(LATEST_RELEASES, context=args._cert) as url:
|
316
328
|
data = et.parse(url).getroot()
|
317
329
|
except Exception:
|
318
330
|
sys.exit('Failed to fetch latest YYYYMMDD release atom file.')
|
@@ -325,12 +337,10 @@ def fetch_tags() -> Iterator[tuple[str, str]]:
|
|
325
337
|
yield tl, dt
|
326
338
|
|
327
339
|
|
328
|
-
def fetch_tag_latest() -> str:
|
340
|
+
def fetch_tag_latest(args: Namespace) -> str:
|
329
341
|
"Fetch the latest release tag from the GitHub"
|
330
|
-
from urllib.request import urlopen
|
331
|
-
|
332
342
|
try:
|
333
|
-
with urlopen(LATEST_RELEASE_TAG) as url:
|
343
|
+
with urlopen(LATEST_RELEASE_TAG, context=args._cert) as url:
|
334
344
|
data = url.geturl()
|
335
345
|
except Exception:
|
336
346
|
sys.exit('Failed to fetch latest YYYYMMDD release tag.')
|
@@ -351,14 +361,14 @@ def get_release_tag(args: Namespace) -> str:
|
|
351
361
|
if time.time() < (stat.st_mtime + int(args.cache_minutes * 60)):
|
352
362
|
return args._latest_release.read_text().strip()
|
353
363
|
|
354
|
-
if not (tag := fetch_tag_latest()):
|
364
|
+
if not (tag := fetch_tag_latest(args)):
|
355
365
|
sys.exit('Latest YYYYMMDD release tag timestamp file is unavailable.')
|
356
366
|
|
357
367
|
args._latest_release.write_text(tag + '\n')
|
358
368
|
return tag
|
359
369
|
|
360
370
|
|
361
|
-
def add_file(files: dict, tag: str, name: str, url: str) -> None:
|
371
|
+
def add_file(files: dict[str, Any], tag: str, name: str, url: str) -> None:
|
362
372
|
"Extract the implementation, version, and architecture from a filename"
|
363
373
|
if name.endswith('.tar.zst'):
|
364
374
|
name = name[:-8]
|
@@ -386,7 +396,7 @@ def add_file(files: dict, tag: str, name: str, url: str) -> None:
|
|
386
396
|
vers[ver][arch] = url
|
387
397
|
|
388
398
|
|
389
|
-
def get_release_files(args, tag
|
399
|
+
def get_release_files(args, tag) -> dict[str, Any]:
|
390
400
|
"Return the release files for the given tag"
|
391
401
|
# Look for tag data in our release cache
|
392
402
|
jfile = args._releases / tag
|
@@ -414,7 +424,7 @@ def get_release_files(args, tag, implementation: str | None = None) -> dict:
|
|
414
424
|
if error := set_json(jfile, files):
|
415
425
|
sys.exit(f'Failed to write release {tag} file {jfile}: {error}')
|
416
426
|
|
417
|
-
return files.get(
|
427
|
+
return files.get(args._implementation, {})
|
418
428
|
|
419
429
|
|
420
430
|
def update_version_symlinks(args: Namespace) -> None:
|
@@ -499,7 +509,7 @@ def purge_unused_releases(args: Namespace) -> None:
|
|
499
509
|
def show_list(args: Namespace) -> None:
|
500
510
|
"Show a list of available releases"
|
501
511
|
latest = parse_version(get_release_tag(args))
|
502
|
-
releases = {r: d for r, d in fetch_tags()}
|
512
|
+
releases = {r: d for r, d in fetch_tags(args)}
|
503
513
|
cached = set(p.name for p in args._releases.iterdir())
|
504
514
|
for release in sorted(cached.union(releases)):
|
505
515
|
if args.re_match and not re.search(args.re_match, release):
|
@@ -578,7 +588,7 @@ def strip_binaries(vdir: Path, distribution: str) -> bool:
|
|
578
588
|
|
579
589
|
|
580
590
|
def install(
|
581
|
-
args: Namespace, vdir: Path, release: str, distribution: str, files: dict
|
591
|
+
args: Namespace, vdir: Path, release: str, distribution: str, files: dict[str, Any]
|
582
592
|
) -> str | None:
|
583
593
|
"Install a version"
|
584
594
|
version = vdir.name
|
@@ -610,6 +620,51 @@ def install(
|
|
610
620
|
return error
|
611
621
|
|
612
622
|
|
623
|
+
def to_human(num, prec: int | None = None) -> str:
|
624
|
+
"Convert a number of bytes to a human-readable format"
|
625
|
+
units = ('B', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y')
|
626
|
+
for unit in units:
|
627
|
+
if abs(num) < 1024.0 or unit == units[-1]:
|
628
|
+
return f'{round(num, prec)}{unit}'
|
629
|
+
num /= 1024.0
|
630
|
+
|
631
|
+
return ''
|
632
|
+
|
633
|
+
|
634
|
+
def show_cache_size(path: Path, args: Namespace) -> None:
|
635
|
+
"Show the size of the cache directory"
|
636
|
+
total = 0
|
637
|
+
for spath in sorted(path.iterdir()):
|
638
|
+
size = (
|
639
|
+
sum(p.stat().st_size for p in spath.iterdir())
|
640
|
+
if spath.is_dir()
|
641
|
+
else spath.stat().st_size
|
642
|
+
)
|
643
|
+
total += size
|
644
|
+
size_str = f'{size}B' if args.no_human_readable else to_human(size)
|
645
|
+
name = str(spath) if spath.is_dir() else spath.name
|
646
|
+
print(f'{size_str}\t{name}')
|
647
|
+
|
648
|
+
if not args.no_total:
|
649
|
+
size_str = f'{total}B' if args.no_human_readable else to_human(total, 2)
|
650
|
+
print(f'{size_str}\tTOTAL')
|
651
|
+
|
652
|
+
|
653
|
+
def create_cert(option: str) -> ssl.SSLContext | None:
|
654
|
+
"Create an SSL context for HTTPS connections based on the specified certificate store"
|
655
|
+
|
656
|
+
if not option or option == 'system':
|
657
|
+
return None
|
658
|
+
|
659
|
+
if option == 'certifi':
|
660
|
+
import certifi
|
661
|
+
|
662
|
+
return ssl.create_default_context(cafile=certifi.where())
|
663
|
+
|
664
|
+
assert option == 'none'
|
665
|
+
return ssl._create_unverified_context()
|
666
|
+
|
667
|
+
|
613
668
|
def main() -> str | None:
|
614
669
|
"Main code"
|
615
670
|
distro_default = DISTRIBUTIONS.get((platform.system(), platform.machine()))
|
@@ -631,7 +686,8 @@ def main() -> str | None:
|
|
631
686
|
opt.add_argument(
|
632
687
|
'-D',
|
633
688
|
'--distribution',
|
634
|
-
help=f'{REPO} distribution. Default is "{distro_help} for this host'
|
689
|
+
help=f'{REPO} distribution. Default is "{distro_help}" for this host. '
|
690
|
+
f'Run "{PROG} show -a" to see all distributions.',
|
635
691
|
)
|
636
692
|
opt.add_argument(
|
637
693
|
'-P',
|
@@ -669,6 +725,11 @@ def main() -> str | None:
|
|
669
725
|
opt.add_argument(
|
670
726
|
'--no-strip', action='store_true', help='do not strip downloaded binaries'
|
671
727
|
)
|
728
|
+
opt.add_argument(
|
729
|
+
'--cert',
|
730
|
+
choices=CERTS,
|
731
|
+
help=f'specify which SSL certificates to use for HTTPS requests. Default="{CERTS[0]}"',
|
732
|
+
)
|
672
733
|
opt.add_argument(
|
673
734
|
'-V', '--version', action='store_true', help=f'just show {PROG} version'
|
674
735
|
)
|
@@ -734,6 +795,7 @@ def main() -> str | None:
|
|
734
795
|
prefix_dir = Path(args.prefix_dir).expanduser().resolve()
|
735
796
|
cache_dir = Path(args.cache_dir).expanduser().resolve()
|
736
797
|
|
798
|
+
args._implementation = 'cpython' # at the moment, only support CPython
|
737
799
|
args._distribution = distribution
|
738
800
|
args._data = f'{PROG}.json'
|
739
801
|
|
@@ -745,6 +807,7 @@ def main() -> str | None:
|
|
745
807
|
args._releases = cache_dir / 'releases'
|
746
808
|
args._releases.mkdir(parents=True, exist_ok=True)
|
747
809
|
args._latest_release = cache_dir / 'latest_release'
|
810
|
+
args._cert = create_cert(args.cert)
|
748
811
|
|
749
812
|
result = args.func(args)
|
750
813
|
purge_unused_releases(args)
|
@@ -784,7 +847,7 @@ class install_:
|
|
784
847
|
@staticmethod
|
785
848
|
def run(args: Namespace) -> str | None:
|
786
849
|
release = get_release_tag(args)
|
787
|
-
files = get_release_files(args, release
|
850
|
+
files = get_release_files(args, release)
|
788
851
|
if not files:
|
789
852
|
return f'Release "{release}" not found, or has no compatible files.'
|
790
853
|
|
@@ -843,7 +906,7 @@ class update_:
|
|
843
906
|
@staticmethod
|
844
907
|
def run(args: Namespace) -> str | None:
|
845
908
|
release_target = get_release_tag(args)
|
846
|
-
files = get_release_files(args, release_target
|
909
|
+
files = get_release_files(args, release_target)
|
847
910
|
if not files:
|
848
911
|
return f'Release "{release_target}" not found.'
|
849
912
|
|
@@ -960,7 +1023,7 @@ class list_:
|
|
960
1023
|
@staticmethod
|
961
1024
|
def run(args: Namespace) -> str | None:
|
962
1025
|
release_target = get_release_tag(args)
|
963
|
-
files = get_release_files(args, release_target
|
1026
|
+
files = get_release_files(args, release_target)
|
964
1027
|
if not files:
|
965
1028
|
return f'Release "{release_target}" not found.'
|
966
1029
|
|
@@ -1058,7 +1121,7 @@ class show_:
|
|
1058
1121
|
return None
|
1059
1122
|
|
1060
1123
|
release = get_release_tag(args)
|
1061
|
-
files = get_release_files(args, release
|
1124
|
+
files = get_release_files(args, release)
|
1062
1125
|
if not files:
|
1063
1126
|
return f'Error: release "{release}" not found.'
|
1064
1127
|
|
@@ -1143,5 +1206,33 @@ class path_:
|
|
1143
1206
|
print(path)
|
1144
1207
|
|
1145
1208
|
|
1209
|
+
# COMMAND
|
1210
|
+
class cache_:
|
1211
|
+
"Show release cache sizes."
|
1212
|
+
|
1213
|
+
@staticmethod
|
1214
|
+
def init(parser: ArgumentParser) -> None:
|
1215
|
+
parser.add_argument(
|
1216
|
+
'-T', '--no-total', action='store_true', help='do not show total cache size'
|
1217
|
+
)
|
1218
|
+
parser.add_argument(
|
1219
|
+
'-H',
|
1220
|
+
'--no-human-readable',
|
1221
|
+
action='store_true',
|
1222
|
+
help='show sizes in bytes, not human readable format',
|
1223
|
+
)
|
1224
|
+
parser.add_argument(
|
1225
|
+
'release', nargs='*', help='show cache size for given release[s] only'
|
1226
|
+
)
|
1227
|
+
|
1228
|
+
@staticmethod
|
1229
|
+
def run(args: Namespace) -> str | None:
|
1230
|
+
if args.release:
|
1231
|
+
for release in args.release:
|
1232
|
+
show_cache_size(args._downloads / release, args)
|
1233
|
+
else:
|
1234
|
+
show_cache_size(args._downloads, args)
|
1235
|
+
|
1236
|
+
|
1146
1237
|
if __name__ == '__main__':
|
1147
1238
|
sys.exit(main())
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|