pystand 2.34__tar.gz → 2.35__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,11 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pystand
3
- Version: 2.34
3
+ Version: 2.35
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
- Classifier: Programming Language :: Python :: 3
10
9
  Requires-Python: >=3.8
11
10
  Description-Content-Type: text/markdown
12
11
  Requires-Dist: packaging
@@ -141,10 +140,10 @@ Type `pystand` or `pystand -h` to view the usage summary:
141
140
 
142
141
  ```
143
142
  usage: pystand [-h] [-D DISTRIBUTION] [-P PREFIX_DIR] [-C CACHE_DIR]
144
- [-M CACHE_MINUTES] [--purge-days PURGE_DAYS]
145
- [--github-access-token GITHUB_ACCESS_TOKEN] [--no-strip]
146
- [--no-color] [--cert {system,certifi,none}] [-V]
147
- {install,i,update,u,up,remove,r,rm,list,l,show,s,path,p,cache,c,uv,uvx} ...
143
+ [-M CACHE_MINUTES] [--purge-days PURGE_DAYS]
144
+ [--github-access-token GITHUB_ACCESS_TOKEN] [--no-strip]
145
+ [--no-color] [--cert {system,certifi,none}] [-V]
146
+ {install,i,update,u,up,remove,r,rm,list,l,show,s,path,p,cache,c,uv,uvx} ...
148
147
 
149
148
  Command line tool to download, install, and update pre-built Python versions
150
149
  from the python-build-standalone project at https://github.com/astral-
@@ -203,14 +202,13 @@ Some commands offer aliases as shown in parentheses above. Note you can set
203
202
  default starting global options in ~/.config/pystand-flags.conf.
204
203
  ```
205
204
 
206
- Type `pystand <command> -h` to see specific help/usage for any
207
- individual command:
205
+ Type `pystand <command> -h` to see specific help/usage for any individual command:
208
206
 
209
207
  ### Command `install`
210
208
 
211
209
  ```
212
210
  usage: pystand install [-h] [-r RELEASE] [-a] [-A] [--skip] [-f] [-s]
213
- [version ...]
211
+ [version ...]
214
212
 
215
213
  Install one, more, or all versions from a python-build-standalone release.
216
214
 
@@ -239,7 +237,7 @@ aliases: i
239
237
 
240
238
  ```
241
239
  usage: pystand update [-h] [-r RELEASE] [-a] [--pre-release] [--skip] [-k]
242
- [version ...]
240
+ [version ...]
243
241
 
244
242
  Update one, more, or all versions to another release.
245
243
 
@@ -378,7 +376,7 @@ aliases: c
378
376
 
379
377
  ```
380
378
  usage: pystand uv [-h] [-p PYTHON]
381
- command [subcommand] [uv_args_for_command ...]
379
+ command [subcommand] [uv_args_for_command ...]
382
380
 
383
381
  Run a uv command using a version of python installed by pystand.
384
382
 
@@ -122,10 +122,10 @@ Type `pystand` or `pystand -h` to view the usage summary:
122
122
 
123
123
  ```
124
124
  usage: pystand [-h] [-D DISTRIBUTION] [-P PREFIX_DIR] [-C CACHE_DIR]
125
- [-M CACHE_MINUTES] [--purge-days PURGE_DAYS]
126
- [--github-access-token GITHUB_ACCESS_TOKEN] [--no-strip]
127
- [--no-color] [--cert {system,certifi,none}] [-V]
128
- {install,i,update,u,up,remove,r,rm,list,l,show,s,path,p,cache,c,uv,uvx} ...
125
+ [-M CACHE_MINUTES] [--purge-days PURGE_DAYS]
126
+ [--github-access-token GITHUB_ACCESS_TOKEN] [--no-strip]
127
+ [--no-color] [--cert {system,certifi,none}] [-V]
128
+ {install,i,update,u,up,remove,r,rm,list,l,show,s,path,p,cache,c,uv,uvx} ...
129
129
 
130
130
  Command line tool to download, install, and update pre-built Python versions
131
131
  from the python-build-standalone project at https://github.com/astral-
@@ -184,14 +184,13 @@ Some commands offer aliases as shown in parentheses above. Note you can set
184
184
  default starting global options in ~/.config/pystand-flags.conf.
185
185
  ```
186
186
 
187
- Type `pystand <command> -h` to see specific help/usage for any
188
- individual command:
187
+ Type `pystand <command> -h` to see specific help/usage for any individual command:
189
188
 
190
189
  ### Command `install`
191
190
 
192
191
  ```
