pystand 2.23__tar.gz → 2.25__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.4
2
2
  Name: pystand
3
- Version: 2.23
3
+ Version: 2.25
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
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pystand
3
- Version: 2.23
3
+ Version: 2.25
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
@@ -18,7 +18,6 @@ import sys
18
18
  import time
19
19
  from collections import defaultdict
20
20
  from collections.abc import Iterable, Iterator
21
- from dataclasses import dataclass, field
22
21
  from datetime import date, datetime
23
22
  from pathlib import Path
24
23
  from typing import Any
@@ -71,14 +70,45 @@ COLORS = [
71
70
  ]
72
71
 
73
72
 
74
- @dataclass
75
73
  class ColorTable:
76
- next: Iterator[str] = field(default_factory=lambda: itertools.cycle(COLORS[1:-1]))
77
- table: dict[str, str] = field(default_factory=dict)
74
+ "Base class for color tables"
78
75
 
76
+ def __init__(self, initval: str, no_color: bool) -> None:
77
+ self.table = None if no_color else {self.parse_key(initval): COLORS[0]}
78
+ self.next = itertools.cycle(COLORS[1:-1])
79
79
 
80
- colors_rel = ColorTable()
81
- colors_dist = ColorTable()
80
+ def get_color(self, text: str) -> str:
81
+ "Assign a new color by cycling through the available colors"
82
+ if not self.table:
83
+ return text
84
+
85
+ if not (color := self.table.get(key := self.parse_key(text))):
86
+ self.table[key] = color = next(self.next)
87
+
88
+ return f'{color}{text}{COLORS[-1]}'
89
+
90
+ def parse_key(self, text: str) -> str:
91
+ "Parse text for color table key, default is full text"
92
+ return text
93
+
94
+
95
+ class ColorRel(ColorTable):
96
+ "Return colored release version string"
97
+
98
+ def format(self, version: str, release: str) -> str:
99
+ "Return a formatted release version string"
100
+ return f'{version} @ {self.get_color(release)}'
101
+
102
+
103
+ class ColorDist(ColorTable):
104
+ "Return colored distribution string"
105
+
106
+ def format(self, dist: str) -> str:
107
+ return f'distribution="{self.get_color(dist)}"'
108
+
109
+ def parse_key(self, text: str) -> str:
110
+ "Extract key for distribution color"
111
+ return text.split('-', 1)[0]
82
112
 
83
113
 
84
114
  def is_admin() -> bool:
@@ -103,36 +133,6 @@ def get_version() -> str:
103
133
  return ver
104
134
 
105
135
 
106
- def fmtrel(version: str, release: str, args: Namespace) -> str:
107
- "Return a formatted release version string"
108
- if not args.no_color:
109
- if release == args._release:
110
- color = COLORS[0]
111
- elif not (color := colors_rel.table.get(release)):
112
- # Assign a new color for this release by cycling through the
113
- # available colors.
114
- colors_rel.table[release] = color = next(colors_rel.next)
115
-
116
- release = f'{color}{release}{COLORS[-1]}'
117
-
118
- return f'{version} @ {release}'
119
-
120
-
121
- def fmtdist(dist: str, args: Namespace) -> str:
122
- "Return a formatted distribution string"
123
- if not args.no_color:
124
- if dist == args._distribution:
125
- color = COLORS[0]
126
- elif not (color := colors_dist.table.get(dist)):
127
- # Assign a new color for this distribution by cycling through the
128
- # available colors.
129
- colors_dist.table[dist] = color = next(colors_dist.next)
130
-
131
- dist = f'{color}{dist}{COLORS[-1]}'
132
-
133
- return f'distribution="{dist}"'
134
-
135
-
136
136
  def get_json(file: Path) -> dict[str, Any]:
137
137
  from json import load
138
138
 
@@ -398,7 +398,11 @@ def fetch_tag_latest(args: Namespace) -> str:
398
398
 
399
399
  def get_release_tag(args: Namespace) -> str:
400
400
  "Return the release tag, or latest if not specified"
401
- if hasattr(args, 'release') and (release := args.release):
401
+ if (
402
+ hasattr(args, 'release')
403
+ and (release := args.release)
404
+ and isinstance(release, str)
405
+ ):
402
406
  if err := check_release_tag(release):
403
407
  sys.exit(err)
404
408
 
@@ -691,7 +695,9 @@ def show_cache_size(path: Path, args: Namespace) -> None:
691
695
  )
692
696
  total += size
693
697
  size_str = f'{size}B' if args.no_human_readable else to_human(size)
694
- name = str(spath) if spath.is_dir() else spath.name
698
+ name = (
699
+ str(spath) if spath.is_dir() else f'{spath.parent.name}{os.sep}{spath.name}'
700
+ )
695
701
  print(f'{size_str}\t{name}')
696
702
 
697
703
  if not args.no_total:
@@ -838,6 +844,7 @@ def main() -> str | None:
838
844
 
839
845
  args._implementation = 'cpython' # at the moment, only support CPython
840
846
  args._distribution = distribution
847
+ args._fmtdist = ColorDist(distribution, args.no_color).format
841
848
  args._data = f'{PROG}.json'
842
849
 
843
850
  args._versions = prefix_dir
@@ -855,6 +862,7 @@ def main() -> str | None:
855
862
  try:
856
863
  with locks[0].acquire(blocking=False), locks[1].acquire(blocking=False):
857
864
  args._release = get_release_tag(args)
865
+ args._fmtrel = ColorRel(args._release, args.no_color).format
858
866
  result = args.func(args)
