spin 0.9__tar.gz → 0.10__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: spin
3
- Version: 0.9
3
+ Version: 0.10
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
@@ -120,7 +120,7 @@ These commands will then be rendered as:
120
120
 
121
121
  ```
122
122
  Build:
123
- build 🔧 Build package with Meson/ninja and install
123
+ build 🔧 Build package with Meson/ninja
124
124
  test 🔧 Run tests
125
125
 
126
126
  Environments:
@@ -147,7 +147,7 @@ python -m spin
147
147
  Available as `spin.cmds.meson.*`.
148
148
 
149
149
  ```
150
- build 🔧 Build package with Meson/ninja and install to `build-install`
150
+ build 🔧 Build package with Meson/ninja
151
151
  ipython 💻 Launch IPython shell with PYTHONPATH set
152
152
  python 🐍 Launch Python shell with PYTHONPATH set
153
153
  shell 💻 Launch shell with PYTHONPATH set
@@ -171,9 +171,6 @@ Available as `spin.cmds.build.*`:
171
171
  `pip` allows for editable installs, another common
172
172
  development workflow.
173
173
 
174
- Note that, for now, the meson commands above do not work with an
175
- editable install of the package. We're [working on changing that](https://github.com/scientific-python/spin/pull/141).
176
-
177
174
  Available as `spin.cmds.pip.*`:
178
175
 
179
176
  ```
@@ -59,7 +59,7 @@ These commands will then be rendered as:
59
59
 
60
60
  ```
61
61
  Build:
62
- build 🔧 Build package with Meson/ninja and install
62
+ build 🔧 Build package with Meson/ninja
63
63
  test 🔧 Run tests
64
64
 
65
65
  Environments:
@@ -86,7 +86,7 @@ python -m spin
86
86
  Available as `spin.cmds.meson.*`.
87
87
 
88
88
  ```
89
- build 🔧 Build package with Meson/ninja and install to `build-install`
89
+ build 🔧 Build package with Meson/ninja
90
90
  ipython 💻 Launch IPython shell with PYTHONPATH set
91
91
  python 🐍 Launch Python shell with PYTHONPATH set
92
92
  shell 💻 Launch shell with PYTHONPATH set
@@ -110,9 +110,6 @@ Available as `spin.cmds.build.*`:
110
110
  `pip` allows for editable installs, another common
111
111
  development workflow.
112
112
 
113
- Note that, for now, the meson commands above do not work with an
114
- editable install of the package. We're [working on changing that](https://github.com/scientific-python/spin/pull/141).
115
-
116
113
  Available as `spin.cmds.pip.*`:
117
114
 
118
115
  ```