193
192
  usage: pystand install [-h] [-r RELEASE] [-a] [-A] [--skip] [-f] [-s]
194
- [version ...]
193
+ [version ...]
195
194
 
196
195
  Install one, more, or all versions from a python-build-standalone release.
197
196
 
@@ -220,7 +219,7 @@ aliases: i
220
219
 
221
220
  ```
222
221
  usage: pystand update [-h] [-r RELEASE] [-a] [--pre-release] [--skip] [-k]
223
- [version ...]
222
+ [version ...]
224
223
 
225
224
  Update one, more, or all versions to another release.
226
225
 
@@ -359,7 +358,7 @@ aliases: c
359
358
 
360
359
  ```
361
360
  usage: pystand uv [-h] [-p PYTHON]
362
- command [subcommand] [uv_args_for_command ...]
361
+ command [subcommand] [uv_args_for_command ...]
363
362
 
364
363
  Run a uv command using a version of python installed by pystand.
365
364
 
@@ -15,7 +15,7 @@ upload: build
15
15
  uv-publish
16
16
 
17
17
  doc:
18
- update-readme-usage -A
18
+ update-readme-usage
19
19
 
20
20
  format:
21
21
  ruff check --select I --fix {{PYFILES}} && ruff format {{PYFILES}}
@@ -9,9 +9,6 @@ readme = "README.md"
9
9
  license = "GPL-3.0-or-later"
10
10
  requires-python = ">=3.8"
11
11
  keywords = ["python-build-standalone", "pyenv", "hatch", "pdm"]
12
- classifiers = [
13
- "Programming Language :: Python :: 3",
14
- ]
15
12
  dynamic = ["version"]
