spin 0.14__tar.gz → 0.16__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.
Files changed (32) hide show
  1. {spin-0.14/spin.egg-info → spin-0.16}/PKG-INFO +10 -5
  2. {spin-0.14 → spin-0.16}/README.md +4 -0
  3. {spin-0.14 → spin-0.16}/pyproject.toml +5 -4
  4. {spin-0.14 → spin-0.16}/spin/__init__.py +1 -1
  5. {spin-0.14 → spin-0.16}/spin/__main__.py +12 -15
  6. {spin-0.14 → spin-0.16}/spin/cmds/build.py +12 -0
  7. {spin-0.14 → spin-0.16}/spin/cmds/meson.py +48 -23
  8. {spin-0.14 → spin-0.16}/spin/cmds/pip.py +3 -1
  9. {spin-0.14 → spin-0.16}/spin/cmds/util.py +9 -2
  10. {spin-0.14 → spin-0.16}/spin/tests/test_build_cmds.py +14 -4
  11. {spin-0.14 → spin-0.16}/spin/tests/testutil.py +11 -2
  12. {spin-0.14 → spin-0.16/spin.egg-info}/PKG-INFO +10 -5
  13. {spin-0.14 → spin-0.16}/spin.egg-info/requires.txt +5 -2
  14. {spin-0.14 → spin-0.16}/LICENSE +0 -0
  15. {spin-0.14 → spin-0.16}/setup.cfg +0 -0
  16. {spin-0.14 → spin-0.16}/spin/cmds/__init__.py +0 -0
  17. {spin-0.14 → spin-0.16}/spin/cmds/meta.py +0 -0
  18. {spin-0.14 → spin-0.16}/spin/color_format.py +0 -0
  19. {spin-0.14 → spin-0.16}/spin/containers.py +0 -0
  20. {spin-0.14 → spin-0.16}/spin/sectioned_help.py +0 -0
  21. {spin-0.14 → spin-0.16}/spin/tests/__init__.py +0 -0
  22. {spin-0.14 → spin-0.16}/spin/tests/conftest.py +0 -0
  23. {spin-0.14 → spin-0.16}/spin/tests/test_cli.py +0 -0
  24. {spin-0.14 → spin-0.16}/spin/tests/test_editable.py +0 -0
  25. {spin-0.14 → spin-0.16}/spin/tests/test_extend_command.py +0 -0
  26. {spin-0.14 → spin-0.16}/spin/tests/test_meson.py +0 -0
  27. {spin-0.14 → spin-0.16}/spin/tests/test_test.py +0 -0
  28. {spin-0.14 → spin-0.16}/spin/tests/test_util.py +0 -0
  29. {spin-0.14 → spin-0.16}/spin.egg-info/SOURCES.txt +0 -0
  30. {spin-0.14 → spin-0.16}/spin.egg-info/dependency_links.txt +0 -0
  31. {spin-0.14 → spin-0.16}/spin.egg-info/entry_points.txt +0 -0
  32. {spin-0.14 → spin-0.16}/spin.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: spin
3
- Version: 0.14
3
+ Version: 0.16
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,21 +41,22 @@ 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.9
45
44
  Classifier: Programming Language :: Python :: 3.10
46
45
  Classifier: Programming Language :: Python :: 3.11
47
46
  Classifier: Programming Language :: Python :: 3.12
48
47
  Classifier: Programming Language :: Python :: 3.13
48
+ Classifier: Programming Language :: Python :: 3.14
49
49
  Classifier: Programming Language :: Python :: 3 :: Only
50
- Requires-Python: >=3.9
50
+ Requires-Python: >=3.10
51
51
  Description-Content-Type: text/markdown
52
52
  License-File: LICENSE
53
- Requires-Dist: click
53
+ Requires-Dist: click<=8.2,>=8; python_version == "3.10"
54
+ Requires-Dist: click!=8.3.0,<8.4,>=8
54
55
  Requires-Dist: tomli; python_version < "3.11"
55
56
  Requires-Dist: colorama; platform_system == "Windows"
56
57
  Requires-Dist: importlib_metadata>=7
57
58
  Provides-Extra: lint
58
- Requires-Dist: pre-commit==4.2.0; extra == "lint"
59
+ Requires-Dist: pre-commit==4.3.0; extra == "lint"
59
60
  Provides-Extra: dev
60
61
  Requires-Dist: changelist==0.5; extra == "dev"
61
62
  Dynamic: license-file