859
867
  purge_unused_releases(args)
860
868
  update_version_symlinks(args)
@@ -944,7 +952,7 @@ class install_:
944
952
  for version in args.version:
945
953
  full_version = matcher.match(version)
946
954
  if not full_version:
947
- return f'Version {fmtrel(version, release, args)} not found.'
955
+ return f'Version {args._fmtrel(version, release)} not found.'
948
956
 
949
957
  version = full_version
950
958
  vdir = args._versions / version
@@ -956,7 +964,7 @@ class install_:
956
964
  if error := install(args, vdir, release, args._distribution, files):
957
965
  return error
958
966
 
959
- print(f'Version {fmtrel(version, release, args)} installed.')
967
+ print(f'Version {args._fmtrel(version, release)} installed.')
960
968
 
961
969
 
962
970
  # COMMAND
@@ -1018,9 +1026,9 @@ class update_:
1018
1026
 
1019
1027
  if nextver == version and args.keep and release:
1020
1028
  print(
1021
- f'Error: {fmtrel(version, release, args)} would not be kept '
1022
- f'if update to {fmtrel(nextver, release_target, args)} '
1023
- f'{fmtdist(distribution, args)}',
1029
+ f'Error: {args._fmtrel(version, release)} would not be kept '
1030
+ f'if update to {args._fmtrel(nextver, release_target)} '
1031
+ f'{args._fmtdist(distribution)}',
1024
1032
  file=sys.stderr,
1025
1033
  )
1026
1034
  continue
@@ -1030,9 +1038,9 @@ class update_:
1030
1038
  continue
1031
1039
 
1032
1040
  print(
1033
- f'{fmtrel(version, release, args)} updating to '
1034
- f'{fmtrel(nextver, release_target, args)} '
1035
- f'{fmtdist(distribution, args)} ..'
1041
+ f'{args._fmtrel(version, release)} updating to '
1042
+ f'{args._fmtrel(nextver, release_target)} '
1043
+ f'{args._fmtdist(distribution)} ..'
1036
1044
  )
1037
1045
 
1038
1046
  # If the source was originally included, then include it in
@@ -1084,7 +1092,7 @@ class remove_:
1084
1092
  release = get_json(dfile).get('release') or '?'
1085
1093
  if not release_del or release == release_del:
1086
1094
  remove(args, version)
1087
- print(f'Version {fmtrel(version, release, args)} removed.')
1095
+ print(f'Version {args._fmtrel(version, release)} removed.')
1088
1096
 
1089
1097
 
1090
1098
  # COMMAND
@@ -1147,7 +1155,7 @@ class list_:
1147
1155
  )
1148
1156
  app = (
1149
1157
  f' not eligible for '
1150
- f'update because {fmtrel(nextver, nrelease, args)} '
1158
+ f'update because {args._fmtrel(nextver, nrelease)} '
1151
1159
  'is already installed.'
1152
1160
  )
1153
1161
  else:
@@ -1155,19 +1163,19 @@ class list_:
1155
1163
  # this same distribution anymore
1156
1164
  if nextver and distribution in files.get(nextver, {}):
1157
1165
  upd = (
1158
- f' updatable to {fmtrel(nextver, release_target, args)}'
1166
+ f' updatable to {args._fmtrel(nextver, release_target)}'
1159
1167
  )
1160
1168
  elif args.verbose:
1161
1169
  app = (
1162
1170
  ' not eligible for update because '
1163
- f'{fmtrel(nextver, release_target, args)} does '
1164
- f'not provide {fmtdist(distribution, args)}.'
1171
+ f'{args._fmtrel(nextver, release_target)} does '
1172
+ f'not provide {args._fmtdist(distribution)}.'
1165
1173
  )
1166
1174
 
1167
1175
  if release:
1168
1176
  print(
1169
- f'{fmtrel(version, release, args)}{upd} '
1170
- f'{fmtdist(distribution, args)}{app}'
1177
+ f'{args._fmtrel(version, release)}{upd} '
1178
+ f'{args._fmtdist(distribution)}{app}'
1171
1179
  )
1172
1180
 
1173
1181
 
@@ -1239,12 +1247,12 @@ class show_:
1239
1247
  args.re_match, f'{version}+{distribution}'
1240
1248
  ):
1241
1249
  print(
1242
- f'{fmtrel(version, release, args)} '
1243
- f'{fmtdist(distribution, args)}{app}'
1250
+ f'{args._fmtrel(version, release)} '
1251
+ f'{args._fmtdist(distribution)}{app}'
1244
1252
  )
1245
1253
  if not installable:
1246
1254
  print(
1247
- f'Warning: no {fmtdist(args._distribution, args)} '
1255
+ f'Warning: no {args._fmtdist(args._distribution)} '
1248
1256
  f'versions found in release "{release}".'
1249
1257
  )
1250
1258
 
@@ -1338,7 +1346,15 @@ class cache_:
1338
1346
 
1339
1347
  elif args.release:
1340
1348
  for release in args.release:
1341
- show_cache_size(args._downloads / release, args)
1349
+ # Allow user to include cache path in release name
1350
+ path = (args._downloads / release).expanduser()
1351
+ if err := check_release_tag(path.name):
1352
+ return err
1353
+
1354
+ if not path.exists():
1355
+ return f'No cache for release {release}.'
1356
+
1357
+ show_cache_size(path, args)
1342
1358
  else:
1343
1359
  show_cache_size(args._downloads, args)
1344
1360
 
File without changes
File without changes
File without changes
File without changes
File without changes