spin 0.13__tar.gz → 0.14__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.
- {spin-0.13/spin.egg-info → spin-0.14}/PKG-INFO +28 -5
- {spin-0.13 → spin-0.14}/README.md +23 -0
- {spin-0.13 → spin-0.14}/pyproject.toml +2 -3
- {spin-0.13 → spin-0.14}/spin/__init__.py +1 -1
- {spin-0.13 → spin-0.14}/spin/__main__.py +14 -8
- {spin-0.13 → spin-0.14}/spin/cmds/meson.py +103 -67
- {spin-0.13 → spin-0.14}/spin/cmds/util.py +10 -2
- {spin-0.13 → spin-0.14}/spin/tests/test_build_cmds.py +25 -15
- {spin-0.13 → spin-0.14}/spin/tests/test_editable.py +3 -3
- {spin-0.13 → spin-0.14/spin.egg-info}/PKG-INFO +28 -5
- {spin-0.13 → spin-0.14}/spin.egg-info/requires.txt +1 -1
- {spin-0.13 → spin-0.14}/LICENSE +0 -0
- {spin-0.13 → spin-0.14}/setup.cfg +0 -0
- {spin-0.13 → spin-0.14}/spin/cmds/__init__.py +0 -0
- {spin-0.13 → spin-0.14}/spin/cmds/build.py +0 -0
- {spin-0.13 → spin-0.14}/spin/cmds/meta.py +0 -0
- {spin-0.13 → spin-0.14}/spin/cmds/pip.py +0 -0
- {spin-0.13 → spin-0.14}/spin/color_format.py +0 -0
- {spin-0.13 → spin-0.14}/spin/containers.py +0 -0
- {spin-0.13 → spin-0.14}/spin/sectioned_help.py +0 -0
- {spin-0.13 → spin-0.14}/spin/tests/__init__.py +0 -0
- {spin-0.13 → spin-0.14}/spin/tests/conftest.py +0 -0
- {spin-0.13 → spin-0.14}/spin/tests/test_cli.py +0 -0
- {spin-0.13 → spin-0.14}/spin/tests/test_extend_command.py +0 -0
- {spin-0.13 → spin-0.14}/spin/tests/test_meson.py +0 -0
- {spin-0.13 → spin-0.14}/spin/tests/test_test.py +0 -0
- {spin-0.13 → spin-0.14}/spin/tests/test_util.py +0 -0
- {spin-0.13 → spin-0.14}/spin/tests/testutil.py +0 -0
- {spin-0.13 → spin-0.14}/spin.egg-info/SOURCES.txt +0 -0
- {spin-0.13 → spin-0.14}/spin.egg-info/dependency_links.txt +0 -0
- {spin-0.13 → spin-0.14}/spin.egg-info/entry_points.txt +0 -0
- {spin-0.13 → spin-0.14}/spin.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: spin
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.14
|
|
4
4
|
Summary: Developer tool for scientific Python libraries
|
|
5
5
|
Maintainer-email: Scientific Python <spin@discuss.scientific-python.org>
|
|
6
6
|
License: BSD 3-Clause License
|
|
@@ -41,14 +41,13 @@ Classifier: Intended Audience :: Science/Research
|
|
|
41
41
|
Classifier: License :: OSI Approved :: BSD License
|
|
42
42
|
Classifier: Operating System :: OS Independent
|
|
43
43
|
Classifier: Programming Language :: Python :: 3
|
|
44
|
-
Classifier: Programming Language :: Python :: 3.8
|
|
45
44
|
Classifier: Programming Language :: Python :: 3.9
|
|
46
45
|
Classifier: Programming Language :: Python :: 3.10
|
|
47
46
|
Classifier: Programming Language :: Python :: 3.11
|
|
48
47
|
Classifier: Programming Language :: Python :: 3.12
|
|
49
48
|
Classifier: Programming Language :: Python :: 3.13
|
|
50
49
|
Classifier: Programming Language :: Python :: 3 :: Only
|
|
51
|
-
Requires-Python: >=3.
|
|
50
|
+
Requires-Python: >=3.9
|
|
52
51
|
Description-Content-Type: text/markdown
|
|
53
52
|
License-File: LICENSE
|
|
54
53
|
Requires-Dist: click
|
|
@@ -56,9 +55,10 @@ Requires-Dist: tomli; python_version < "3.11"
|
|
|
56
55
|
Requires-Dist: colorama; platform_system == "Windows"
|
|
57
56
|
Requires-Dist: importlib_metadata>=7
|
|
58
57
|
Provides-Extra: lint
|
|
59
|
-
Requires-Dist: pre-commit==4.0
|
|
58
|
+
Requires-Dist: pre-commit==4.2.0; extra == "lint"
|
|
60
59
|
Provides-Extra: dev
|
|
61
60
|
Requires-Dist: changelist==0.5; extra == "dev"
|
|
61
|
+
Dynamic: license-file
|
|
62
62
|
|
|
63
63
|
# 💫 Scientific Python INcantations (`spin`)
|
|
64
64
|
|
|
@@ -92,6 +92,7 @@ Most of the Meson commands listed below should work "out of the box" for those.
|
|
|
92
92
|
- [Argument overrides](#argument-overrides)
|
|
93
93
|
- [Advanced: adding arguments to built-in commands](#advanced-adding-arguments-to-built-in-commands)
|
|
94
94
|
- [Advanced: override Meson CLI](#advanced-override-meson-cli)
|
|
95
|
+
- [Auto-completion](#auto-completion)
|
|
95
96
|
- [FAQ](#faq)
|
|
96
97
|
- [For contributors](#for-contributors)
|
|
97
98
|
- [History](#history)
|
|
@@ -307,6 +308,28 @@ Meson CLI can be set in `pyproject.toml`:
|
|
|
307
308
|
cli = 'path/to/custom/meson'
|
|
308
309
|
```
|
|
309
310
|
|
|
311
|
+
## Auto-completion
|
|
312
|
+
|
|
313
|
+
To enable shell auto-completion, first install `spin`, then follow these instructions
|
|
314
|
+
(from the [click documentation](https://click.palletsprojects.com/en/stable/shell-completion/#enabling-completion)).
|
|
315
|
+
The same instructions work for ZSH, just replace "bash" with "zsh".
|
|
316
|
+
|
|
317
|
+
1. Create a completions file:
|
|
318
|
+
|
|
319
|
+
```
|
|
320
|
+
_SPIN_COMPLETE=bash_source spin > ~/.spin-complete.bash
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
Ignore the "need valid configuration" error messages.
|
|
324
|
+
|
|
325
|
+
2. In your `~/.bashrc`, add:
|
|
326
|
+
|
|
327
|
+
```
|
|
328
|
+
source ~/.spin-complete.bash
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
Auto-completions should now work in any spin-enabled project directory.
|
|
332
|
+
|
|
310
333
|
## FAQ
|
|
311
334
|
|
|
312
335
|
- Running `spin`, the emojis in the command list don't show up.
|
|
@@ -30,6 +30,7 @@ Most of the Meson commands listed below should work "out of the box" for those.
|
|
|
30
30
|
- [Argument overrides](#argument-overrides)
|
|
31
31
|
- [Advanced: adding arguments to built-in commands](#advanced-adding-arguments-to-built-in-commands)
|
|
32
32
|
- [Advanced: override Meson CLI](#advanced-override-meson-cli)
|
|
33
|
+
- [Auto-completion](#auto-completion)
|
|
33
34
|
- [FAQ](#faq)
|
|
34
35
|
- [For contributors](#for-contributors)
|
|
35
36
|
- [History](#history)
|
|
@@ -245,6 +246,28 @@ Meson CLI can be set in `pyproject.toml`:
|
|
|
245
246
|
cli = 'path/to/custom/meson'
|
|
246
247
|
```
|
|
247
248
|
|
|
249
|
+
## Auto-completion
|
|
250
|
+
|
|
251
|
+
To enable shell auto-completion, first install `spin`, then follow these instructions
|
|
252
|
+
(from the [click documentation](https://click.palletsprojects.com/en/stable/shell-completion/#enabling-completion)).
|
|
253
|
+
The same instructions work for ZSH, just replace "bash" with "zsh".
|
|
254
|
+
|
|
255
|
+
1. Create a completions file:
|
|
256
|
+
|
|
257
|
+
```
|
|
258
|
+
_SPIN_COMPLETE=bash_source spin > ~/.spin-complete.bash
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
Ignore the "need valid configuration" error messages.
|
|
262
|
+
|
|
263
|
+
2. In your `~/.bashrc`, add:
|
|
264
|
+
|
|
265
|
+
```
|
|
266
|
+
source ~/.spin-complete.bash
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
Auto-completions should now work in any spin-enabled project directory.
|
|
270
|
+
|
|
248
271
|
## FAQ
|
|
249
272
|
|
|
250
273
|
- Running `spin`, the emojis in the command list don't show up.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "spin"
|
|
3
|
-
requires-python = ">=3.
|
|
3
|
+
requires-python = ">=3.9"
|
|
4
4
|
description = "Developer tool for scientific Python libraries"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
license = {file = "LICENSE"}
|
|
@@ -15,7 +15,6 @@ classifiers = [
|
|
|
15
15
|
"License :: OSI Approved :: BSD License",
|
|
16
16
|
"Operating System :: OS Independent",
|
|
17
17
|
"Programming Language :: Python :: 3",
|
|
18
|
-
"Programming Language :: Python :: 3.8",
|
|
19
18
|
"Programming Language :: Python :: 3.9",
|
|
20
19
|
"Programming Language :: Python :: 3.10",
|
|
21
20
|
"Programming Language :: Python :: 3.11",
|
|
@@ -35,7 +34,7 @@ dynamic = ['version']
|
|
|
35
34
|
spin = "spin.__main__:main"
|
|
36
35
|
|
|
37
36
|
[project.optional-dependencies]
|
|
38
|
-
lint = ["pre-commit == 4.0
|
|
37
|
+
lint = ["pre-commit == 4.2.0"]
|
|
39
38
|
dev = ["changelist == 0.5"]
|
|
40
39
|
|
|
41
40
|
[project.urls]
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import collections
|
|
2
2
|
import importlib
|
|
3
3
|
import importlib.util
|
|
4
|
+
import inspect
|
|
4
5
|
import os
|
|
5
6
|
import pathlib
|
|
6
7
|
import sys
|
|
7
8
|
import textwrap
|
|
8
|
-
import traceback
|
|
9
9
|
from typing import Union
|
|
10
10
|
|
|
11
11
|
import click
|
|
@@ -199,20 +199,26 @@ def main():
|
|
|
199
199
|
|
|
200
200
|
try:
|
|
201
201
|
group()
|
|
202
|
-
except Exception:
|
|
203
|
-
click.secho("\n" + traceback.format_exc(limit=-3), fg="red", file=sys.stderr)
|
|
202
|
+
except Exception as e:
|
|
204
203
|
click.secho(
|
|
205
204
|
textwrap.dedent(
|
|
206
205
|
f"""\
|
|
207
|
-
An internal error has occurred. Please file a bug report at
|
|
208
206
|
|
|
209
|
-
|
|
207
|
+
{type(e).__name__}: {e}
|
|
210
208
|
|
|
211
|
-
|
|
209
|
+
This exception was raised from:
|
|
212
210
|
|
|
213
|
-
|
|
211
|
+
{inspect.trace()[-1].filename}
|
|
214
212
|
|
|
215
|
-
|
|
213
|
+
If you suspect this is a bug in `spin`, please file a report at:
|
|
214
|
+
|
|
215
|
+
https://github.com/scientific-python/spin
|
|
216
|
+
|
|
217
|
+
including the above traceback and the following information:
|
|
218
|
+
|
|
219
|
+
spin: {__version__}, package: {proj_name}
|
|
220
|
+
|
|
221
|
+
Aborting."""
|
|
216
222
|
),
|
|
217
223
|
fg="red",
|
|
218
224
|
bold=True,
|
|
@@ -236,6 +236,16 @@ if sys.platform.startswith("win"):
|
|
|
236
236
|
else:
|
|
237
237
|
DEFAULT_PREFIX = "/usr"
|
|
238
238
|
|
|
239
|
+
|
|
240
|
+
build_option = click.option(
|
|
241
|
+
"--no-build",
|
|
242
|
+
"build",
|
|
243
|
+
is_flag=True,
|
|
244
|
+
callback=lambda ctx, param, value: not value, # store opposite value in `build` var
|
|
245
|
+
default=False,
|
|
246
|
+
help="Disable building before executing command",
|
|
247
|
+
)
|
|
248
|
+
|
|
239
249
|
build_dir_option = click.option(
|
|
240
250
|
"-C",
|
|
241
251
|
"--build-dir",
|
|
@@ -283,13 +293,15 @@ def build(
|
|
|
283
293
|
quiet=False,
|
|
284
294
|
build_dir=None,
|
|
285
295
|
prefix=None,
|
|
296
|
+
meson_compile_args=(),
|
|
297
|
+
meson_install_args=(),
|
|
286
298
|
):
|
|
287
299
|
"""🔧 Build package with Meson/ninja
|
|
288
300
|
|
|
289
301
|
The package is installed to `build-install` (unless a different
|
|
290
302
|
build directory is specified with `-C`).
|
|
291
303
|
|
|
292
|
-
MESON_ARGS are passed through e.g.:
|
|
304
|
+
MESON_ARGS are passed through to `meson setup` e.g.:
|
|
293
305
|
|
|
294
306
|
spin build -- -Dpkg_config_path=/lib64/pkgconfig
|
|
295
307
|
|
|
@@ -328,12 +340,14 @@ def build(
|
|
|
328
340
|
)
|
|
329
341
|
return
|
|
330
342
|
|
|
331
|
-
|
|
343
|
+
meson_args_setup = list(meson_args)
|
|
332
344
|
|
|
333
345
|
if gcov:
|
|
334
|
-
|
|
346
|
+
meson_args_setup = meson_args_setup + ["-Db_coverage=true"]
|
|
335
347
|
|
|
336
|
-
setup_cmd =
|
|
348
|
+
setup_cmd = (
|
|
349
|
+
_meson_cli() + ["setup", build_dir, f"--prefix={prefix}"] + meson_args_setup
|
|
350
|
+
)
|
|
337
351
|
|
|
338
352
|
if clean:
|
|
339
353
|
print(f"Removing `{build_dir}`")
|
|
@@ -365,10 +379,15 @@ def build(
|
|
|
365
379
|
compile_flags += ["-j", str(jobs)]
|
|
366
380
|
|
|
367
381
|
p = _run(
|
|
368
|
-
_meson_cli()
|
|
382
|
+
_meson_cli()
|
|
383
|
+
+ ["compile"]
|
|
384
|
+
+ compile_flags
|
|
385
|
+
+ ["-C", build_dir]
|
|
386
|
+
+ list(meson_compile_args),
|
|
369
387
|
sys_exit=True,
|
|
370
388
|
output=not quiet,
|
|
371
389
|
)
|
|
390
|
+
|
|
372
391
|
p = _run(
|
|
373
392
|
_meson_cli()
|
|
374
393
|
+ [
|
|
@@ -380,7 +399,8 @@ def build(
|
|
|
380
399
|
install_dir
|
|
381
400
|
if os.path.isabs(install_dir)
|
|
382
401
|
else os.path.relpath(abs_install_dir, abs_build_dir),
|
|
383
|
-
]
|
|
402
|
+
]
|
|
403
|
+
+ list(meson_install_args),
|
|
384
404
|
output=(not quiet) and verbose,
|
|
385
405
|
)
|
|
386
406
|
|
|
@@ -437,6 +457,7 @@ Which tests to run. Can be a module, function, class, or method:
|
|
|
437
457
|
default="html",
|
|
438
458
|
help=f"Format of the gcov report. Can be one of {', '.join(e.value for e in GcovReportFormat)}.",
|
|
439
459
|
)
|
|
460
|
+
@build_option
|
|
440
461
|
@build_dir_option
|
|
441
462
|
@click.pass_context
|
|
442
463
|
def test(
|
|
@@ -449,6 +470,7 @@ def test(
|
|
|
449
470
|
coverage=False,
|
|
450
471
|
gcov=None,
|
|
451
472
|
gcov_format=None,
|
|
473
|
+
build=None,
|
|
452
474
|
build_dir=None,
|
|
453
475
|
):
|
|
454
476
|
"""🔧 Run tests
|
|
@@ -531,15 +553,16 @@ def test(
|
|
|
531
553
|
)
|
|
532
554
|
raise SystemExit(1)
|
|
533
555
|
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
556
|
+
if build:
|
|
557
|
+
build_cmd = _get_configured_command("build")
|
|
558
|
+
if build_cmd:
|
|
559
|
+
click.secho(
|
|
560
|
+
"Invoking `build` prior to running tests:", bold=True, fg="bright_green"
|
|
561
|
+
)
|
|
562
|
+
if gcov is not None:
|
|
563
|
+
ctx.invoke(build_cmd, build_dir=build_dir, gcov=bool(gcov))
|
|
564
|
+
else:
|
|
565
|
+
ctx.invoke(build_cmd, build_dir=build_dir)
|
|
543
566
|
|
|
544
567
|
site_path = _set_pythonpath(build_dir)
|
|
545
568
|
|
|
@@ -637,9 +660,10 @@ def test(
|
|
|
637
660
|
"--code", "-c", metavar="CODE", help="Python program passed in as a string"
|
|
638
661
|
)
|
|
639
662
|
@click.argument("gdb_args", nargs=-1)
|
|
663
|
+
@build_option
|
|
640
664
|
@build_dir_option
|
|
641
665
|
@click.pass_context
|
|
642
|
-
def gdb(ctx, *, code, gdb_args, build_dir):
|
|
666
|
+
def gdb(ctx, *, code, gdb_args, build=None, build_dir=None):
|
|
643
667
|
"""👾 Execute code through GDB
|
|
644
668
|
|
|
645
669
|
spin gdb -c 'import numpy as np; print(np.__version__)'
|
|
@@ -660,12 +684,13 @@ def gdb(ctx, *, code, gdb_args, build_dir):
|
|
|
660
684
|
spin gdb my_tests.py
|
|
661
685
|
spin gdb -- my_tests.py --mytest-flag
|
|
662
686
|
"""
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
687
|
+
if build:
|
|
688
|
+
build_cmd = _get_configured_command("build")
|
|
689
|
+
if build_cmd:
|
|
690
|
+
click.secho(
|
|
691
|
+
"Invoking `build` prior to invoking gdb:", bold=True, fg="bright_green"
|
|
692
|
+
)
|
|
693
|
+
ctx.invoke(build_cmd, build_dir=build_dir)
|
|
669
694
|
|
|
670
695
|
_set_pythonpath(build_dir)
|
|
671
696
|
gdb_args = list(gdb_args)
|
|
@@ -690,21 +715,25 @@ def gdb(ctx, *, code, gdb_args, build_dir):
|
|
|
690
715
|
|
|
691
716
|
@click.command()
|
|
692
717
|
@click.argument("ipython_args", nargs=-1)
|
|
718
|
+
@build_option
|
|
693
719
|
@build_dir_option
|
|
694
720
|
@click.pass_context
|
|
695
|
-
def ipython(ctx, *, ipython_args, build_dir, pre_import=""):
|
|
721
|
+
def ipython(ctx, *, ipython_args, build=None, build_dir=None, pre_import=""):
|
|
696
722
|
"""💻 Launch IPython shell with PYTHONPATH set
|
|
697
723
|
|
|
698
724
|
IPYTHON_ARGS are passed through directly to IPython, e.g.:
|
|
699
725
|
|
|
700
726
|
spin ipython -- -i myscript.py
|
|
701
727
|
"""
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
728
|
+
if build:
|
|
729
|
+
build_cmd = _get_configured_command("build")
|
|
730
|
+
if build_cmd:
|
|
731
|
+
click.secho(
|
|
732
|
+
"Invoking `build` prior to launching ipython:",
|
|
733
|
+
bold=True,
|
|
734
|
+
fg="bright_green",
|
|
735
|
+
)
|
|
736
|
+
ctx.invoke(build_cmd, build_dir=build_dir)
|
|
708
737
|
|
|
709
738
|
p = _set_pythonpath(build_dir)
|
|
710
739
|
if p:
|
|
@@ -716,9 +745,10 @@ def ipython(ctx, *, ipython_args, build_dir, pre_import=""):
|
|
|
716
745
|
|
|
717
746
|
@click.command()
|
|
718
747
|
@click.argument("shell_args", nargs=-1)
|
|
748
|
+
@build_option
|
|
719
749
|
@build_dir_option
|
|
720
750
|
@click.pass_context
|
|
721
|
-
def shell(ctx, shell_args=[], build_dir=None):
|
|
751
|
+
def shell(ctx, shell_args=[], build=None, build_dir=None):
|
|
722
752
|
"""💻 Launch shell with PYTHONPATH set
|
|
723
753
|
|
|
724
754
|
SHELL_ARGS are passed through directly to the shell, e.g.:
|
|
@@ -728,12 +758,15 @@ def shell(ctx, shell_args=[], build_dir=None):
|
|
|
728
758
|
Ensure that your shell init file (e.g., ~/.zshrc) does not override
|
|
729
759
|
the PYTHONPATH.
|
|
730
760
|
"""
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
761
|
+
if build:
|
|
762
|
+
build_cmd = _get_configured_command("build")
|
|
763
|
+
if build_cmd:
|
|
764
|
+
click.secho(
|
|
765
|
+
"Invoking `build` prior to invoking shell:",
|
|
766
|
+
bold=True,
|
|
767
|
+
fg="bright_green",
|
|
768
|
+
)
|
|
769
|
+
ctx.invoke(build_cmd, build_dir=build_dir)
|
|
737
770
|
|
|
738
771
|
p = _set_pythonpath(build_dir)
|
|
739
772
|
if p:
|
|
@@ -748,21 +781,25 @@ def shell(ctx, shell_args=[], build_dir=None):
|
|
|
748
781
|
|
|
749
782
|
@click.command()
|
|
750
783
|
@click.argument("python_args", nargs=-1)
|
|
784
|
+
@build_option
|
|
751
785
|
@build_dir_option
|
|
752
786
|
@click.pass_context
|
|
753
|
-
def python(ctx, *, python_args, build_dir):
|
|
787
|
+
def python(ctx, *, python_args, build=None, build_dir=None):
|
|
754
788
|
"""🐍 Launch Python shell with PYTHONPATH set
|
|
755
789
|
|
|
756
790
|
PYTHON_ARGS are passed through directly to Python, e.g.:
|
|
757
791
|
|
|
758
792
|
spin python -- -c 'import sys; print(sys.path)'
|
|
759
793
|
"""
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
794
|
+
if build:
|
|
795
|
+
build_cmd = _get_configured_command("build")
|
|
796
|
+
if build_cmd:
|
|
797
|
+
click.secho(
|
|
798
|
+
"Invoking `build` prior to invoking Python:",
|
|
799
|
+
bold=True,
|
|
800
|
+
fg="bright_green",
|
|
801
|
+
)
|
|
802
|
+
ctx.invoke(build_cmd, build_dir=build_dir)
|
|
766
803
|
|
|
767
804
|
p = _set_pythonpath(build_dir)
|
|
768
805
|
if p:
|
|
@@ -789,10 +826,11 @@ def python(ctx, *, python_args, build_dir):
|
|
|
789
826
|
|
|
790
827
|
|
|
791
828
|
@click.command(context_settings={"ignore_unknown_options": True})
|
|
829
|
+
@build_option
|
|
792
830
|
@build_dir_option
|
|
793
831
|
@click.argument("args", nargs=-1)
|
|
794
832
|
@click.pass_context
|
|
795
|
-
def run(ctx, *, args, build_dir=None):
|
|
833
|
+
def run(ctx, *, args, build=None, build_dir=None):
|
|
796
834
|
"""🏁 Run a shell command with PYTHONPATH set
|
|
797
835
|
|
|
798
836
|
\b
|
|
@@ -811,12 +849,13 @@ def run(ctx, *, args, build_dir=None):
|
|
|
811
849
|
if not len(args) > 0:
|
|
812
850
|
raise RuntimeError("No command given")
|
|
813
851
|
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
852
|
+
if build:
|
|
853
|
+
build_cmd = _get_configured_command("build")
|
|
854
|
+
if build_cmd:
|
|
855
|
+
# Redirect spin generated output
|
|
856
|
+
with contextlib.redirect_stdout(sys.stderr):
|
|
857
|
+
# Also ask build to be quiet
|
|
858
|
+
ctx.invoke(build_cmd, build_dir=build_dir, quiet=True)
|
|
820
859
|
|
|
821
860
|
is_posix = sys.platform in ("linux", "darwin")
|
|
822
861
|
shell = len(args) == 1
|
|
@@ -872,12 +911,6 @@ def run(ctx, *, args, build_dir=None):
|
|
|
872
911
|
default=False,
|
|
873
912
|
help="Clean previously built docs before building",
|
|
874
913
|
)
|
|
875
|
-
@click.option(
|
|
876
|
-
"--build/--no-build",
|
|
877
|
-
"first_build",
|
|
878
|
-
default=True,
|
|
879
|
-
help="Build project before generating docs",
|
|
880
|
-
)
|
|
881
914
|
@click.option(
|
|
882
915
|
"--plot/--no-plot",
|
|
883
916
|
"sphinx_gallery_plot",
|
|
@@ -891,6 +924,7 @@ def run(ctx, *, args, build_dir=None):
|
|
|
891
924
|
metavar="N_JOBS",
|
|
892
925
|
help="Number of parallel build jobs",
|
|
893
926
|
)
|
|
927
|
+
@build_option
|
|
894
928
|
@build_dir_option
|
|
895
929
|
@click.pass_context
|
|
896
930
|
def docs(
|
|
@@ -898,10 +932,10 @@ def docs(
|
|
|
898
932
|
*,
|
|
899
933
|
sphinx_target,
|
|
900
934
|
clean,
|
|
901
|
-
first_build,
|
|
902
935
|
jobs,
|
|
903
936
|
sphinx_gallery_plot,
|
|
904
937
|
clean_dirs=None,
|
|
938
|
+
build=None,
|
|
905
939
|
build_dir=None,
|
|
906
940
|
):
|
|
907
941
|
"""📖 Build Sphinx documentation
|
|
@@ -931,7 +965,7 @@ def docs(
|
|
|
931
965
|
|
|
932
966
|
if sphinx_target in ("targets", "help"):
|
|
933
967
|
clean = False
|
|
934
|
-
|
|
968
|
+
build = False
|
|
935
969
|
sphinx_target = "help"
|
|
936
970
|
|
|
937
971
|
if clean:
|
|
@@ -953,7 +987,7 @@ def docs(
|
|
|
953
987
|
|
|
954
988
|
build_cmd = _get_configured_command("build")
|
|
955
989
|
|
|
956
|
-
if build_cmd and
|
|
990
|
+
if build_cmd and build:
|
|
957
991
|
click.secho(
|
|
958
992
|
"Invoking `build` prior to building docs:", bold=True, fg="bright_green"
|
|
959
993
|
)
|
|
@@ -979,7 +1013,7 @@ def docs(
|
|
|
979
1013
|
|
|
980
1014
|
if site_path:
|
|
981
1015
|
os.environ["PYTHONPATH"] = (
|
|
982
|
-
f
|
|
1016
|
+
f"{site_path}{os.sep}:{os.environ.get('PYTHONPATH', '')}"
|
|
983
1017
|
)
|
|
984
1018
|
click.secho(
|
|
985
1019
|
f"$ export PYTHONPATH={os.environ['PYTHONPATH']}",
|
|
@@ -997,9 +1031,10 @@ def docs(
|
|
|
997
1031
|
"--code", "-c", metavar="CODE", help="Python program passed in as a string"
|
|
998
1032
|
)
|
|
999
1033
|
@click.argument("lldb_args", nargs=-1)
|
|
1034
|
+
@build_option
|
|
1000
1035
|
@build_dir_option
|
|
1001
1036
|
@click.pass_context
|
|
1002
|
-
def lldb(ctx, *, code, lldb_args, build_dir=None):
|
|
1037
|
+
def lldb(ctx, *, code, lldb_args, build=None, build_dir=None):
|
|
1003
1038
|
"""👾 Execute code through LLDB
|
|
1004
1039
|
|
|
1005
1040
|
spin lldb -c 'import numpy as np; print(np.__version__)'
|
|
@@ -1022,12 +1057,13 @@ def lldb(ctx, *, code, lldb_args, build_dir=None):
|
|
|
1022
1057
|
spin lldb -- --arch x86_64 -- my_tests.py
|
|
1023
1058
|
spin lldb -c 'import numpy as np; print(np.__version__)' -- --arch x86_64
|
|
1024
1059
|
"""
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1060
|
+
if build:
|
|
1061
|
+
build_cmd = _get_configured_command("build")
|
|
1062
|
+
if build_cmd:
|
|
1063
|
+
click.secho(
|
|
1064
|
+
"Invoking `build` prior to invoking lldb:", bold=True, fg="bright_green"
|
|
1065
|
+
)
|
|
1066
|
+
ctx.invoke(build_cmd, build_dir=build_dir)
|
|
1031
1067
|
|
|
1032
1068
|
_set_pythonpath(build_dir)
|
|
1033
1069
|
lldb_args = list(lldb_args)
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
from __future__ import (
|
|
2
|
-
annotations, # noqa: F401 # TODO: remove once only >3.
|
|
2
|
+
annotations, # noqa: F401 # TODO: remove once only >3.14 is supported
|
|
3
3
|
)
|
|
4
4
|
|
|
5
5
|
import copy
|
|
6
6
|
import os
|
|
7
7
|
import shlex
|
|
8
|
+
import shutil
|
|
8
9
|
import subprocess
|
|
9
10
|
import sys
|
|
10
11
|
from collections.abc import Callable
|
|
@@ -65,8 +66,15 @@ def run(
|
|
|
65
66
|
kwargs = {**output_kwargs, **kwargs}
|
|
66
67
|
|
|
67
68
|
if replace and (sys.platform in ("linux", "darwin")):
|
|
69
|
+
if not shutil.which(cmd[0]):
|
|
70
|
+
click.secho(
|
|
71
|
+
f"`{cmd[0]}` executable not found; exiting.",
|
|
72
|
+
fg="bright_red",
|
|
73
|
+
)
|
|
74
|
+
raise SystemExit(1) from None
|
|
75
|
+
|
|
68
76
|
os.execvp(cmd[0], cmd)
|
|
69
|
-
print(f"Failed to launch `{cmd}
|
|
77
|
+
print(f"Failed to launch `{cmd}`; exiting.")
|
|
70
78
|
sys.exit(-1)
|
|
71
79
|
else:
|
|
72
80
|
try:
|
|
@@ -14,6 +14,7 @@ from .testutil import (
|
|
|
14
14
|
skip_unless_linux,
|
|
15
15
|
skip_unless_macos,
|
|
16
16
|
spin,
|
|
17
|
+
stderr,
|
|
17
18
|
stdout,
|
|
18
19
|
)
|
|
19
20
|
|
|
@@ -23,9 +24,9 @@ def test_basic_build(example_pkg):
|
|
|
23
24
|
spin("build")
|
|
24
25
|
|
|
25
26
|
assert Path("build").exists(), "`build` folder not created after `spin build`"
|
|
26
|
-
assert Path(
|
|
27
|
-
"build-install"
|
|
28
|
-
)
|
|
27
|
+
assert Path("build-install").exists(), (
|
|
28
|
+
"`build-install` folder not created after `spin build`"
|
|
29
|
+
)
|
|
29
30
|
|
|
30
31
|
|
|
31
32
|
def test_debug_builds(example_pkg):
|
|
@@ -64,17 +65,17 @@ def test_coverage_reports(example_pkg, report_type, output_file):
|
|
|
64
65
|
spin("test", "--gcov", f"--gcov-format={report_type}")
|
|
65
66
|
|
|
66
67
|
coverage_report = Path("./build/meson-logs", output_file)
|
|
67
|
-
assert (
|
|
68
|
-
|
|
69
|
-
)
|
|
68
|
+
assert coverage_report.exists(), (
|
|
69
|
+
f"coverage report not generated for gcov build ({report_type})"
|
|
70
|
+
)
|
|
70
71
|
|
|
71
72
|
|
|
72
73
|
def test_expand_pythonpath(example_pkg):
|
|
73
74
|
"""Does an $ENV_VAR get expanded in `spin run`?"""
|
|
74
75
|
output = spin("run", "echo $PYTHONPATH")
|
|
75
|
-
assert any(
|
|
76
|
-
|
|
77
|
-
)
|
|
76
|
+
assert any(p in stdout(output) for p in ("site-packages", "dist-packages")), (
|
|
77
|
+
f"Expected value of $PYTHONPATH, got {stdout(output)} instead"
|
|
78
|
+
)
|
|
78
79
|
|
|
79
80
|
|
|
80
81
|
def test_run_stdout(example_pkg):
|
|
@@ -85,9 +86,18 @@ def test_run_stdout(example_pkg):
|
|
|
85
86
|
"-c",
|
|
86
87
|
"import sys; del sys.path[0]; import example_pkg; print(example_pkg.__version__)",
|
|
87
88
|
)
|
|
88
|
-
assert (
|
|
89
|
-
stdout(p)
|
|
90
|
-
)
|
|
89
|
+
assert stdout(p) == "0.0.0dev0", (
|
|
90
|
+
f"`spin run` stdout did not yield version, but {stdout(p)}"
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def test_run_no_build(example_pkg):
|
|
95
|
+
"""Does spin run ... --no-build correctly avoid building."""
|
|
96
|
+
output = stderr(spin("run", "echo $PYTHONPATH"))
|
|
97
|
+
assert "meson compile" in output
|
|
98
|
+
|
|
99
|
+
output = stderr(spin("run", "--no-build", "echo $PYTHONPATH"))
|
|
100
|
+
assert "meson compile" not in output
|
|
91
101
|
|
|
92
102
|
|
|
93
103
|
# Detecting whether a file is executable is not that easy on Windows,
|
|
@@ -97,9 +107,9 @@ def test_recommend_run_python(example_pkg):
|
|
|
97
107
|
"""If `spin run file.py` is called, is `spin run python file.py` recommended?"""
|
|
98
108
|
with tempfile.NamedTemporaryFile(suffix=".py") as f:
|
|
99
109
|
p = spin("run", f.name, sys_exit=False)
|
|
100
|
-
assert "Did you mean to call" in stdout(
|
|
101
|
-
|
|
102
|
-
)
|
|
110
|
+
assert "Did you mean to call" in stdout(p), (
|
|
111
|
+
"Failed to recommend `python run python file.py`"
|
|
112
|
+
)
|
|
103
113
|
|
|
104
114
|
|
|
105
115
|
def test_sdist(example_pkg):
|
|
@@ -2,9 +2,9 @@ from .testutil import spin, stdout
|
|
|
2
2
|
|
|
3
3
|
|
|
4
4
|
def test_detect_editable(example_pkg, editable_install):
|
|
5
|
-
assert "Editable install of same source detected" in stdout(
|
|
6
|
-
|
|
7
|
-
)
|
|
5
|
+
assert "Editable install of same source detected" in stdout(spin("build")), (
|
|
6
|
+
"Failed to detect and warn about editable install"
|
|
7
|
+
)
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
def test_editable_tests(example_pkg, editable_install):
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: spin
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.14
|
|
4
4
|
Summary: Developer tool for scientific Python libraries
|
|
5
5
|
Maintainer-email: Scientific Python <spin@discuss.scientific-python.org>
|
|
6
6
|
License: BSD 3-Clause License
|
|
@@ -41,14 +41,13 @@ Classifier: Intended Audience :: Science/Research
|
|
|
41
41
|
Classifier: License :: OSI Approved :: BSD License
|
|
42
42
|
Classifier: Operating System :: OS Independent
|
|
43
43
|
Classifier: Programming Language :: Python :: 3
|
|
44
|
-
Classifier: Programming Language :: Python :: 3.8
|
|
45
44
|
Classifier: Programming Language :: Python :: 3.9
|
|
46
45
|
Classifier: Programming Language :: Python :: 3.10
|
|
47
46
|
Classifier: Programming Language :: Python :: 3.11
|
|
48
47
|
Classifier: Programming Language :: Python :: 3.12
|
|
49
48
|
Classifier: Programming Language :: Python :: 3.13
|
|
50
49
|
Classifier: Programming Language :: Python :: 3 :: Only
|
|
51
|
-
Requires-Python: >=3.
|
|
50
|
+
Requires-Python: >=3.9
|
|
52
51
|
Description-Content-Type: text/markdown
|
|
53
52
|
License-File: LICENSE
|
|
54
53
|
Requires-Dist: click
|
|
@@ -56,9 +55,10 @@ Requires-Dist: tomli; python_version < "3.11"
|
|
|
56
55
|
Requires-Dist: colorama; platform_system == "Windows"
|
|
57
56
|
Requires-Dist: importlib_metadata>=7
|
|
58
57
|
Provides-Extra: lint
|
|
59
|
-
Requires-Dist: pre-commit==4.0
|
|
58
|
+
Requires-Dist: pre-commit==4.2.0; extra == "lint"
|
|
60
59
|
Provides-Extra: dev
|
|
61
60
|
Requires-Dist: changelist==0.5; extra == "dev"
|
|
61
|
+
Dynamic: license-file
|
|
62
62
|
|
|
63
63
|
# 💫 Scientific Python INcantations (`spin`)
|
|
64
64
|
|
|
@@ -92,6 +92,7 @@ Most of the Meson commands listed below should work "out of the box" for those.
|
|
|
92
92
|
- [Argument overrides](#argument-overrides)
|
|
93
93
|
- [Advanced: adding arguments to built-in commands](#advanced-adding-arguments-to-built-in-commands)
|
|
94
94
|
- [Advanced: override Meson CLI](#advanced-override-meson-cli)
|
|
95
|
+
- [Auto-completion](#auto-completion)
|
|
95
96
|
- [FAQ](#faq)
|
|
96
97
|
- [For contributors](#for-contributors)
|
|
97
98
|
- [History](#history)
|
|
@@ -307,6 +308,28 @@ Meson CLI can be set in `pyproject.toml`:
|
|
|
307
308
|
cli = 'path/to/custom/meson'
|
|
308
309
|
```
|
|
309
310
|
|
|
311
|
+
## Auto-completion
|
|
312
|
+
|
|
313
|
+
To enable shell auto-completion, first install `spin`, then follow these instructions
|
|
314
|
+
(from the [click documentation](https://click.palletsprojects.com/en/stable/shell-completion/#enabling-completion)).
|
|
315
|
+
The same instructions work for ZSH, just replace "bash" with "zsh".
|
|
316
|
+
|
|
317
|
+
1. Create a completions file:
|
|
318
|
+
|
|
319
|
+
```
|
|
320
|
+
_SPIN_COMPLETE=bash_source spin > ~/.spin-complete.bash
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
Ignore the "need valid configuration" error messages.
|
|
324
|
+
|
|
325
|
+
2. In your `~/.bashrc`, add:
|
|
326
|
+
|
|
327
|
+
```
|
|
328
|
+
source ~/.spin-complete.bash
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
Auto-completions should now work in any spin-enabled project directory.
|
|
332
|
+
|
|
310
333
|
## FAQ
|
|
311
334
|
|
|
312
335
|
- Running `spin`, the emojis in the command list don't show up.
|
{spin-0.13 → spin-0.14}/LICENSE
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|