Skip to content

Commit df3886c

Browse files
authored
Update to Python 3.10 as minimum (3.9 is EOL) (#304)
* Update to Python 3.10 as minimum (3.9 is EOL) Also write down Python support policy. * Use union annotation syntax that came with Python 3.10 * pipx: use correct version of Python * tests: handle paths on Windows * Print spin output for Windows debugging * Use Windows abspath prefix on Windows
1 parent fe9a3eb commit df3886c

File tree

8 files changed

+31
-17
lines changed

8 files changed

+31
-17
lines changed

.github/workflows/test.yml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,13 @@ jobs:
2020
test_spin:
2121
strategy:
2222
matrix:
23-
python_version: ["3.9", "3.10", "3.11", "3.12", "3.13", "3.14-dev"]
23+
python_version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
2424
os: [ubuntu-latest, windows-latest, macos-latest]
2525
runs-on: ${{ matrix.os }}
2626
steps:
2727
- uses: actions/checkout@v5
2828
- uses: actions/setup-python@v6
29+
id: setup-python
2930
with:
3031
python-version: ${{ matrix.python_version }}
3132
allow-prereleases: true
@@ -37,4 +38,4 @@ jobs:
3738
sudo apt-get install -y gdb lcov
3839
- name: Tests PyTest
3940
run: |
40-
pipx run nox --forcecolor -s test
41+
pipx run --python '${{ steps.setup-python.outputs.python-path }}' nox --forcecolor -s test

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,9 @@ nox -s test -- -v
297297
nox -s test -- -v spin/tests/test_meson.py
298298
```
299299

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+
300303
## History
301304

302305
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)).

example_pkg_src/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[project]
22
name = "example_pkg"
33
version = "0.0dev0"
4-
requires-python = ">=3.9"
4+
requires-python = ">=3.10"
55
description = "spin Example Package"
66

77
[build-system]

pyproject.toml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "spin"
3-
requires-python = ">=3.9"
3+
requires-python = ">=3.10" # Oldest non-EOL Python here
44
description = "Developer tool for scientific Python libraries"
55
readme = "README.md"
66
license = {file = "LICENSE"}
@@ -15,7 +15,6 @@ classifiers = [
1515
"License :: OSI Approved :: BSD License",
1616
"Operating System :: OS Independent",
1717
"Programming Language :: Python :: 3",
18-
"Programming Language :: Python :: 3.9",
1918
"Programming Language :: Python :: 3.10",
2019
"Programming Language :: Python :: 3.11",
2120
"Programming Language :: Python :: 3.12",

spin/__main__.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
import sys
77
import textwrap
88
import traceback
9-
from typing import Union
109

1110
import click
1211

@@ -31,7 +30,7 @@
3130
)
3231

3332

34-
def _detect_config_dir(path: pathlib.Path) -> Union[pathlib.Path, None]:
33+
def _detect_config_dir(path: pathlib.Path) -> pathlib.Path | None:
3534
path = path.resolve()
3635
files = os.listdir(path)
3736
if any(f in files for f in config_filenames):

spin/cmds/meson.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
import sys
99
from enum import Enum
1010
from pathlib import Path
11-
from typing import Union
1211

1312
import click
1413

@@ -36,7 +35,7 @@ def _meson_cli():
3635
return [meson_cli]
3736

3837

39-
def editable_install_path(distname: str) -> Union[str, None]:
38+
def editable_install_path(distname: str) -> str | None:
4039
"""Return path of the editable install for package `distname`.
4140
4241
If the package is not an editable install, return None.
@@ -191,15 +190,15 @@ def _get_site_packages(build_dir: str) -> str:
191190
return site_packages
192191

193192

194-
def _meson_version() -> Union[str, None]:
193+
def _meson_version() -> str | None:
195194
try:
196195
p = _run(_meson_cli() + ["--version"], output=False, echo=False)
197196
return p.stdout.decode("ascii").strip()
198197
except:
199198
return None
200199

201200

202-
def _meson_version_configured(build_dir: str) -> Union[str, None]:
201+
def _meson_version_configured(build_dir: str) -> str | None:
203202
try:
204203
meson_info_fn = os.path.join(build_dir, "meson-info", "meson-info.json")
205204
with open(meson_info_fn) as f:

spin/tests/test_build_cmds.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import subprocess
33
import sys
44
import tempfile
5-
from pathlib import Path
5+
from pathlib import Path, PureWindowsPath
66

77
import pytest
88

@@ -19,6 +19,10 @@
1919
)
2020

2121

22+
def unix_path(p: str) -> str:
23+
return PureWindowsPath(p).as_posix()
24+
25+
2226
def test_basic_build(example_pkg):
2327
"""Does the package build?"""
2428
spin("build")
@@ -39,7 +43,7 @@ def test_debug_builds(example_pkg):
3943

4044
def test_prefix_builds(example_pkg):
4145
"""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')}")
4347
assert (Path("build-install") / Path("foobar")).exists()
4448

4549

@@ -184,7 +188,7 @@ def test_parallel_builds(example_pkg):
184188
spin("build")
185189
spin("build", "-C", "parallel/build")
186190
p = spin("python", "--", "-c", "import example_pkg; print(example_pkg.__file__)")
187-
example_pkg_path = stdout(p).split("\n")[-1]
191+
example_pkg_path = unix_path(stdout(p).split("\n")[-1])
188192
p = spin(
189193
"python",
190194
"-C",
@@ -193,7 +197,7 @@ def test_parallel_builds(example_pkg):
193197
"-c",
194198
"import example_pkg; print(example_pkg.__file__)",
195199
)
196-
example_pkg_parallel_path = stdout(p).split("\n")[-1]
200+
example_pkg_parallel_path = unix_path(stdout(p).split("\n")[-1])
197201
assert "build-install" in example_pkg_path
198202
assert "parallel/build-install" in example_pkg_parallel_path
199203
assert "parallel/build-install" not in example_pkg_path

spin/tests/testutil.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,21 @@
2424

2525
def spin(*args, **user_kwargs):
2626
args = (str(el) for el in args)
27+
# Capture stdout, stderr separately
2728
default_kwargs = {
2829
"stdout": subprocess.PIPE,
2930
"stderr": subprocess.PIPE,
30-
"sys_exit": True,
31+
"sys_exit": False,
3132
}
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
3342

3443

3544
def stdout(p):

0 commit comments

Comments
 (0)