pystand 2.36__tar.gz → 2.38__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,12 +1,12 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pystand
3
- Version: 2.36
3
+ Version: 2.38
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
7
7
  Project-URL: Homepage, https://github.com/bulletmark/pystand
8
8
  Keywords: python-build-standalone,pyenv,hatch,pdm
9
- Requires-Python: >=3.8
9
+ Requires-Python: >=3.9
10
10
  Description-Content-Type: text/markdown
11
11
  Requires-Dist: packaging
12
12
  Requires-Dist: platformdirs
@@ -247,7 +247,7 @@ positional arguments:
247
247
  options:
248
248
  -h, --help show this help message and exit
249
249
  -r, --release RELEASE
250
- update to specified YYYMMDD release (e.g. 20260807),
250
+ update to specified YYYYMMDD release (e.g. 20260807),
251
251
  default is latest release
252
252
  -a, --all update ALL versions
253
253
  --pre-release update release version to a later superceding pre-
@@ -276,8 +276,8 @@ options:
276
276
  --skip skip the specified versions when removing all (only
277
277
  can be specified with --all)
278
278
  -r, --release RELEASE
279
- only remove versions if from specified YYYMMDD release
280
- (e.g. 20260807)
279
+ only remove versions if from specified YYYYMMDD
280
+ release (e.g. 20260807)
281
281
 
282
282
  aliases: r, rm
283
283
  ```
@@ -412,7 +412,7 @@ options:
412
412
 
413
413
  ## Installation and Upgrade
414
414
 
415
- Python 3.8 or later is required. Arch Linux users can install [`pystand`
415
+ Python 3.9 or later is required. Arch Linux users can install [`pystand`
416
416
  from the AUR](https://aur.archlinux.org/packages/pystand) and skip this
417
417
  section.
418
418
 
@@ -229,7 +229,7 @@ positional arguments:
229
229
  options:
230
230
  -h, --help show this help message and exit
231
231
  -r, --release RELEASE
232
- update to specified YYYMMDD release (e.g. 20260807),
232
+ update to specified YYYYMMDD release (e.g. 20260807),
233
233
  default is latest release
234
234
  -a, --all update ALL versions
235
235
  --pre-release update release version to a later superceding pre-
@@ -258,8 +258,8 @@ options:
258
258
  --skip skip the specified versions when removing all (only
259
259
  can be specified with --all)
260
260
  -r, --release RELEASE
261
- only remove versions if from specified YYYMMDD release
262
- (e.g. 20260807)
261
+ only remove versions if from specified YYYYMMDD
262
+ release (e.g. 20260807)
263
263
 
264
264
  aliases: r, rm
265
265
  ```
@@ -394,7 +394,7 @@ options:
394
394
 
395
395
  ## Installation and Upgrade
396
396
 
