pystand 2.10__tar.gz → 2.12__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pystand
3
- Version: 2.10
3
+ Version: 2.12
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
@@ -27,8 +27,8 @@ provided:
27
27
  |Command |Description |
28
28
  |---------|----------------------------------------------------------------------|
29
29
  |`install`|Install one or more versions from a python-build-standalone release |
30
- |`update` |Update one, more, or all versions to another release |
31
- |`remove` |Remove/uninstall one, more, or all versions |
30
+ |`update` (or `upgrade`) |Update one, more, or all versions to another release |
31
+ |`remove` (or `uninstall`) |Remove/uninstall one, more, or all versions |
32
32
  |`list` |List installed versions and show which have an update available |
33
33
  |`show` |Show versions available from a release |
34
34
  |`path` |Show path prefix to installed version base directory |
@@ -142,7 +142,7 @@ options:
142
142
  -D DISTRIBUTION, --distribution DISTRIBUTION
143
143
  python-build-standalone distribution. Default is
144
144
  "x86_64_v3-unknown-linux-gnu-install_only_stripped for
145
- this host.
145
+ this host
146
146
  -P PREFIX_DIR, --prefix-dir PREFIX_DIR
147
147
  specify prefix dir for storing versions. Default is
148
148
  "$HOME/.local/share/pystand"
@@ -10,8 +10,8 @@ provided:
10
10
  |Command |Description |
11
11
  |---------|----------------------------------------------------------------------|
12
12
  |`install`|Install one or more versions from a python-build-standalone release |
13
- |`update` |Update one, more, or all versions to another release |
14
- |`remove` |Remove/uninstall one, more, or all versions |
13
+ |`update` (or `upgrade`) |Update one, more, or all versions to another release |
14
+ |`remove` (or `uninstall`) |Remove/uninstall one, more, or all versions |
15
15
  |`list` |List installed versions and show which have an update available |
16
16
  |`show` |Show versions available from a release |
17
17
  |`path` |Show path prefix to installed version base directory |
@@ -125,7 +125,7 @@ options:
125
125
  -D DISTRIBUTION, --distribution DISTRIBUTION
126
126
  python-build-standalone distribution. Default is
127
127
  "x86_64_v3-unknown-linux-gnu-install_only_stripped for
128
- this host.
128
+ this host
129
129
  -P PREFIX_DIR, --prefix-dir PREFIX_DIR
130
130
  specify prefix dir for storing versions. Default is
131
131
  "$HOME/.local/share/pystand"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pystand
3
- Version: 2.10
3
+ Version: 2.12
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
@@ -27,8 +27,8 @@ provided:
27
27
  |Command |Description |
28
28
  |---------|----------------------------------------------------------------------|
29
29
  |`install`|Install one or more versions from a python-build-standalone release |
30
- |`update` |Update one, more, or all versions to another release |
31
- |`remove` |Remove/uninstall one, more, or all versions |
30
+ |`update` (or `upgrade`) |Update one, more, or all versions to another release |
31
+ |`remove` (or `uninstall`) |Remove/uninstall one, more, or all versions |
32
32
  |`list` |List installed versions and show which have an update available |
33
33
  |`show` |Show versions available from a release |
34
34
  |`path` |Show path prefix to installed version base directory |
@@ -142,7 +142,7 @@ options:
142
142
  -D DISTRIBUTION, --distribution DISTRIBUTION
143
143
  python-build-standalone distribution. Default is
144
144
  "x86_64_v3-unknown-linux-gnu-install_only_stripped for
145
- this host.
145
+ this host
146
146
  -P PREFIX_DIR, --prefix-dir PREFIX_DIR
147
147
  specify prefix dir for storing versions. Default is
148
148
  "$HOME/.local/share/pystand"
@@ -14,9 +14,9 @@ import shlex
14
14
  import shutil
15
15
  import sys
16
16
  import time
17
- from argparse import SUPPRESS, ArgumentParser, Namespace
17
+ from argparse import ArgumentParser, Namespace
18
18
  from collections import defaultdict
19
- from datetime import date, datetime, timedelta
19
+ from datetime import date, datetime
20
20
  from pathlib import Path
21
21
  from typing import Any, Iterable, Iterator
22
22
 
@@ -27,7 +27,8 @@ from packaging.version import parse as parse_version
27
27
  REPO = 'python-build-standalone'
28
28
  GITHUB_REPO = f'astral-sh/{REPO}'
29
29
  GITHUB_SITE = f'https://github.com/{GITHUB_REPO}'
30
- LATEST_RELEASE = f'{GITHUB_SITE}/releases.atom'
30
+ LATEST_RELEASES = f'{GITHUB_SITE}/releases.atom'
31
+ LATEST_RELEASE_TAG = f'{GITHUB_SITE}/releases/latest'
31
32
 
32
33
  # Sample release tag for documentation/usage examples
33
34
  SAMPL_RELEASE = '20240415'
@@ -279,11 +280,11 @@ def check_release_tag(release: str) -> str | None:
279
280
  # because it is much faster than using the GitHub API, and has no
280
281
  # rate-limits.
281
282
  def fetch_tags() -> Iterator[tuple[str, str]]:
282
- 'Fetch the latest release tag from the GitHub release atom feed'
283
+ 'Fetch the latest release tags from the GitHub release atom feed'
283
284
  import xml.etree.ElementTree as et
284
285
  from urllib.request import urlopen
285
286
  try:
286
- with urlopen(LATEST_RELEASE) as url:
287
+ with urlopen(LATEST_RELEASES) as url:
287
288
  data = et.parse(url).getroot()