16
13
  dependencies = [
17
14
  "packaging",
@@ -36,17 +33,10 @@ pystand = "pystand:main"
36
33
  [tool.setuptools_scm]
37
34
  version_scheme = "post-release"
38
35
 
39
- [tool.mypy]
40
- implicit_optional = true
41
- warn_no_return = false
42
- allow_untyped_globals = true
43
- allow_redefinition = true
44
- ignore_missing_imports = true
45
-
46
36
  [tool.ruff.format]
47
37
  quote-style = "single"
48
38
 
49
39
  [tool.ruff.lint]
50
- ignore = ["SIM115", "SIM905", "BLE001", "PLW1510", "S110"]
40
+ ignore = ["SIM115", "SIM905", "BLE001", "PLW1510", "S110", "DTZ", "C408"]
51
41
 
52
42
  # vim:se sw=2:
@@ -1,12 +1,11 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pystand
3
- Version: 2.34
3
+ Version: 2.35
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
- Classifier: Programming Language :: Python :: 3
10
9
  Requires-Python: >=3.8
11
10
  Description-Content-Type: text/markdown
12
11
  Requires-Dist: packaging
@@ -141,10 +140,10 @@ Type `pystand` or `pystand -h` to view the usage summary:
141
140
 
142
141
  ```
143
142
  usage: pystand [-h] [-D DISTRIBUTION] [-P PREFIX_DIR] [-C CACHE_DIR]
144
- [-M CACHE_MINUTES] [--purge-days PURGE_DAYS]
145
- [--github-access-token GITHUB_ACCESS_TOKEN] [--no-strip]
146
- [--no-color] [--cert {system,certifi,none}] [-V]
147
- {install,i,update,u,up,remove,r,rm,list,l,show,s,path,p,cache,c,uv,uvx} ...
143
+ [-M CACHE_MINUTES] [--purge-days PURGE_DAYS]
144
+ [--github-access-token GITHUB_ACCESS_TOKEN] [--no-strip]
145
+ [--no-color] [--cert {system,certifi,none}] [-V]
146
+ {install,i,update,u,up,remove,r,rm,list,l,show,s,path,p,cache,c,uv,uvx} ...
148
147
 
149
148
  Command line tool to download, install, and update pre-built Python versions
150
149
  from the python-build-standalone project at https://github.com/astral-
@@ -203,14 +202,13 @@ Some commands offer aliases as shown in parentheses above. Note you can set
203
202
  default starting global options in ~/.config/pystand-flags.conf.
204
203
  ```
205
204
 
206
- Type `pystand <command> -h` to see specific help/usage for any
207
- individual command:
205
+ Type `pystand <command> -h` to see specific help/usage for any individual command:
208
206
 
209
207
  ### Command `install`
210
208
 
211
209
  ```
212
210
  usage: pystand install [-h] [-r RELEASE] [-a] [-A] [--skip] [-f] [-s]
213
- [version ...]
211
+ [version ...]
214
212
 
215
213
  Install one, more, or all versions from a python-build-standalone release.
216
214
 
@@ -239,7 +237,7 @@ aliases: i
239
237
 
240
238
  ```
241
239
  usage: pystand update [-h] [-r RELEASE] [-a] [--pre-release] [--skip] [-k]
242
- [version ...]
240
+ [version ...]
243
241
 
244
242
  Update one, more, or all versions to another release.
245
243
 
@@ -378,7 +376,7 @@ aliases: c
378
376
 
379
377
  ```
380
378
  usage: pystand uv [-h] [-p PYTHON]
381
- command [subcommand] [uv_args_for_command ...]
379
+ command [subcommand] [uv_args_for_command ...]
382
380
 
383
381
  Run a uv command using a version of python installed by pystand.
384
382
 
@@ -0,0 +1,8 @@
1
+ {
2
+ "tag": "2.35",
3
+ "distance": 0,
4
+ "node": "gf90437d7f2e68e78d07fcefc226c22bbc3c067be",
5
+ "dirty": false,
6
+ "branch": "main",
7
+ "node_date": "2026-09-04"
8
+ }
@@ -447,7 +447,10 @@ def add_file(files: dict[str, Any], tag: str, name: str, url: str) -> None:
447
447
  else:
448
448
  return
449
449
 
450
- impl, ver, arch = name.split('-', 2)
450
+ try:
451
+ impl, ver, arch = name.split('-', 2)
452
+ except ValueError:
453
+ return
451
454
 
452
455
  # Modern releases have a '+' in the name to separate the version
453
456
  if '+' in ver:
@@ -591,7 +594,7 @@ def show_list(args: Namespace) -> None:
591
594
  releases = fetch_tags(args)
592
595
  cached = {p.name for p in args._releases.iterdir()}
593
596
  for release in sorted(cached.union(releases)):
594
- if args.re_match and not re.search(args.re_match, release):
597
+ if args._re_match and not args._re_match.search(release):
595
598
  continue
596
599
 
597
600
  # Ignore any bogus releases that don't parse as versions
@@ -621,16 +624,16 @@ def show_list(args: Namespace) -> None:
621
624
  print(f'{release} {dts}{app}{pre}')
622
625
 
623
626
 
624
- def get_title(desc: str) -> str:
625
- "Return single title line from description"
627
+ def get_title(desc: str, name: str) -> str:
628
+ "Return single title line from command description"
626
629
  res = []
627
630
  for line in desc.splitlines():
628
- line = line.strip()
629
- res.append(line)
630
- if line.endswith('.'):
631
- return ' '.join(res)
631
+ if line := line.strip():
632
+ res.append(line)
633
+ if line.endswith('.'):
634
+ return ' '.join(res)
632
635
 
633
- sys.exit('Must end description with a full stop.')
636
+ sys.exit(f'Must end {name} command description with a full stop.')
634
637
 
635
638
 
636
639
  def remove(args: Namespace, version: str) -> None:
@@ -642,7 +645,9 @@ def remove(args: Namespace, version: str) -> None:
642
645
  # Touch the associated release file to ensure it lives until the
643
646
  # full purge time has expired if this was the last version using it
644
647
  if release := get_json(vdir / args._data).get('release'):
645
- (args._releases / release).touch()
648
+ release_file = args._releases / release
649
+ if release_file.is_file():
650
+ release_file.touch()
646
651
 
647
652
  shutil.rmtree(vdir)
648
653
 
@@ -662,15 +667,42 @@ def strip_binaries(vdir: Path, distribution: str) -> bool:
662
667
  if not file.is_symlink() and file.is_file():
663
668
  cmd = f'strip -p --strip-unneeded {file}'.split()
664
669
  try:
665
- subprocess.run(cmd, stderr=subprocess.DEVNULL)
670
+ res = subprocess.run(cmd, stderr=subprocess.DEVNULL)
666
671
  except Exception:
667
672
  pass
668
673
  else:
669
- was_stripped = True
674
+ was_stripped |= res.returncode == 0
670
675
 
671
676
  return was_stripped
672
677
 
673
678
 
679
+ def find_python(vdir: Path) -> Path | None:
680
+ "Find the python executable in a version directory"
681
+ for path in (vdir / 'bin' / 'python', vdir / 'python.exe'):
682
+ if path.is_file():
683
+ return path
684
+
685
+ return None
686
+
687
+
688
+ def compile_bytecode(srcdir: Path, tgtdir: Path) -> str | None:
689
+ "Compile all Python bytecode in a version directory"
690
+ if not (python := find_python(srcdir)):
691
+ return f'Python executable not found in {srcdir}'
692
+
693
+ cmd = (python, '-m', 'compileall', '-qd', str(tgtdir), str(srcdir))
694
+
695
+ try:
696
+ res = subprocess.run(cmd)
697
+ except Exception as e:
698
+ return f'Compile error: {e}'
699
+
700
+ if res.returncode != 0:
701
+ return f'Compile error code: {res.returncode}'
702
+
703
+ return None
704
+
705
+
674
706
  def install(
675
707
  args: Namespace, vdir: Path, release: str, distribution: str, files: dict[str, Any]
676
708
  ) -> str | None:
@@ -694,6 +726,11 @@ def install(
694
726
 
695
727
  if error := set_json(tmpdir / args._data, data):
696
728
  error = f'Failed to write {version} data file: {error}'
729
+ elif c_error := compile_bytecode(tmpdir, vdir):
730
+ print(
731
+ f'Warning: Could not compile bytecode for {version}: {c_error}',
732
+ file=sys.stderr,
733
+ )
697
734
 
698
735
  if error:
699
736
  shutil.rmtree(tmpdir)
@@ -856,7 +893,7 @@ def main() -> str | None:
856
893
  return f'Must define a docstring for command class "{name}".'
857
894
 
858
895
  aliases = cls.aliases if hasattr(cls, 'aliases') else ()
859
- title = get_title(desc)
896
+ title = get_title(desc, name)
860
897
  cmdopt = cmd.add_parser(name, description=desc, help=title, aliases=aliases)
861
898
 
862
899
  # Set up this commands own arguments, if it has any
@@ -1277,11 +1314,19 @@ class show_:
1277
1314
 
1278
1315
  @staticmethod
1279
1316
  def run(args: Namespace) -> str | None:
1317
+ re_match: re.Pattern | None = None
1318
+ if args.re_match:
1319
+ try:
1320
+ re_match = re.compile(args.re_match)
1321
+ except re.error as error:
1322
+ args.parser.error(f'Invalid regular expression: {error}')
1323
+
1280
1324
  if args.all and args.list:
1281
1325
  args.parser.error('Can not specify --all with --list.')
1282
1326
 
1283
1327
  if args.list:
1284
1328
  args.release = False
1329
+ args._re_match = re_match
1285
1330
  show_list(args)
1286
1331
  return None
1287
1332
 
@@ -1305,9 +1350,7 @@ class show_:
1305
1350
  if distribution == args._distribution:
1306
1351
  installable = True
1307
1352
 
1308
- if not args.re_match or re.search(
1309
- args.re_match, f'{version}+{distribution}'
1310
- ):
1353
+ if not re_match or re_match.search(f'{version}+{distribution}'):
1311
1354
  print(
1312
1355
  f'{args._fmtrel(version, release)} '
1313
1356
  f'{args._fmtdist(distribution)}{app}'
@@ -1361,13 +1404,8 @@ class path_:
1361
1404
  if args.resolve:
1362
1405
  path = path.resolve()
1363
1406
 
1364
- if args.python_path:
1365
- basepath = path
1366
- path = basepath / 'bin' / 'python'
1367
- if not path.exists():
1368
- path = basepath / 'python.exe'
1369
- if not path.exists():
1370
- return f'Error: Can not find python executable in "{basepath}"'
1407
+ if args.python_path and not (path := find_python(basepath := path)):
1408
+ return f'Error: Can not find python executable in "{basepath}"'
1371
1409
 
1372
1410
  print(path)
1373
1411
 
@@ -1,8 +0,0 @@
1
- {
2
- "tag": "2.34",
3
- "distance": 0,
4
- "node": "ge147d95a4c421da907727e05ae933d512bcd48fb",
5
- "dirty": false,
6
- "branch": "main",
7
- "node_date": "2026-08-10"
8
- }
File without changes
File without changes
File without changes