pystand 1.8__tar.gz → 1.9__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-1.8/pystand.egg-info → pystand-1.9}/PKG-INFO +9 -8
- {pystand-1.8 → pystand-1.9}/README.md +8 -7
- {pystand-1.8 → pystand-1.9/pystand.egg-info}/PKG-INFO +9 -8
- {pystand-1.8 → pystand-1.9}/pystand.py +50 -27
- {pystand-1.8 → pystand-1.9}/.flake8 +0 -0
- {pystand-1.8 → pystand-1.9}/.gitignore +0 -0
- {pystand-1.8 → pystand-1.9}/Makefile +0 -0
- {pystand-1.8 → pystand-1.9}/pyproject.toml +0 -0
- {pystand-1.8 → pystand-1.9}/pystand.egg-info/SOURCES.txt +0 -0
- {pystand-1.8 → pystand-1.9}/pystand.egg-info/dependency_links.txt +0 -0
- {pystand-1.8 → pystand-1.9}/pystand.egg-info/entry_points.txt +0 -0
- {pystand-1.8 → pystand-1.9}/pystand.egg-info/requires.txt +0 -0
- {pystand-1.8 → pystand-1.9}/pystand.egg-info/top_level.txt +0 -0
- {pystand-1.8 → pystand-1.9}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: pystand
|
3
|
-
Version: 1.
|
3
|
+
Version: 1.9
|
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: GPLv3
|
@@ -255,18 +255,18 @@ options:
|
|
255
255
|
### Command `show`
|
256
256
|
|
257
257
|
```
|
258
|
-
usage: pystand show [-h] [-
|
258
|
+
usage: pystand show [-h] [-D] [release]
|
259
259
|
|
260
260
|
Show versions available from a release.
|
261
261
|
|
262
262
|
positional arguments:
|
263
|
-
release
|
264
|
-
|
263
|
+
release python-build-standalone YYYYMMDD release to show (e.g.
|
264
|
+
20240415), default is latest release
|
265
265
|
|
266
266
|
options:
|
267
|
-
-h, --help
|
268
|
-
-
|
269
|
-
|
267
|
+
-h, --help show this help message and exit
|
268
|
+
-D, --distribution also show all available distributions for each version
|
269
|
+
from the release
|
270
270
|
```
|
271
271
|
|
272
272
|
### Command `path`
|
@@ -291,7 +291,7 @@ from the AUR](https://aur.archlinux.org/packages/pystand) and skip this
|
|
291
291
|
section.
|
292
292
|
|
293
293
|
The easiest way to install [`pystand`][pystand] is to use [`pipx`][pipx]
|
294
|
-
(or [`pipxu`][pipxu]).
|
294
|
+
(or [`pipxu`][pipxu], or [`uv tool`][uvtool]).
|
295
295
|
|
296
296
|
```sh
|
297
297
|
$ pipx install pystand
|
@@ -375,6 +375,7 @@ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License at
|
|
375
375
|
[pbs-rel]: https://github.com/indygreg/python-build-standalone/releases
|
376
376
|
[pipx]: https://github.com/pypa/pipx
|
377
377
|
[pipxu]: https://github.com/bulletmark/pipxu
|
378
|
+
[uvtool]: https://docs.astral.sh/uv/guides/tools/#installing-tools
|
378
379
|
[pyenv]: https://github.com/pyenv/pyenv
|
379
380
|
[pdm]: https://pdm-project.org/
|
380
381
|
[pdmpy]: https://pdm-project.org/en/latest/usage/project/#install-python-interpreters-with-pdm
|
@@ -239,18 +239,18 @@ options:
|
|
239
239
|
### Command `show`
|
240
240
|
|
241
241
|
```
|
242
|
-
usage: pystand show [-h] [-
|
242
|
+
usage: pystand show [-h] [-D] [release]
|
243
243
|
|
244
244
|
Show versions available from a release.
|
245
245
|
|
246
246
|
positional arguments:
|
247
|
-
release
|
248
|
-
|
247
|
+
release python-build-standalone YYYYMMDD release to show (e.g.
|
248
|
+
20240415), default is latest release
|
249
249
|
|
250
250
|
options:
|
251
|
-
-h, --help
|
252
|
-
-
|
253
|
-
|
251
|
+
-h, --help show this help message and exit
|
252
|
+
-D, --distribution also show all available distributions for each version
|
253
|
+
from the release
|
254
254
|
```
|
255
255
|
|
256
256
|
### Command `path`
|
@@ -275,7 +275,7 @@ from the AUR](https://aur.archlinux.org/packages/pystand) and skip this
|
|
275
275
|
section.
|
276
276
|
|
277
277
|
The easiest way to install [`pystand`][pystand] is to use [`pipx`][pipx]
|
278
|
-
(or [`pipxu`][pipxu]).
|
278
|
+
(or [`pipxu`][pipxu], or [`uv tool`][uvtool]).
|
279
279
|
|
280
280
|
```sh
|
281
281
|
$ pipx install pystand
|
@@ -359,6 +359,7 @@ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License at
|
|
359
359
|
[pbs-rel]: https://github.com/indygreg/python-build-standalone/releases
|
360
360
|
[pipx]: https://github.com/pypa/pipx
|
361
361
|
[pipxu]: https://github.com/bulletmark/pipxu
|
362
|
+
[uvtool]: https://docs.astral.sh/uv/guides/tools/#installing-tools
|
362
363
|
[pyenv]: https://github.com/pyenv/pyenv
|
363
364
|
[pdm]: https://pdm-project.org/
|
364
365
|
[pdmpy]: https://pdm-project.org/en/latest/usage/project/#install-python-interpreters-with-pdm
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: pystand
|
3
|
-
Version: 1.
|
3
|
+
Version: 1.9
|
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: GPLv3
|
@@ -255,18 +255,18 @@ options:
|
|
255
255
|
### Command `show`
|
256
256
|
|
257
257
|
```
|
258
|
-
usage: pystand show [-h] [-
|
258
|
+
usage: pystand show [-h] [-D] [release]
|
259
259
|
|
260
260
|
Show versions available from a release.
|
261
261
|
|
262
262
|
positional arguments:
|
263
|
-
release
|
264
|
-
|
263
|
+
release python-build-standalone YYYYMMDD release to show (e.g.
|
264
|
+
20240415), default is latest release
|
265
265
|
|
266
266
|
options:
|
267
|
-
-h, --help
|
268
|
-
-
|
269
|
-
|
267
|
+
-h, --help show this help message and exit
|
268
|
+
-D, --distribution also show all available distributions for each version
|
269
|
+
from the release
|
270
270
|
```
|
271
271
|
|
272
272
|
### Command `path`
|
@@ -291,7 +291,7 @@ from the AUR](https://aur.archlinux.org/packages/pystand) and skip this
|
|
291
291
|
section.
|
292
292
|
|
293
293
|
The easiest way to install [`pystand`][pystand] is to use [`pipx`][pipx]
|
294
|
-
(or [`pipxu`][pipxu]).
|
294
|
+
(or [`pipxu`][pipxu], or [`uv tool`][uvtool]).
|
295
295
|
|
296
296
|
```sh
|
297
297
|
$ pipx install pystand
|
@@ -375,6 +375,7 @@ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License at
|
|
375
375
|
[pbs-rel]: https://github.com/indygreg/python-build-standalone/releases
|
376
376
|
[pipx]: https://github.com/pypa/pipx
|
377
377
|
[pipxu]: https://github.com/bulletmark/pipxu
|
378
|
+
[uvtool]: https://docs.astral.sh/uv/guides/tools/#installing-tools
|
378
379
|
[pyenv]: https://github.com/pyenv/pyenv
|
379
380
|
[pdm]: https://pdm-project.org/
|
380
381
|
[pdmpy]: https://pdm-project.org/en/latest/usage/project/#install-python-interpreters-with-pdm
|
@@ -21,7 +21,7 @@ from argparse import ArgumentParser, Namespace
|
|
21
21
|
from collections import defaultdict
|
22
22
|
from datetime import date
|
23
23
|
from pathlib import Path
|
24
|
-
from typing import Any, Iterable, Iterator
|
24
|
+
from typing import Any, Iterable, Iterator
|
25
25
|
|
26
26
|
import argcomplete
|
27
27
|
import platformdirs
|
@@ -92,7 +92,7 @@ def get_json(file: Path) -> dict:
|
|
92
92
|
|
93
93
|
return {}
|
94
94
|
|
95
|
-
def set_json(file: Path, data: dict) ->
|
95
|
+
def set_json(file: Path, data: dict) -> str | None:
|
96
96
|
'Set JSON data to given file'
|
97
97
|
try:
|
98
98
|
with file.open('w') as fp:
|
@@ -132,26 +132,38 @@ def rm_path(path: Path) -> None:
|
|
132
132
|
elif path.exists():
|
133
133
|
path.unlink()
|
134
134
|
|
135
|
+
def is_release_version(version: str) -> bool:
|
136
|
+
'Check if a string is a formal Python release tag'
|
137
|
+
return version.replace('.', '').isdigit()
|
138
|
+
|
135
139
|
class VersionMatcher:
|
136
140
|
'Match a version string to a list of versions'
|
137
141
|
def __init__(self, seq: Iterable[str]) -> None:
|
138
142
|
self.seq = sorted(seq, key=parse_version, reverse=True)
|
139
143
|
|
140
|
-
def match(self, version: str, *,
|
141
|
-
upconvert_minor: bool = False) -> Optional[str]:
|
144
|
+
def match(self, version: str, *, upgrade: bool = False) -> str | None:
|
142
145
|
'Return full version string given a [possibly] part version prefix'
|
143
146
|
if version in self.seq:
|
144
147
|
return version
|
145
148
|
|
146
|
-
|
149
|
+
is_release = is_release_version(version)
|
150
|
+
major_only = '.' not in version
|
151
|
+
|
152
|
+
if major_only:
|
153
|
+
upgrade = True
|
154
|
+
elif upgrade:
|
147
155
|
version = version.rsplit('.', 1)[0]
|
148
156
|
|
149
157
|
if not version.endswith('.'):
|
150
158
|
version += '.'
|
151
159
|
|
160
|
+
# Only allow upgrade of formal release to another formal
|
161
|
+
# release, or alpha/beta release to another alpha/beta release.
|
152
162
|
for full_version in self.seq:
|
153
163
|
if full_version.startswith(version):
|
154
|
-
|
164
|
+
if not upgrade \
|
165
|
+
or is_release_version(full_version) == is_release:
|
166
|
+
return full_version
|
155
167
|
|
156
168
|
return None
|
157
169
|
|
@@ -191,7 +203,7 @@ def get_version_names(args: Namespace) -> list[str]:
|
|
191
203
|
if args.all else versions
|
192
204
|
|
193
205
|
def check_release_tag(release: str, *,
|
194
|
-
check_first: bool = True) ->
|
206
|
+
check_first: bool = True) -> str | None:
|
195
207
|
'Check the specified release tag is valid'
|
196
208
|
if not release.isdigit() or len(release) != len(FIRST_RELEASE):
|
197
209
|
return 'Release must be a YYYYMMDD string.'
|
@@ -253,7 +265,7 @@ def merge(files: dict, name: str, url: str, stripped: bool) -> None:
|
|
253
265
|
else:
|
254
266
|
files[impl][ver][distrib] = ('', url) if stripped else url
|
255
267
|
|
256
|
-
def get_release_files(args, tag, implementation:
|
268
|
+
def get_release_files(args, tag, implementation: str | None = None) -> dict:
|
257
269
|
'Return the release files for the given tag'
|
258
270
|
# Look for tag data in our release cache
|
259
271
|
jfile = args._releases / tag
|
@@ -295,7 +307,7 @@ def update_version_symlinks(args: Namespace) -> None:
|
|
295
307
|
if not base.exists():
|
296
308
|
return
|
297
309
|
|
298
|
-
# Record
|
310
|
+
# Record all the existing symlinks and version dirs
|
299
311
|
oldlinks = {}
|
300
312
|
vers = []
|
301
313
|
for path in base.iterdir():
|
@@ -303,19 +315,30 @@ def update_version_symlinks(args: Namespace) -> None:
|
|
303
315
|
if path.is_symlink():
|
304
316
|
oldlinks[path.name] = os.readlink(str(path))
|
305
317
|
else:
|
306
|
-
vers.append(path)
|
318
|
+
vers.append(path.name)
|
307
319
|
|
308
320
|
# Create a map of all the new major version links
|
309
|
-
newlinks_all = defaultdict(
|
310
|
-
for
|
311
|
-
|
321
|
+
newlinks_all = defaultdict(set)
|
322
|
+
pre_releases = set(v for v in vers if not is_release_version(v))
|
323
|
+
for namevers in vers:
|
312
324
|
while '.' in namevers[:-1]:
|
313
325
|
namevers_major = namevers.rsplit('.', maxsplit=1)[0]
|
314
|
-
newlinks_all[namevers_major].
|
326
|
+
newlinks_all[namevers_major].add(namevers)
|
327
|
+
|
328
|
+
if namevers in pre_releases:
|
329
|
+
pre_releases.add(namevers_major)
|
330
|
+
|
315
331
|
namevers = namevers_major
|
316
332
|
|
317
|
-
|
318
|
-
|
333
|
+
# Find the latest version for each major version, but ensure we
|
334
|
+
# don't link a major release to pre-released version, if it also can
|
335
|
+
# point to released versions.
|
336
|
+
newlinks = {}
|
337
|
+
for ver, cands in newlinks_all.items():
|
338
|
+
if ver in pre_releases and (rels := (cands - pre_releases)):
|
339
|
+
cands = rels
|
340
|
+
|
341
|
+
newlinks[ver] = sorted(cands, key=parse_version)[-1]
|
319
342
|
|
320
343
|
# Remove all old or invalid existing links
|
321
344
|
for name, tgt in oldlinks.items():
|
@@ -403,7 +426,7 @@ def strip_binaries(vdir: Path, distribution: str) -> bool:
|
|
403
426
|
return was_stripped
|
404
427
|
|
405
428
|
def install(args: Namespace, vdir: Path, release: str, distribution: str,
|
406
|
-
files: dict) ->
|
429
|
+
files: dict) -> str | None:
|
407
430
|
'Install a version'
|
408
431
|
version = vdir.name
|
409
432
|
|
@@ -460,7 +483,7 @@ def install(args: Namespace, vdir: Path, release: str, distribution: str,
|
|
460
483
|
shutil.rmtree(tmpdir)
|
461
484
|
return error
|
462
485
|
|
463
|
-
def main() ->
|
486
|
+
def main() -> str | None:
|
464
487
|
'Main code'
|
465
488
|
distro_default = DISTRIBUTIONS.get((platform.system(), platform.machine()))
|
466
489
|
distro_help = distro_default or '?unknown?'
|
@@ -583,7 +606,7 @@ class _install(COMMAND):
|
|
583
606
|
help='version to install. E.g. 3.12 or 3.12.3')
|
584
607
|
|
585
608
|
@staticmethod
|
586
|
-
def run(args: Namespace) ->
|
609
|
+
def run(args: Namespace) -> str | None:
|
587
610
|
release = get_release_tag(args)
|
588
611
|
files = get_release_files(args, release, 'cpython')
|
589
612
|
if not files:
|
@@ -627,7 +650,7 @@ class _update(COMMAND):
|
|
627
650
|
'--all --skip)')
|
628
651
|
|
629
652
|
@staticmethod
|
630
|
-
def run(args: Namespace) ->
|
653
|
+
def run(args: Namespace) -> str | None:
|
631
654
|
release_target = get_release_tag(args)
|
632
655
|
files = get_release_files(args, release_target, 'cpython')
|
633
656
|
if not files:
|
@@ -642,7 +665,7 @@ class _update(COMMAND):
|
|
642
665
|
if release == release_target:
|
643
666
|
continue
|
644
667
|
|
645
|
-
nextver = matcher.match(version,
|
668
|
+
nextver = matcher.match(version, upgrade=True)
|
646
669
|
|
647
670
|
distribution = data.get('distribution')
|
648
671
|
if not distribution or distribution not in files.get(nextver, {}):
|
@@ -687,7 +710,7 @@ class _remove(COMMAND):
|
|
687
710
|
'--all --skip)')
|
688
711
|
|
689
712
|
@staticmethod
|
690
|
-
def run(args: Namespace) ->
|
713
|
+
def run(args: Namespace) -> str | None:
|
691
714
|
release_del = args.release
|
692
715
|
if release_del and \
|
693
716
|
(err := check_release_tag(release_del, check_first=False)):
|
@@ -716,7 +739,7 @@ class _list(COMMAND):
|
|
716
739
|
help='only list specified version, else all')
|
717
740
|
|
718
741
|
@staticmethod
|
719
|
-
def run(args: Namespace) ->
|
742
|
+
def run(args: Namespace) -> str | None:
|
720
743
|
release_target = get_release_tag(args)
|
721
744
|
files = get_release_files(args, release_target, 'cpython')
|
722
745
|
if not files:
|
@@ -735,7 +758,7 @@ class _list(COMMAND):
|
|
735
758
|
upd = ''
|
736
759
|
app = ''
|
737
760
|
if release_target and release != release_target:
|
738
|
-
nextver = matcher.match(version,
|
761
|
+
nextver = matcher.match(version, upgrade=True)
|
739
762
|
new_vdir = args._versions / nextver
|
740
763
|
if nextver != version and new_vdir.exists():
|
741
764
|
if args.verbose:
|
@@ -762,7 +785,7 @@ class _show(COMMAND):
|
|
762
785
|
'Show versions available from a release.'
|
763
786
|
@staticmethod
|
764
787
|
def init(parser: ArgumentParser) -> None:
|
765
|
-
parser.add_argument('-
|
788
|
+
parser.add_argument('-D', '--distribution', action='store_true',
|
766
789
|
help='also show all available distributions for '
|
767
790
|
'each version from the release')
|
768
791
|
parser.add_argument('release', nargs='?',
|
@@ -789,7 +812,7 @@ class _show(COMMAND):
|
|
789
812
|
for distribution in files[version]:
|
790
813
|
app = ' (installed)' \
|
791
814
|
if distribution == installed_distribution else ''
|
792
|
-
if args.
|
815
|
+
if args.distribution or app \
|
793
816
|
or distribution == args._distribution:
|
794
817
|
if distribution == args._distribution:
|
795
818
|
installable = True
|
@@ -810,7 +833,7 @@ class _path(COMMAND):
|
|
810
833
|
parser.add_argument('version', help='version to return path for')
|
811
834
|
|
812
835
|
@staticmethod
|
813
|
-
def run(args: Namespace) ->
|
836
|
+
def run(args: Namespace) -> str | None:
|
814
837
|
matcher = VersionMatcher([f.name for f in iter_versions(args)])
|
815
838
|
version = matcher.match(args.version) or args.version
|
816
839
|
path = args._versions / version
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|