pystand 1.0__py3-none-any.whl → 1.1__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.
- {pystand-1.0.dist-info → pystand-1.1.dist-info}/METADATA +8 -1
- pystand-1.1.dist-info/RECORD +6 -0
- pystand.py +51 -7
- pystand-1.0.dist-info/RECORD +0 -6
- {pystand-1.0.dist-info → pystand-1.1.dist-info}/WHEEL +0 -0
- {pystand-1.0.dist-info → pystand-1.1.dist-info}/entry_points.txt +0 -0
- {pystand-1.0.dist-info → pystand-1.1.dist-info}/top_level.txt +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: pystand
|
3
|
-
Version: 1.
|
3
|
+
Version: 1.1
|
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
|
@@ -313,6 +313,13 @@ Note, consistent with this, you actually don't need to specify a
|
|
313
313
|
minor version, e.g. `pystand install 3` would also install `3.12.3`
|
314
314
|
(assuming `3.12.3` is the latest available version for Python 3).
|
315
315
|
|
316
|
+
After installs or updates or removals,`pystand` also maintains symbolic
|
317
|
+
links to each latest installed version in it's version directory, e.g. a
|
318
|
+
symlink `~/.local/share/pystand/versions/3.12` will be created pointing
|
319
|
+
to `~/.local/share/pystand/versions/3.12.3` so that you can optionally
|
320
|
+
hard code the symlink directory in places where it can not be set
|
321
|
+
dynamically (i.e. where using `pystand path` is not an option).
|
322
|
+
|
316
323
|
## Command Default Options
|
317
324
|
|
318
325
|
You can add default global options to a personal configuration file
|
@@ -0,0 +1,6 @@
|
|
1
|
+
pystand.py,sha256=mZmfP9CHLmj2rtwqQuteY7dwpkqtsLsU6d8oPxGgeUI,25512
|
2
|
+
pystand-1.1.dist-info/METADATA,sha256=CtnNqg_-4iv49ZXU_gyMVPCZUqiKgKcOyQ3l1kEZ9Pw,14093
|
3
|
+
pystand-1.1.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
4
|
+
pystand-1.1.dist-info/entry_points.txt,sha256=DG4ps3I3nni1bubV1tXs6u8FARgkdbAYaEAzZD4RAo8,41
|
5
|
+
pystand-1.1.dist-info/top_level.txt,sha256=NoWUh19UQymAJLHTCdxMnVwV6Teftef5fzyF3OWLyNY,8
|
6
|
+
pystand-1.1.dist-info/RECORD,,
|
pystand.py
CHANGED
@@ -18,11 +18,11 @@ import urllib.request
|
|
18
18
|
from argparse import ArgumentParser, Namespace
|
19
19
|
from collections import defaultdict
|
20
20
|
from pathlib import Path
|
21
|
-
from typing import Iterable, Iterator, Optional
|
21
|
+
from typing import Any, Iterable, Iterator, Optional
|
22
22
|
|
23
23
|
import argcomplete
|
24
24
|
import platformdirs
|
25
|
-
from packaging.version import
|
25
|
+
from packaging.version import parse as parse_version
|
26
26
|
|
27
27
|
REPO_OWNER = 'indygreg'
|
28
28
|
REPO = 'python-build-standalone'
|
@@ -90,7 +90,7 @@ def set_json(file: Path, data: dict) -> Optional[str]:
|
|
90
90
|
# The gh handle is an opaque github instance handle
|
91
91
|
get_gh_handle = None
|
92
92
|
|
93
|
-
def get_gh(args: Namespace):
|
93
|
+
def get_gh(args: Namespace) -> Any:
|
94
94
|
'Return a GitHub handle'
|
95
95
|
# The gh handle is a global to lazily create it only if/when needed
|
96
96
|
global get_gh_handle
|
@@ -113,7 +113,7 @@ def rm_path(path: Path) -> None:
|
|
113
113
|
class VersionMatcher:
|
114
114
|
'Match a version string to a list of versions'
|
115
115
|
def __init__(self, seq: Iterable[str]) -> None:
|
116
|
-
self.seq = sorted(seq, key=
|
116
|
+
self.seq = sorted(seq, key=parse_version, reverse=True)
|
117
117
|
|
118
118
|
def match(self, version: str, *,
|
119
119
|
upconvert_minor: bool = False) -> Optional[str]:
|
@@ -165,7 +165,8 @@ def get_version_names(args: Namespace) -> list[str]:
|
|
165
165
|
unknowns = [f'"{u}"' for u in unknown]
|
166
166
|
sys.exit(f'Error: version{s} {", ".join(unknowns)} not found.')
|
167
167
|
|
168
|
-
return sorted(all_names - given, key=
|
168
|
+
return sorted(all_names - given, key=parse_version) \
|
169
|
+
if args.all else versions
|
169
170
|
|
170
171
|
def get_latest_release_tag(args: Namespace) -> str:
|
171
172
|
'Return the latest release tag'
|
@@ -191,7 +192,7 @@ def get_latest_release_tag(args: Namespace) -> str:
|
|
191
192
|
args._latest_release.write_text(tag + '\n')
|
192
193
|
return tag
|
193
194
|
|
194
|
-
def get_release_files(args, tag, implementation: str = None) -> dict:
|
195
|
+
def get_release_files(args, tag, implementation: Optional[str] = None) -> dict:
|
195
196
|
'Return the release files for the given tag'
|
196
197
|
# Look for tag data in our release cache
|
197
198
|
jfile = args._releases / tag
|
@@ -226,6 +227,48 @@ def get_release_files(args, tag, implementation: str = None) -> dict:
|
|
226
227
|
|
227
228
|
return files.get(implementation, {}) if implementation else files
|
228
229
|
|
230
|
+
def update_version_symlinks(args: Namespace) -> None:
|
231
|
+
'Create/update symlinks pointing to latest version'
|
232
|
+
base = args._versions
|
233
|
+
if not base.exists():
|
234
|
+
return
|
235
|
+
|
236
|
+
# Record of all the existing symlinks and version dirs
|
237
|
+
oldlinks = {}
|
238
|
+
vers = []
|
239
|
+
for path in base.iterdir():
|
240
|
+
if not path.name.startswith('.'):
|
241
|
+
if path.is_symlink():
|
242
|
+
oldlinks[path.name] = os.readlink(str(path))
|
243
|
+
else:
|
244
|
+
vers.append(path)
|
245
|
+
|
246
|
+
# Create a map of all the new major version links
|
247
|
+
newlinks_all = defaultdict(list)
|
248
|
+
for path in vers:
|
249
|
+
namevers = path.name
|
250
|
+
while '.' in namevers[:-1]:
|
251
|
+
namevers_major = namevers.rsplit('.', maxsplit=1)[0]
|
252
|
+
newlinks_all[namevers_major].append(namevers)
|
253
|
+
namevers = namevers_major
|
254
|
+
|
255
|
+
newlinks = {k: sorted(v, key=parse_version)[-1] for k, v in
|
256
|
+
newlinks_all.items()}
|
257
|
+
|
258
|
+
# Remove all old or invalid existing links
|
259
|
+
for name, tgt in oldlinks.items():
|
260
|
+
new_tgt = newlinks.get(name)
|
261
|
+
if not new_tgt or new_tgt != tgt:
|
262
|
+
path = Path(base / name)
|
263
|
+
path.unlink()
|
264
|
+
|
265
|
+
# Create all needed new links
|
266
|
+
for name, tgt in newlinks.items():
|
267
|
+
old_tgt = oldlinks.get(name)
|
268
|
+
if not old_tgt or old_tgt != tgt:
|
269
|
+
path = Path(base / name)
|
270
|
+
path.symlink_to(tgt, target_is_directory=True)
|
271
|
+
|
229
272
|
def purge_unused_releases(args: Namespace) -> None:
|
230
273
|
'Purge old releases that are no longer needed and have expired'
|
231
274
|
releases = set(f.name for f in args._releases.iterdir())
|
@@ -405,6 +448,7 @@ def main() -> Optional[str]:
|
|
405
448
|
args._releases.mkdir(parents=True, exist_ok=True)
|
406
449
|
|
407
450
|
result = args.func(args)
|
451
|
+
update_version_symlinks(args)
|
408
452
|
purge_unused_releases(args)
|
409
453
|
return result
|
410
454
|
|
@@ -609,7 +653,7 @@ class _show(COMMAND):
|
|
609
653
|
installed[vdir.name] = distro
|
610
654
|
|
611
655
|
installable = False
|
612
|
-
for version in sorted(files, key=
|
656
|
+
for version in sorted(files, key=parse_version):
|
613
657
|
installed_distribution = installed.get(version)
|
614
658
|
for distribution in files[version]:
|
615
659
|
app = ' (installed)' \
|
pystand-1.0.dist-info/RECORD
DELETED
@@ -1,6 +0,0 @@
|
|
1
|
-
pystand.py,sha256=fWBjn-KMOGv-oR3Q2XYfaKE5ZtHUDXRq2Kx__mnd6Es,23997
|
2
|
-
pystand-1.0.dist-info/METADATA,sha256=_GdO43TfIj8kSEHfU2suk-xq0Dh07rKsa29Z6vIfnhA,13674
|
3
|
-
pystand-1.0.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
4
|
-
pystand-1.0.dist-info/entry_points.txt,sha256=DG4ps3I3nni1bubV1tXs6u8FARgkdbAYaEAzZD4RAo8,41
|
5
|
-
pystand-1.0.dist-info/top_level.txt,sha256=NoWUh19UQymAJLHTCdxMnVwV6Teftef5fzyF3OWLyNY,8
|
6
|
-
pystand-1.0.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|