@@ -188,6 +189,7 @@ Available as `spin.cmds.build.*`:
188
189
 
189
190
  ```
190
191
  sdist 📦 Build a source distribution in `dist/`
192
+ wheel 📦 Build a wheel distribution in `dist/`
191
193
  ```
192
194
 
193
195
  ### [pip](https://pip.pypa.io) (Package Installer for Python)
@@ -358,6 +360,9 @@ nox -s test -- -v
358
360
  nox -s test -- -v spin/tests/test_meson.py
359
361
  ```
360
362
 
363
+ `spin` takes a slightly more conservative approach than [SPEC 0](https://scientific-python.org/specs/spec-0000/), and
364
+ supports all non-EOL versions of Python.
365
+
361
366
  ## History
362
367
 
363
368
  The `dev.py` tool was [proposed for SciPy](https://github.com/scipy/scipy/issues/15489) by Ralf Gommers and [implemented](https://github.com/scipy/scipy/pull/15959) by Sayantika Banik, Eduardo Naufel Schettino, and Ralf Gommers (also see [Sayantika's blog post](https://labs.quansight.org/blog/the-evolution-of-the-scipy-developer-cli)).
@@ -126,6 +126,7 @@ Available as `spin.cmds.build.*`:
126
126
 
127
127
  ```
128
128
  sdist 📦 Build a source distribution in `dist/`
129
+ wheel 📦 Build a wheel distribution in `dist/`
129
130
  ```
130
131
 
131
132
  ### [pip](https://pip.pypa.io) (Package Installer for Python)
@@ -296,6 +297,9 @@ nox -s test -- -v
296
297
  nox -s test -- -v spin/tests/test_meson.py
297
298
  ```
298
299
 