@@ -54,6 +54,8 @@ filterwarnings = [
54
54
  [tool.ruff]
55
55
  line-length = 88
56
56
  target-version = "py38"
57
+
58
+ [tool.ruff.lint]
57
59
  select = [
58
60
  "C",
59
61
  "E",
@@ -6,7 +6,7 @@ For usage information see `python -m spin`.
6
6
  Read more at https://github.com/scientific-python/spin
7
7
  """
8
8
 
9
- __version__ = "0.9"
9
+ __version__ = "0.10"
10
10
 
11
11
  from .cmds import util
12
12
 
@@ -3,6 +3,8 @@ import importlib
3
3
  import importlib.util
4
4
  import os
5
5
  import sys
6
+ import textwrap
7
+ import traceback
6
8
 
7
9
  import click
8
10
 
@@ -152,8 +154,25 @@ def main():
152
154
 
153
155
  try:
154
156
  group()
155
- except Exception as e:
156
- print(f"Error: {e}; aborting.", file=sys.stderr)
157
+ except Exception:
158
+ click.secho("\n" + traceback.format_exc(limit=-3), fg="red", file=sys.stderr)
159
+ click.secho(
160
+ textwrap.dedent(
161
+ f"""\
162
+ An internal error has occurred. Please file a bug report at
163
+
164
+ https://github.com/scientific-python/spin
165
+
166
+ including the above traceback and the following information:
167
+
168
+ spin: {__version__}, package: {proj_name}
169
+
170
+ Aborting."""
171
+ ),
172
+ fg="red",
173
+ bold=True,
174
+ file=sys.stderr,
175
+ )
157
176
  sys.exit(1)
158
177
 
159
178
 
@@ -45,7 +45,9 @@ def _editable_install_path(distname):
45
45
  except importlib_metadata.PackageNotFoundError:
46
46
  return None
47
47
 
48
- if getattr(dist.origin.dir_info, "editable", False):
48
+ if hasattr(dist.origin, "dir_info") and getattr(
49
+ dist.origin.dir_info, "editable", False
50
+ ):
49
51
  if sys.platform == "win32":
50
52
  return dist.origin.url.removeprefix("file:///")
51
53
  else:
@@ -213,9 +215,9 @@ def _check_coverage_tool_installation(coverage_type: GcovReportFormat):
213
215
 
214
216
  # Verify the tools are installed prior to the build
215
217
  p = _run(["ninja", "-C", build_dir, "-t", "targets", "all"], output=False)
216
- if f"coverage-{coverage_type.value}" not in p.stdout.decode("ascii"):
218
+ if f"coverage-{coverage_type}" not in p.stdout.decode("ascii"):
217
219
  raise click.ClickException(
218
- f"coverage-{coverage_type.value} is not supported... "
220
+ f"coverage-{coverage_type} is not supported... "
219
221
  f"Ensure the following are installed: {', '.join(requirements[coverage_type])} "
220
222
  "and rerun `spin test --gcov`"
221
223
  )
@@ -233,15 +235,17 @@ def _check_coverage_tool_installation(coverage_type: GcovReportFormat):
233
235
  help="Enable C code coverage using `gcov`. Use `spin test --gcov` to generate reports.",
234
236
  )
235
237
  @click.argument("meson_args", nargs=-1)
236
- def build(meson_args, jobs=None, clean=False, verbose=False, gcov=False, quiet=False):
237
- """🔧 Build package with Meson/ninja and install
238
+ def build(
239
+ *, meson_args, jobs=None, clean=False, verbose=False, gcov=False, quiet=False
240
+ ):
241
+ """🔧 Build package with Meson/ninja
242
+
243
+ The package is installed to `build-install`.
238
244
 
239
245
  MESON_ARGS are passed through e.g.:
240
246
 
241
247
  spin build -- -Dpkg_config_path=/lib64/pkgconfig
242
248
 
243
- The package is installed to build-install
244
-
245
249
  By default meson-python does release builds. To be able to use a debugger,
246
250
  tell meson to build in debug mode:
247
251
 
@@ -361,13 +365,14 @@ Which tests to run. Can be a module, function, class, or method:
361
365
  )
362
366
  @click.option(
363
367
  "--gcov-format",
364
- type=click.Choice(GcovReportFormat),
368
+ type=click.Choice([e.name for e in GcovReportFormat]),
365
369
  default="html",
366
370
  help=f"Format of the gcov report. Can be one of {', '.join(e.value for e in GcovReportFormat)}.",
367
371
  )
368
372
  @click.pass_context
369
373
  def test(
370
374
  ctx,
375
+ *,
371
376
  pytest_args,
372
377
  n_jobs,
373
378
  tests,
@@ -433,16 +438,20 @@ def test(
433
438
  click.secho(
434
439
  "Invoking `build` prior to running tests:", bold=True, fg="bright_green"
435
440
  )
436
- ctx.invoke(build_cmd, gcov=bool(gcov))
441
+ if gcov is not None:
442
+ ctx.invoke(build_cmd, gcov=bool(gcov))
443
+ else:
444
+ ctx.invoke(build_cmd)
437
445
 
438
446
  package = cfg.get("tool.spin.package", None)
447
+ if package is None:
448
+ print(
449
+ "Please specify `package = packagename` under `tool.spin` section of `pyproject.toml`"
450
+ )
451
+ raise SystemExit(1)
452
+
439
453
  if (not pytest_args) and (not tests):
440
- pytest_args = (package,)
441
- if pytest_args == (None,):
442
- print(
443
- "Please specify `package = packagename` under `tool.spin` section of `pyproject.toml`"
444
- )
445
- sys.exit(1)
454
+ tests = package
446
455
 
447
456
  site_path = _set_pythonpath()
448
457
  if site_path:
@@ -491,12 +500,9 @@ def test(
491
500
  else:
492
501
  cmd = ["pytest"]
493
502
 
494
- cwd = os.getcwd()
495
503
  pytest_p = _run(
496
- cmd + ([f"--rootdir={site_path}"] if site_path else []) + list(pytest_args),
497
- cwd=site_path,
504
+ cmd + list(pytest_args),
498
505
  )
499
- os.chdir(cwd)
500
506
 
501
507
  if gcov:
502
508
  # Verify the tools are present
@@ -509,7 +515,7 @@ def test(
509
515
 
510
516
  # Generate report
511
517
  click.secho(
512
- f"Generating {gcov_format.value} coverage report...",
518
+ f"Generating {gcov_format} coverage report...",
513
519
  bold=True,
514
520
  fg="bright_yellow",
515
521
  )
@@ -518,7 +524,7 @@ def test(
518
524
  "ninja",
519
525
  "-C",
520
526
  build_dir,
521
- f"coverage-{gcov_format.value.lower()}",
527
+ f"coverage-{gcov_format.lower()}",
522
528
  ],
523
529
  output=False,
524
530
  )
@@ -540,7 +546,7 @@ def test(
540
546
  @click.option("--code", "-c", help="Python program passed in as a string")
541
547
  @click.argument("gdb_args", nargs=-1)
542
548
  @click.pass_context
543
- def gdb(ctx, code, gdb_args):
549
+ def gdb(ctx, *, code, gdb_args):
544
550
  """👾 Execute code through GDB
545
551
 
546
552
  spin gdb -c 'import numpy as np; print(np.__version__)'
@@ -592,7 +598,7 @@ def gdb(ctx, code, gdb_args):
592
598
  @click.command()
593
599
  @click.argument("ipython_args", nargs=-1)
594
600
  @click.pass_context
595
- def ipython(ctx, ipython_args):
601
+ def ipython(ctx, *, ipython_args):
596
602
  """💻 Launch IPython shell with PYTHONPATH set
597
603
 
598
604
  IPYTHON_ARGS are passed through directly to IPython, e.g.:
@@ -646,7 +652,7 @@ def shell(ctx, shell_args=[]):
646
652
  @click.command()
647
653
  @click.argument("python_args", nargs=-1)
648
654
  @click.pass_context
649
- def python(ctx, python_args):
655
+ def python(ctx, *, python_args):
650
656
  """🐍 Launch Python shell with PYTHONPATH set
651
657
 
652
658
  PYTHON_ARGS are passed through directly to Python, e.g.:
@@ -687,7 +693,7 @@ def python(ctx, python_args):
687
693
  @click.command(context_settings={"ignore_unknown_options": True})
688
694
  @click.argument("args", nargs=-1)
689
695
  @click.pass_context
690
- def run(ctx, args):
696
+ def run(ctx, *, args):
691
697
  """🏁 Run a shell command with PYTHONPATH set
692
698
 
693
699
  \b
@@ -761,7 +767,16 @@ def run(ctx, args):
761
767
  )
762
768
  @click.option("--jobs", "-j", default="auto", help="Number of parallel build jobs")
763
769
  @click.pass_context
764
- def docs(ctx, sphinx_target, clean, first_build, jobs, sphinx_gallery_plot):
770
+ def docs(
771
+ ctx,
772
+ *,
773
+ sphinx_target,
774
+ clean,
775
+ first_build,
776
+ jobs,
777
+ sphinx_gallery_plot,
778
+ clean_dirs=None,
779
+ ):
765
780
  """📖 Build Sphinx documentation
766
781
 
767
782
  By default, SPHINXOPTS="-W", raising errors on warnings.
@@ -793,13 +808,18 @@ def docs(ctx, sphinx_target, clean, first_build, jobs, sphinx_gallery_plot):
793
808
  sphinx_target = "help"
794
809
 
795
810
  if clean:
796
- doc_dirs = [
797
- f"./{doc_dir}/build/",
798
- f"./{doc_dir}/source/api/",
799
- f"./{doc_dir}/source/auto_examples/",
800
- f"./{doc_dir}/source/jupyterlite_contents/",
801
- ]
802
- for target_dir in doc_dirs:
811
+ if clean_dirs is None:
812
+ clean_dirs = []
813
+ for prefix in ("", "_"):
814
+ clean_dirs += [
815
+ f"./{doc_dir}/{prefix}build/",
816
+ f"./{doc_dir}/{prefix}build/",
817
+ f"./{doc_dir}/{prefix}source/api/",
818
+ f"./{doc_dir}/{prefix}source/auto_examples/",
819
+ f"./{doc_dir}/{prefix}source/jupyterlite_contents/",
820
+ ]
821
+
822
+ for target_dir in clean_dirs:
803
823
  if os.path.isdir(target_dir):
804
824
  print(f"Removing {target_dir!r}")
805
825
  shutil.rmtree(target_dir)
@@ -844,7 +864,7 @@ def docs(ctx, sphinx_target, clean, first_build, jobs, sphinx_gallery_plot):
844
864
  @click.option("--code", "-c", help="Python program passed in as a string")
845
865
  @click.argument("lldb_args", nargs=-1)
846
866
  @click.pass_context
847
- def lldb(ctx, code, lldb_args):
867
+ def lldb(ctx, *, code, lldb_args):
848
868
  """👾 Execute code through LLDB
849
869
 
850
870
  spin lldb -c 'import numpy as np; print(np.__version__)'
@@ -5,20 +5,19 @@ from .util import run as _run
5
5
 
6
6
  @click.command()
7
7
  @click.option(
8
- "-v",
9
- "--verbose",
10
- is_flag=True,
11
- default=False,
12
- help="Print detailed build and installation output",
8
+ "--editable/--no-editable",
9
+ default=True,
10
+ help="Install in editable mode.",
13
11
  )
12
+ @click.option("-v", "--verbose", is_flag=True, help="Print detailed build output.")
14
13
  @click.option(
15
- "--editable/--no-editable",
14
+ "--verbose-import/--no-verbose-import",
16
15
  is_flag=True,
17
16
  default=True,
18
- help="Install in editable mode",
17
+ help="Meson only: importing an editable install may trigger a build. This flag determines whether to print that build's output.",
19
18
  )
20
19
  @click.argument("pip_args", nargs=-1)
21
- def install(pip_args, verbose, editable):
20
+ def install(*, pip_args, editable, verbose, verbose_import):
22
21
  """💽 Build and install package using pip.
23
22
 
24
23
  By default, the package is installed in editable mode.
@@ -34,9 +33,14 @@ def install(pip_args, verbose, editable):
34
33
  pip_args = list(pip_args)
35
34
  pip_cmd = ["pip", "install"]
36
35
  pip_args += ["--no-build-isolation"]
36
+
37
37
  if editable:
38
38
  pip_args += ["--editable"]
39
39
 
40
- pip_args = (["-v"] if verbose else []) + pip_args
40
+ if verbose_import:
41
+ pip_args = ["--config-settings=editable-verbose=true"] + pip_args
42
+
43
+ if verbose:
44
+ pip_args = ["-v"] + pip_args
41
45
 
42
46
  _run(pip_cmd + pip_args + ["."], sys_exit=False, replace=True)
@@ -2,13 +2,21 @@ import os
2
2
  import shlex
3
3
  import subprocess
4
4
  import sys
5
+ from typing import Optional
5
6
 
6
7
  import click
7
8
 
8
9
 
9
10
  def run(
10
- cmd, cwd=None, replace=False, sys_exit=True, output=True, echo=True, *args, **kwargs
11
- ):
11
+ cmd: list[str],
12
+ cwd: Optional[str] = None, # in 3.10 and up: str | None
13
+ replace: bool = False,
14
+ sys_exit: bool = True,
15
+ output: bool = True,
16
+ echo: bool = True,
17
+ *args,
18
+ **kwargs,
19
+ ) -> subprocess.CompletedProcess:
12
20
  """Run a shell command.
13
21
 
14
22
  Parameters
@@ -5,7 +5,7 @@ import tempfile
5
5
  from pathlib import Path
6
6
 
7
7
  import pytest
8
- from testutil import skip_on_windows, skip_unless_linux, spin, stdout
8
+ from testutil import skip_on_windows, skip_unless_linux, skip_unless_macos, spin, stdout
9
9
 
10
10
  from spin.cmds.util import run
11
11
 
@@ -143,3 +143,17 @@ def test_gdb():
143
143
  "--batch",
144
144
  )
145
145
  assert "hi" in stdout(p)
146
+
147
+
148
+ @skip_unless_macos
149
+ def test_lldb():
150
+ p = spin(
151
+ "lldb",
152
+ "-c",
153
+ 'import example_pkg; example_pkg.echo("hi")',
154
+ "--",
155
+ "-o",
156
+ "run",
157
+ "--batch",
158
+ )
159
+ assert "hi" in stdout(p)
@@ -13,6 +13,10 @@ skip_unless_linux = pytest.mark.skipif(
13
13
  not sys.platform.startswith("linux"), reason="Skipped; platform not Linux"
14
14
  )
15
15
 
16
+ skip_unless_macos = pytest.mark.skipif(
17
+ not sys.platform.startswith("darwin"), reason="Skipped; platform not macOS"
18
+ )
19
+
16
20
 
17
21
  def spin(*args, **user_kwargs):
18
22
  args = (str(el) for el in args)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: spin
3
- Version: 0.9
3
+ Version: 0.10
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
@@ -120,7 +120,7 @@ These commands will then be rendered as:
120
120
 
121
121
  ```
122
122
  Build:
123
- build 🔧 Build package with Meson/ninja and install
123
+ build 🔧 Build package with Meson/ninja
124
124
  test 🔧 Run tests
125
125
 
126
126
  Environments:
@@ -147,7 +147,7 @@ python -m spin
147
147
  Available as `spin.cmds.meson.*`.
148
148
 
149
149
  ```
150
- build 🔧 Build package with Meson/ninja and install to `build-install`
150
+ build 🔧 Build package with Meson/ninja
151
151
  ipython 💻 Launch IPython shell with PYTHONPATH set
152
152
  python 🐍 Launch Python shell with PYTHONPATH set
153
153
  shell 💻 Launch shell with PYTHONPATH set
@@ -171,9 +171,6 @@ Available as `spin.cmds.build.*`:
171
171
  `pip` allows for editable installs, another common
172
172
  development workflow.
173
173
 
174
- Note that, for now, the meson commands above do not work with an
175
- editable install of the package. We're [working on changing that](https://github.com/scientific-python/spin/pull/141).
176
-
177
174
  Available as `spin.cmds.pip.*`:
178
175
 
179
176
  ```
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