397
- Python 3.8 or later is required. Arch Linux users can install [`pystand`
397
+ Python 3.9 or later is required. Arch Linux users can install [`pystand`
398
398
  from the AUR](https://aur.archlinux.org/packages/pystand) and skip this
399
399
  section.
400
400
 
@@ -7,7 +7,7 @@ name = "pystand"
7
7
  description = "Install Python versions from python-build-standalone project"
8
8
  readme = "README.md"
9
9
  license = "GPL-3.0-or-later"
10
- requires-python = ">=3.8"
10
+ requires-python = ">=3.9"
11
11
  keywords = ["python-build-standalone", "pyenv", "hatch", "pdm"]
12
12
  dynamic = ["version"]
13
13
  dependencies = [
@@ -1,12 +1,12 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pystand
3
- Version: 2.36
3
+ Version: 2.38
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
7
7
  Project-URL: Homepage, https://github.com/bulletmark/pystand
8
8
  Keywords: python-build-standalone,pyenv,hatch,pdm
9
- Requires-Python: >=3.8
9
+ Requires-Python: >=3.9
10
10
  Description-Content-Type: text/markdown
11
11
  Requires-Dist: packaging
12
12
  Requires-Dist: platformdirs
@@ -247,7 +247,7 @@ positional arguments:
247
247
  options:
248
248
  -h, --help show this help message and exit
249
249
  -r, --release RELEASE
250
- update to specified YYYMMDD release (e.g. 20260807),
250
+ update to specified YYYYMMDD release (e.g. 20260807),
251
251
  default is latest release
252
252
  -a, --all update ALL versions
253
253
  --pre-release update release version to a later superceding pre-
@@ -276,8 +276,8 @@ options:
276
276
  --skip skip the specified versions when removing all (only
277
277
  can be specified with --all)
278
278
  -r, --release RELEASE
279
- only remove versions if from specified YYYMMDD release
280
- (e.g. 20260807)
279
+ only remove versions if from specified YYYYMMDD
280
+ release (e.g. 20260807)
281
281
 
282
282
  aliases: r, rm
283
283
  ```
@@ -412,7 +412,7 @@ options:
412
412
 
413
413
  ## Installation and Upgrade
414
414
 
415
- Python 3.8 or later is required. Arch Linux users can install [`pystand`
415
+ Python 3.9 or later is required. Arch Linux users can install [`pystand`
416
416
  from the AUR](https://aur.archlinux.org/packages/pystand) and skip this
417
417
  section.
418
418
 
@@ -0,0 +1,8 @@
1
+ {
2
+ "tag": "2.38",
3
+ "distance": 0,
4
+ "node": "ge58d0185e364137af825c64bde9f335ca3f68030",
5
+ "dirty": false,
6
+ "branch": "main",
7
+ "node_date": "2026-09-13"
8
+ }
@@ -430,15 +430,18 @@ def get_release_tag(args: Namespace) -> str:
430
430
 
431
431
  return release
432
432
 
433
- if args._latest_release.exists():
434
- stat = args._latest_release.stat()
435
- if time.time() < (stat.st_mtime + int(args.cache_minutes * 60)):
436
- return args._latest_release.read_text().strip()
433
+ latest_release = args._latest_release
434
+
435
+ if latest_release.is_file() and (
436
+ not args.check_latest
437
+ or time.time() < (latest_release.stat().st_mtime + int(args.cache_minutes * 60))
438
+ ):
439
+ return latest_release.read_text().strip()
437
440
 
438
441
  if not (tag := fetch_tag_latest(args)):
439
442
  sys.exit('Latest YYYYMMDD release tag timestamp file is unavailable.')
440
443
 
441
- args._latest_release.write_text(tag + '\n')
444
+ latest_release.write_text(tag + '\n')
442
445
  return tag
443
446
 
444
447
 
@@ -563,8 +566,8 @@ def keeplist(args: Namespace) -> set[str]:
563
566
  }
564
567
 
565
568
  # Add current release to keep list (even if not currently installed)
566
- if args._latest_release.exists():
567
- keep.add(args._latest_release.read_text().strip())
569
+ if (latest_release := args._latest_release).exists():
570
+ keep.add(latest_release.read_text().strip())
568
571
 
569
572
  return keep
570
573
 
@@ -592,7 +595,7 @@ def purge_unused_releases(args: Namespace) -> None:
592
595
  rm_path(path)
593
596
 
594
597
 
595
- def show_list(args: Namespace) -> None:
598
+ def show_list(args: Namespace, re_match: re.Pattern | None) -> None:
596
599
  "Show a list of available releases"
597
600
  try:
598
601
  latest = parse_version(args._release)
@@ -601,7 +604,7 @@ def show_list(args: Namespace) -> None:
601
604
  releases = fetch_tags(args)
602
605
  cached = {p.name for p in args._releases.iterdir()}
603
606
  for release in sorted(cached.union(releases)):
604
- if args._re_match and not args._re_match.search(release):
607
+ if re_match and not re_match.search(release):
605
608
  continue
606
609
 
607
610
  # Ignore any bogus releases that don't parse as versions
@@ -644,7 +647,7 @@ def get_title(desc: str, name: str) -> str:
644
647
  sys.exit(f'Must end {name} command description with a full stop.')
645
648
 
646
649
 
647
- def remove(args: Namespace, version: str) -> None:
650
+ def remove_version(args: Namespace, version: str) -> None:
648
651
  "Remove a version"
649
652
  vdir = args._versions / version
650
653
  if not vdir.exists():
@@ -711,7 +714,7 @@ def compile_bytecode(srcdir: Path, tgtdir: Path) -> str | None:
711
714
  return None
712
715
 
713
716
 
714
- def install(
717
+ def install_version(
715
718
  args: Namespace, vdir: Path, release: str, distribution: str, files: dict[str, Any]
716
719
  ) -> str | None:
717
720
  "Install a version"
@@ -743,7 +746,7 @@ def install(
743
746
  if error:
744
747
  shutil.rmtree(tmpdir)
745
748
  else:
746
- remove(args, version)
749
+ remove_version(args, version)
747
750
  tmpdir.replace(vdir)
748
751
 
749
752
  return error
@@ -816,6 +819,10 @@ def run_uv(args: Namespace, cmd: list[str], cmdopts: list[str]) -> str | None:
816
819
  return f'Command "{shlex.join(cmd)}" failed with exit code {res.returncode}.'
817
820
 
818
821
 
822
+ # List of internal command classes, populated by @Command decorator
823
+ commands = []
824
+
825
+
819
826
  def main() -> str | None:
820
827
  "Main code"
821
828
  distro_default = DISTRIBUTIONS.get((platform.system(), platform.machine()))
@@ -889,12 +896,8 @@ def main() -> str | None:
889
896
  cmd = opt.add_subparsers(title='Commands', dest='cmdname')
890
897
 
891
898
  # Add each command ..
892
- for name in globals():
893
- if not name[0].islower() or not name.endswith('_'):
894
- continue
895
-
896
- cls = globals()[name]
897
- name = name[:-1]
899
+ for cls in commands:
900
+ name = cls.__name__.rstrip('_')
898
901
 
899
902
  if hasattr(cls, 'doc'):
900
903
  desc = cls.doc.strip()
@@ -912,7 +915,12 @@ def main() -> str | None:
912
915
  cls.init(cmdopt)
913
916
 
914
917
  # Set the function to call
915
- cmdopt.set_defaults(func=cls.run, name=name, parser=cmdopt)
918
+ cmdopt.set_defaults(
919
+ func=cls.run,
920
+ name=name,
921
+ parser=cmdopt,
922
+ check_latest=getattr(cls, 'check_latest', False),
923
+ )
916
924
 
917
925
  args = opt.parse_args()
918
926
 
@@ -966,10 +974,17 @@ def main() -> str | None:
966
974
  return result
967
975
 
968
976
 
969
- # COMMAND
970
- class install_:
977
+ def Command(command: type) -> type:
978
+ "Decorator to add given command class to list of commands"
979
+ commands.append(command)
980
+ return command
981
+
982
+
983
+ @Command
984
+ class install:
971
985
  doc = f'Install one, more, or all versions from a {REPO} release.'
972
986
  aliases = ('i',)
987
+ check_latest = True
973
988
 
974
989
  @staticmethod
975
990
  def init(parser: ArgumentParser) -> None:
@@ -1059,17 +1074,18 @@ class install_:
1059
1074
  )
1060
1075
  continue
1061
1076
 
1062
- if error := install(args, vdir, release, args._distribution, files):
1077
+ if error := install_version(args, vdir, release, args._distribution, files):
1063
1078
  return error
1064
1079
 
1065
1080
  print(f'Version {args._fmtrel(version, release)} installed.')
1066
1081
 
1067
1082
 
1068
- # COMMAND
1069
- class update_:
1083
+ @Command
1084
+ class update:
1070
1085
  "Update one, more, or all versions to another release."
1071
1086
 
1072
1087
  aliases = ('u', 'up')
1088
+ check_latest = True
1073
1089
 
1074
1090
  @staticmethod
1075
1091
  def init(parser: ArgumentParser) -> None:
@@ -1154,15 +1170,17 @@ class update_:
1154
1170
  # the update.
1155
1171
  args.include_source = (vdir / 'src').is_dir()
1156
1172
 
1157
- if error := install(args, new_vdir, release_target, distribution, files):
1173
+ if error := install_version(
1174
+ args, new_vdir, release_target, distribution, files
1175
+ ):
1158
1176
  return error
1159
1177
 
1160
1178
  if nextver != version and not args.keep:
1161
- remove(args, version)
1179
+ remove_version(args, version)
1162
1180
 
1163
1181
 
1164
- # COMMAND
1165
- class remove_:
1182
+ @Command
1183
+ class remove:
1166
1184
  "Remove/uninstall one, more, or all versions."
1167
1185
 
1168
1186
  aliases = ('r', 'rm')
@@ -1198,15 +1216,17 @@ class remove_:
1198
1216
  dfile = args._versions / version / args._data
1199
1217
  release = get_json(dfile).get('release') or '?'
1200
1218
  if not release_del or release == release_del:
1201
- remove(args, version)
1219
+ remove_version(args, version)
1202
1220
  print(f'Version {args._fmtrel(version, release)} removed.')
1203
1221
 
1204
1222
 
1205
1223
  # COMMAND
1224
+ @Command
1206
1225
  class list_:
1207
1226
  "List installed versions and show which have an update available."
1208
1227
 
1209
1228
  aliases = ('l',)
1229
+ check_latest = True
1210
1230
 
1211
1231
  @staticmethod
1212
1232
  def init(parser: ArgumentParser) -> None:
@@ -1289,7 +1309,8 @@ class list_:
1289
1309
 
1290
1310
 
1291
1311
  # COMMAND
1292
- class show_:
1312
+ @Command
1313
+ class show:
1293
1314
  doc = f"""
1294
1315
  Show versions available from a release.
1295
1316
 
@@ -1297,6 +1318,7 @@ class show_:
1297
1318
  {GITHUB_SITE}/releases.
1298
1319
  """
1299
1320
  aliases = ('s',)
1321
+ check_latest = True
1300
1322
 
1301
1323
  @staticmethod
1302
1324
  def init(parser: ArgumentParser) -> None:
@@ -1332,13 +1354,11 @@ class show_:
1332
1354
  except re.error as error:
1333
1355
  args.parser.error(f'Invalid regular expression: {error}')
1334
1356
 
1335
- if args.all and args.list:
1336
- args.parser.error('Can not specify --all with --list.')
1337
-
1338
1357
  if args.list:
1339
- args.release = False
1340
- args._re_match = re_match
1341
- show_list(args)
1358
+ if args.all:
1359
+ args.parser.error('Can not specify --all with --list.')
1360
+
1361
+ show_list(args, re_match)
1342
1362
  return None
1343
1363
 
1344
1364
  release = args._release
@@ -1373,8 +1393,8 @@ class show_:
1373
1393
  )
1374
1394
 
1375
1395
 
1376
- # COMMAND
1377
- class path_:
1396
+ @Command
1397
+ class path:
1378
1398
  "Show path prefix to installed version base directory."
1379
1399
 
1380
1400
  aliases = ('p',)
@@ -1421,8 +1441,8 @@ class path_:
1421
1441
  print(path)
1422
1442
 
1423
1443
 
1424
- # COMMAND
1425
- class cache_:
1444
+ @Command
1445
+ class cache:
1426
1446
  "Show size of release download caches."
1427
1447
 
1428
1448
  aliases = ('c',)
@@ -1522,8 +1542,8 @@ class cache_:
1522
1542
  show_cache_size(args._downloads, args)
1523
1543
 
1524
1544
 
1525
- # COMMAND
1526
- class uv_:
1545
+ @Command
1546
+ class uv:
1527
1547
  __doc__ = f'Run a uv command using a version of python installed by {PROG}.'
1528
1548
 
1529
1549
  @staticmethod
@@ -1552,8 +1572,8 @@ class uv_:
1552
1572
  return run_uv(args, cmd, args.uv_args_for_command)
1553
1573
 
1554
1574
 
1555
- # COMMAND
1556
- class uvx_:
1575
+ @Command
1576
+ class uvx:
1557
1577
  __doc__ = f'Run a program using uvx and a version of python installed by {PROG}.'
1558
1578
 
1559
1579
  @staticmethod
@@ -1,8 +0,0 @@
1
- {
2
- "tag": "2.36",
3
- "distance": 0,
4
- "node": "g4d3f67f4313887dc9ada0e74d5186f253c66c1f0",
5
- "dirty": false,
6
- "branch": "main",
7
- "node_date": "2026-09-04"
8
- }
File without changes
File without changes
File without changes
File without changes