300
+ `spin` takes a slightly more conservative approach than [SPEC 0](https://scientific-python.org/specs/spec-0000/), and
301
+ supports all non-EOL versions of Python.
302
+
299
303
  ## History
300
304
 
301
305
  The `dev.py` tool was [proposed for SciPy](https://github.com/scipy/scipy/issues/15489) by Ralf Gommers and [implemented](https://github.com/scipy/scipy/pull/15959) by Sayantika Banik, Eduardo Naufel Schettino, and Ralf Gommers (also see [Sayantika's blog post](https://labs.quansight.org/blog/the-evolution-of-the-scipy-developer-cli)).
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "spin"
3
- requires-python = ">=3.9"
3
+ requires-python = ">=3.10" # Oldest non-EOL Python here
4
4
  description = "Developer tool for scientific Python libraries"
5
5
  readme = "README.md"
6
6
  license = {file = "LICENSE"}
@@ -15,15 +15,16 @@ 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.9",
19
18
  "Programming Language :: Python :: 3.10",
20
19
  "Programming Language :: Python :: 3.11",
21
20
  "Programming Language :: Python :: 3.12",
22
21
  "Programming Language :: Python :: 3.13",
22
+ "Programming Language :: Python :: 3.14",
23
23
  "Programming Language :: Python :: 3 :: Only",
24
24
  ]
25
25
  dependencies = [
26
- "click",
26
+ "click>=8,<=8.2; python_version == '3.10'", # cannot deepcopy cmd params (specifically, Sentinel.UNSET) on 3.10
27
+ "click>=8,!=8.3.0,<8.4",
27
28
  "tomli; python_version < '3.11'",
28
29
  "colorama; platform_system == 'Windows'",
29
30
  "importlib_metadata >= 7"
@@ -34,7 +35,7 @@ dynamic = ['version']
34
35
  spin = "spin.__main__:main"
35
36
 
36
37
  [project.optional-dependencies]
37
- lint = ["pre-commit == 4.2.0"]
38
+ lint = ["pre-commit == 4.3.0"]
38
39
  dev = ["changelist == 0.5"]
39
40
 
40
41
  [project.urls]
@@ -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.14"
9
+ __version__ = "0.16"
10
10
 
11
11
  from .cmds import util
12
12
 
@@ -1,12 +1,11 @@
1
1
  import collections
2
2
  import importlib
3
3
  import importlib.util
4
- import inspect
5
4
  import os
6
5
  import pathlib
7
6
  import sys
8
7
  import textwrap
9
- from typing import Union
8
+ import traceback
10
9
 
11
10
  import click
12
11
 
@@ -31,7 +30,7 @@ config_filenames = (
31
30
  )
32
31
 
33
32
 
34
- def _detect_config_dir(path: pathlib.Path) -> Union[pathlib.Path, None]:
33
+ def _detect_config_dir(path: pathlib.Path) -> pathlib.Path | None:
35
34
  path = path.resolve()
36
35
  files = os.listdir(path)
37
36
  if any(f in files for f in config_filenames):
@@ -199,17 +198,16 @@ def main():
199
198
 
200
199
  try:
201
200
  group()
202
- except Exception as e:
201
+ except Exception:
203
202
  click.secho(
204
- textwrap.dedent(
205
- f"""\
206
-
207
- {type(e).__name__}: {e}
208
-
209
- This exception was raised from:
210
-
211
- {inspect.trace()[-1].filename}
203
+ "\n" + "".join(traceback.format_exception(*sys.exc_info(), limit=-1)),
204
+ fg="red",
205
+ bold=True,
206
+ file=sys.stderr,
207
+ )
212
208
 
209
+ click.secho(
210
+ textwrap.dedent(f"""\
213
211
  If you suspect this is a bug in `spin`, please file a report at:
214
212
 
215
213
  https://github.com/scientific-python/spin
@@ -218,9 +216,8 @@ def main():
218
216
 
219
217
  spin: {__version__}, package: {proj_name}
220
218
 
221
- Aborting."""
222
- ),
223
- fg="red",
219
+ Aborting."""),
220
+ fg="yellow",
224
221
  bold=True,
225
222
  file=sys.stderr,
226
223
  )
@@ -13,3 +13,15 @@ def sdist(pyproject_build_args):
13
13
  spin sdist -- -x -n
14
14
  """
15
15
  run(["pyproject-build", ".", "--sdist"] + list(pyproject_build_args))
16
+
17
+
18
+ @click.command()
19
+ @click.argument("pyproject-build-args", metavar="", nargs=-1)
20
+ def wheel(pyproject_build_args):
21
+ """📦 Build a wheel distribution in `dist/`
22
+
23
+ Extra arguments are passed to `pyproject-build`, e.g.
24
+
25
+ spin wheel -- -x -n
26
+ """
27
+ run(["pyproject-build", ".", "--wheel"] + list(pyproject_build_args))
@@ -35,7 +35,15 @@ def _meson_cli():
35
35
  return [meson_cli]
36
36
 
37
37
 
38
- def _editable_install_path(distname):
38
+ def editable_install_path(distname: str) -> str | None:
39
+ """Return path of the editable install for package `distname`.
40
+
41
+ If the package is not an editable install, return None.
42
+
43
+ See Also
44
+ --------
45
+ is_editable_install
46
+ """
39
47
  import importlib_metadata
40
48
 
41
49
  try:
@@ -57,16 +65,34 @@ def _editable_install_path(distname):
57
65
  return None
58
66
 
59
67
 
60
- def _is_editable_install(distname):
61
- return _editable_install_path(distname) is not None
68
+ # backward compat
69
+ _editable_install_path = editable_install_path
70
+
71
+
72
+ def is_editable_install(distname: str, verify_path: bool = False) -> bool:
73
+ """Whether or not an editable install of `distname` is present.
74
+
75
+ Parameters
76
+ ----------
77
+ `distname` : str
78
+ Name of the package. E.g., ``numpy`` or ``scikit-image``.
79
+ Not always the same as the module name (``numpy`` and
80
+ ``skimage`` for the above).
81
+ """
82
+ return editable_install_path(distname) is not None
83
+
84
+
85
+ # backward compat
86
+ _is_editable_install = is_editable_install
62
87
 
63
88
 
64
- def _is_editable_install_of_same_source(distname):
65
- editable_path = _editable_install_path(distname)
66
- return editable_path and os.path.samefile(_editable_install_path(distname), ".")
89
+ def _is_editable_install_of_same_source(distname: str) -> bool:
90
+ """Check whether the editable install was made from the current directory."""
91
+ editable_path = editable_install_path(distname)
92
+ return (editable_path is not None) and os.path.samefile(editable_path, ".")
67
93
 
68
94
 
69
- def _set_pythonpath(build_dir, quiet=False):
95
+ def _set_pythonpath(build_dir: str, quiet: bool = False) -> str:
70
96
  """Set first entry of PYTHONPATH to site packages directory.
71
97
 
72
98
  For editable installs, leave the PYTHONPATH alone.
@@ -78,12 +104,12 @@ def _set_pythonpath(build_dir, quiet=False):
78
104
  cfg = get_config()
79
105
  distname = cfg.get("project.name", None)
80
106
  if distname:
81
- if _is_editable_install(distname):
107
+ if is_editable_install(distname):
82
108
  if _is_editable_install_of_same_source(distname):
83
109
  if not (quiet):
84
110
  click.secho(
85
111
  "Editable install of same source directory detected; not setting PYTHONPATH",
86
- fg="bright_red",
112
+ fg="yellow",
87
113
  )
88
114
  return ""
89
115
  else:
@@ -114,11 +140,11 @@ def _set_pythonpath(build_dir, quiet=False):
114
140
  return site_packages
115
141
 
116
142
 
117
- def _get_install_dir(build_dir):
143
+ def _get_install_dir(build_dir: str) -> str:
118
144
  return f"{build_dir}-install"
119
145
 
120
146
 
121
- def _get_site_packages(build_dir):
147
+ def _get_site_packages(build_dir: str) -> str:
122
148
  install_dir = _get_install_dir(build_dir)
123
149
  try:
124
150
  cfg = get_config()
@@ -140,13 +166,12 @@ def _get_site_packages(build_dir):
140
166
  site_packages = None
141
167
  if any(f"python{X}." in p for p in candidate_paths):
142
168
  # We have a system that uses `python3.X/site-packages` or `python3.X/dist-packages`
143
- site_packages = [p for p in candidate_paths if f"python{X}.{Y}" in p]
144
- if len(site_packages) == 0:
169
+ site_packages_paths = [p for p in candidate_paths if f"python{X}.{Y}" in p]
170
+ if len(site_packages_paths) == 0:
145
171
  raise FileNotFoundError(
146
172
  f"No site-packages found in {install_dir} for Python {X}.{Y}"
147
173
  )
148
- else:
149
- site_packages = site_packages[0]
174
+ site_packages = site_packages_paths[0]
150
175
  else:
151
176
  # A naming scheme that does not encode the Python major/minor version is used, so return
152
177
  # whatever site-packages path was found
@@ -165,22 +190,22 @@ def _get_site_packages(build_dir):
165
190
  return site_packages
166
191
 
167
192
 
168
- def _meson_version():
193
+ def _meson_version() -> str | None:
169
194
  try:
170
195
  p = _run(_meson_cli() + ["--version"], output=False, echo=False)
171
196
  return p.stdout.decode("ascii").strip()
172
197
  except:
173
- pass
198
+ return None
174
199
 
175
200
 
176
- def _meson_version_configured(build_dir):
201
+ def _meson_version_configured(build_dir: str) -> str | None:
177
202
  try:
178
203
  meson_info_fn = os.path.join(build_dir, "meson-info", "meson-info.json")
179
204
  with open(meson_info_fn) as f:
180
205
  meson_info = json.load(f)
181
206
  return meson_info["meson_version"]["full"]
182
207
  except:
183
- pass
208
+ return None
184
209
 
185
210
 
186
211
  def _meson_coverage_configured() -> bool:
@@ -199,7 +224,7 @@ def _meson_coverage_configured() -> bool:
199
224
  return False
200
225
 
201
226
 
202
- def _check_coverage_tool_installation(coverage_type: GcovReportFormat, build_dir):
227
+ def _check_coverage_tool_installation(coverage_type: GcovReportFormat, build_dir: str):
203
228
  requirements = { # https://github.com/mesonbuild/meson/blob/6e381714c7cb15877e2bcaa304b93c212252ada3/docs/markdown/Unit-tests.md?plain=1#L49-L62
204
229
  GcovReportFormat.html: ["Gcovr/GenHTML", "lcov"],
205
230
  GcovReportFormat.xml: ["Gcovr (version 3.3 or higher)"],
@@ -336,7 +361,7 @@ def build(
336
361
  if not quiet:
337
362
  click.secho(
338
363
  "Editable install of same source detected; skipping build",
339
- fg="bright_red",
364
+ fg="yellow",
340
365
  )
341
366
  return
342
367
 
@@ -419,7 +444,7 @@ def _get_configured_command(command_name):
419
444
  metavar="N_JOBS",
420
445
  default="1",
421
446
  help=(
422
- "Number of parallel jobs for testing. Can be set to `auto` to use all cores."
447
+ "Number of parallel jobs for testing with pytest-xdist. Can be set to `auto` to use all cores."
423
448
  ),
424
449
  )
425
450
  @click.option(
@@ -487,7 +512,7 @@ def test(
487
512
 
488
513
  To run test modules, functions, classes, or methods:
489
514
 
490
- spin -t numpy.random
515
+ spin test -t numpy.random
491
516
 
492
517
  To report the durations of the N slowest tests:
493
518
 
@@ -1,3 +1,5 @@
1
+ import sys
2
+
1
3
  import click
2
4
 
3
5
  from .util import run as _run
@@ -37,7 +39,7 @@ def install(*, pip_args, editable, verbose, verbose_import):
37
39
  pip install . --no-build-isolation --editable --no-clean
38
40
  """
39
41
  pip_args = list(pip_args)
40
- pip_cmd = ["pip", "install"]
42
+ pip_cmd = [sys.executable, "-m", "pip", "install"]
41
43
  pip_args += ["--no-build-isolation"]
42
44
 
43
45
  if editable:
@@ -127,7 +127,14 @@ def extend_command(
127
127
  Command to extend.
128
128
  doc : str
129
129
  Replacement docstring.
130
- The wrapped function's docstring is also appended.
130
+ The decorated function's docstring is also appended.
131
+ This is done so that two modes of documenting are enabled:
132
+
133
+ 1. Extend original command documentation.
134
+ Do this by providing a docstring on the decorated function.
135
+ 2. Replace the original command's documentation.
136
+ Do this either by setting ``doc`` to a docstring, or by
137
+ setting ``doc=''`` and adding a docstring to the decorated function.
131
138
  remove_args : tuple of str
132
139
  List of arguments to remove from the parent command.
133
140
  These arguments can still be set explicitly by calling
@@ -168,7 +175,7 @@ def extend_command(
168
175
  return user_func(*args, parent_callback=parent_cmd, **kwargs)
169
176
 
170
177
  my_cmd.callback = click.pass_context(callback_with_parent_callback)
171
- my_cmd.callback._parent = user_func
178
+ my_cmd.callback._parent = user_func # type: ignore[attr-defined]
172
179
 
173
180
  if doc is not None:
174
181
  my_cmd.help = doc
@@ -2,7 +2,7 @@ import os
2
2
  import subprocess
3
3
  import sys
4
4
  import tempfile
5
- from pathlib import Path
5
+ from pathlib import Path, PureWindowsPath
6
6
 
7
7
  import pytest
8
8
 
@@ -19,6 +19,10 @@ from .testutil import (
19
19
  )
20
20
 
21
21
 
22
+ def unix_path(p: str) -> str:
23
+ return PureWindowsPath(p).as_posix()
24
+
25
+
22
26
  def test_basic_build(example_pkg):
23
27
  """Does the package build?"""
24
28
  spin("build")
@@ -39,7 +43,7 @@ def test_debug_builds(example_pkg):
39
43
 
40
44
  def test_prefix_builds(example_pkg):
41
45
  """does spin build --prefix create a build-install directory with the correct structure?"""
42
- spin("build", "--prefix=/foobar/")
46
+ spin("build", f"--prefix={os.path.abspath('/foobar')}")
43
47
  assert (Path("build-install") / Path("foobar")).exists()
44
48
 
45
49
 
@@ -116,6 +120,12 @@ def test_sdist(example_pkg):
116
120
  spin("sdist")
117
121
 
118
122
 
123
+ def test_wheel(example_pkg):
124
+ spin("wheel")
125
+ wheel_file_list = list(Path("dist").glob("example_pkg-0.0.dev0-*.whl"))
126
+ assert len(wheel_file_list) == 1, "Wheel file not created in dist/"
127
+
128
+
119
129
  def test_example(example_pkg):
120
130
  spin("example")
121
131
 
@@ -178,7 +188,7 @@ def test_parallel_builds(example_pkg):
178
188
  spin("build")
179
189
  spin("build", "-C", "parallel/build")
180
190
  p = spin("python", "--", "-c", "import example_pkg; print(example_pkg.__file__)")
181
- example_pkg_path = stdout(p).split("\n")[-1]
191
+ example_pkg_path = unix_path(stdout(p).split("\n")[-1])
182
192
  p = spin(
183
193
  "python",
184
194
  "-C",
@@ -187,7 +197,7 @@ def test_parallel_builds(example_pkg):
187
197
  "-c",
188
198
  "import example_pkg; print(example_pkg.__file__)",
189
199
  )
190
- example_pkg_parallel_path = stdout(p).split("\n")[-1]
200
+ example_pkg_parallel_path = unix_path(stdout(p).split("\n")[-1])
191
201
  assert "build-install" in example_pkg_path
192
202
  assert "parallel/build-install" in example_pkg_parallel_path
193
203
  assert "parallel/build-install" not in example_pkg_path
@@ -24,12 +24,21 @@ skip_py_lt_311 = pytest.mark.skipif(
24
24
 
25
25
  def spin(*args, **user_kwargs):
26
26
  args = (str(el) for el in args)
27
+ # Capture stdout, stderr separately
27
28
  default_kwargs = {
28
29
  "stdout": subprocess.PIPE,
29
30
  "stderr": subprocess.PIPE,
30
- "sys_exit": True,
31
+ "sys_exit": False,
31
32
  }
32
- return run(["spin"] + list(args), **{**default_kwargs, **user_kwargs})
33
+ p = run(["spin"] + list(args), **{**default_kwargs, **user_kwargs})
34
+ if p.returncode != 0:
35
+ print(p.stdout.decode("utf-8"), end="")
36
+ print(p.stderr.decode("utf-8"), end="")
37
+ # Exit unless the spin call explicitly asks us not to
38
+ # by setting sys_exit=False
39
+ if user_kwargs.get("sys_exit", True):
40
+ sys.exit(p.returncode)
41
+ return p
33
42
 
34
43
 
35
44
  def stdout(p):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: spin
3
- Version: 0.14
3
+ Version: 0.16
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,21 +41,22 @@ 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.9
45
44
  Classifier: Programming Language :: Python :: 3.10
46
45
  Classifier: Programming Language :: Python :: 3.11
47
46
  Classifier: Programming Language :: Python :: 3.12
48
47
  Classifier: Programming Language :: Python :: 3.13
48
+ Classifier: Programming Language :: Python :: 3.14
49
49
  Classifier: Programming Language :: Python :: 3 :: Only
50
- Requires-Python: >=3.9
50
+ Requires-Python: >=3.10
51
51
  Description-Content-Type: text/markdown
52
52
  License-File: LICENSE
53
- Requires-Dist: click
53
+ Requires-Dist: click<=8.2,>=8; python_version == "3.10"
54
+ Requires-Dist: click!=8.3.0,<8.4,>=8
54
55
  Requires-Dist: tomli; python_version < "3.11"
55
56
  Requires-Dist: colorama; platform_system == "Windows"
56
57
  Requires-Dist: importlib_metadata>=7
57
58
  Provides-Extra: lint
58
- Requires-Dist: pre-commit==4.2.0; extra == "lint"
59
+ Requires-Dist: pre-commit==4.3.0; extra == "lint"
59
60
  Provides-Extra: dev
60
61
  Requires-Dist: changelist==0.5; extra == "dev"
61
62
  Dynamic: license-file
@@ -188,6 +189,7 @@ Available as `spin.cmds.build.*`:
188
189
 
189
190
  ```
190
191
  sdist 📦 Build a source distribution in `dist/`
192
+ wheel 📦 Build a wheel distribution in `dist/`
191
193
  ```
192
194
 
193
195
  ### [pip](https://pip.pypa.io) (Package Installer for Python)
@@ -358,6 +360,9 @@ nox -s test -- -v
358
360
  nox -s test -- -v spin/tests/test_meson.py
359
361
  ```
360
362
 
363
+ `spin` takes a slightly more conservative approach than [SPEC 0](https://scientific-python.org/specs/spec-0000/), and
364
+ supports all non-EOL versions of Python.
365
+
361
366
  ## History
362
367
 
363
368
  The `dev.py` tool was [proposed for SciPy](https://github.com/scipy/scipy/issues/15489) by Ralf Gommers and [implemented](https://github.com/scipy/scipy/pull/15959) by Sayantika Banik, Eduardo Naufel Schettino, and Ralf Gommers (also see [Sayantika's blog post](https://labs.quansight.org/blog/the-evolution-of-the-scipy-developer-cli)).
@@ -1,4 +1,4 @@
1
- click
1
+ click!=8.3.0,<8.4,>=8
2
2
  importlib_metadata>=7
3
3
 
4
4
  [:platform_system == "Windows"]
@@ -7,8 +7,11 @@ colorama
7
7
  [:python_version < "3.11"]
8
8
  tomli
9
9
 
10
+ [:python_version == "3.10"]
11
+ click<=8.2,>=8
12
+
10
13
  [dev]
11
14
  changelist==0.5
12
15
 
13
16
  [lint]
14
- pre-commit==4.2.0
17
+ pre-commit==4.3.0
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