288
289
  except Exception:
289
290
  sys.exit('Failed to fetch latest YYYYMMDD release atom file.')
@@ -295,14 +296,16 @@ def fetch_tags() -> Iterator[tuple[str, str]]:
295
296
  if tl and dt:
296
297
  yield tl, dt
297
298
 
298
- def fetch_tag_latest(args: Namespace) -> str:
299
- now = datetime.now().astimezone()
300
- for title, datestr in fetch_tags():
301
- timestamp = datetime.fromisoformat(datestr)
302
- if now - timestamp >= args._min_release_time:
303
- return title
299
+ def fetch_tag_latest() -> str:
300
+ 'Fetch the latest release tag from the GitHub'
301
+ from urllib.request import urlopen
302
+ try:
303
+ with urlopen(LATEST_RELEASE_TAG) as url:
304
+ data = url.geturl()
305
+ except Exception:
306
+ sys.exit('Failed to fetch latest YYYYMMDD release tag.')
304
307
 
305
- return ''
308
+ return data.split('/')[-1]
306
309
 
307
310
  def get_release_tag(args: Namespace) -> str:
308
311
  'Return the release tag, or latest if not specified'
@@ -317,7 +320,7 @@ def get_release_tag(args: Namespace) -> str:
317
320
  if time.time() < (stat.st_mtime + int(args.cache_minutes * 60)):
318
321
  return args._latest_release.read_text().strip()
319
322
 
320
- if not (tag := fetch_tag_latest(args)):
323
+ if not (tag := fetch_tag_latest()):
321
324
  sys.exit('Latest YYYYMMDD release tag timestamp file is unavailable.')
322
325
 
323
326
  args._latest_release.write_text(tag + '\n')
@@ -457,6 +460,30 @@ def purge_unused_releases(args: Namespace) -> None:
457
460
  if path.name not in keep:
458
461
  rm_path(path)
459
462
 
463
+ def show_list(args: Namespace) -> None:
464
+ 'Show a list of available releases'
465
+ releases = {r: d for r, d in fetch_tags()}
466
+ cached = set(p.name for p in args._releases.iterdir())
467
+ for release in sorted(cached.union(releases)):
468
+ if args.re_match and not re.search(args.re_match, release):
469
+ continue
470
+
471
+ if dt_str := releases.get(release):
472
+ dts = datetime.fromisoformat(dt_str).astimezone().isoformat(
473
+ sep='_', timespec='minutes')
474
+ else:
475
+ dts = '......................'
476
+
477
+ if release in cached:
478
+ ddir = args._downloads / release
479
+ count = len(list(ddir.iterdir())) if ddir.exists() else 0
480
+ app = f' cached + {count} downloaded files' \
481
+ if count > 0 else ' cached'
482
+ else:
483
+ app = ''
484
+
485
+ print(f'{release} {dts}{app}')
486
+
460
487
  class COMMAND:
461
488
  'Base class for all commands'
462
489
  commands = []
@@ -562,7 +589,7 @@ def main() -> str | None:
562
589
  # Set up main/global arguments
563
590
  opt.add_argument('-D', '--distribution',
564
591
  help=f'{REPO} distribution. '
565
- f'Default is "{distro_help} for this host.')
592
+ f'Default is "{distro_help} for this host')
566
593
  opt.add_argument('-P', '--prefix-dir', default=prefix_dir,
567
594
  help='specify prefix dir for storing '
568
595
  'versions. Default is "%(default)s"')
@@ -584,8 +611,6 @@ def main() -> str | None:
584
611
  help='do not strip downloaded binaries')
585
612
  opt.add_argument('-V', '--version', action='store_true',
586
613
  help=f'just show {PROG} version')
587
- opt.add_argument('--min-release-hours', default=6, type=int,
588
- help=SUPPRESS)
589
614
  cmd = opt.add_subparsers(title='Commands', dest='cmdname')
590
615
 
591
616
  # Add each command ..
@@ -654,7 +679,6 @@ def main() -> str | None:
654
679
  args._releases = cache_dir / 'releases'
655
680
  args._releases.mkdir(parents=True, exist_ok=True)
656
681
  args._latest_release = cache_dir / 'latest_release'
657
- args._min_release_time = timedelta(hours=args.min_release_hours)
658
682
 
659
683
  result = args.func(args)
660
684
  purge_unused_releases(args)
@@ -896,28 +920,18 @@ class _show(COMMAND):
896
920
  'matching this regular expression pattern')
897
921
 
898
922
  @staticmethod
899
- def run(args: Namespace) -> None:
923
+ def run(args: Namespace) -> str | None:
900
924
  if args.all and args.list:
901
925
  args.parser.error('Can not specify --all with --list.')
902
926
 
903
927
  if args.list:
904
- now = datetime.now().astimezone()
905
- for title, datestr in fetch_tags():
906
- if args.re_match and not re.search(args.re_match, title):
907
- continue
908
-
909
- tdiff = now - datetime.fromisoformat(datestr)
910
- if tdiff < args._min_release_time:
911
- print(title, '(pending)')
912
- else:
913
- print(title)
914
-
915
- return
928
+ show_list(args)
929
+ return None
916
930
 
917
931
  release = get_release_tag(args)
918
932
  files = get_release_files(args, release, 'cpython')
919
933
  if not files:
920
- sys.exit(f'Error: release "{release}" not found.')
934
+ return f'Error: release "{release}" not found.'
921
935
 
922
936
  installed = {}
923
937
  for vdir in iter_versions(args):
File without changes
File without changes
File without changes
File without changes
File without changes