-
Notifications
You must be signed in to change notification settings - Fork 3
195 lines (179 loc) · 8.65 KB
/
Copy pathci.yml
File metadata and controls
195 lines (179 loc) · 8.65 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
name: ci
on:
push:
branches: [master]
pull_request:
workflow_dispatch:
# Actions are pinned by commit SHA rather than by tag: a tag can be moved to point at new code,
# a SHA cannot. Dependabot keeps them current.
permissions:
contents: read
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
lint:
name: lint and type-check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
with:
enable-cache: true
# Pinned deliberately. pandas-stubs requires >=3.11, so on 3.10 pandas is untyped and
# mypy reports dozens of errors that say nothing about the code. A type-check gate must
# not depend on whichever interpreter the runner happens to default to.
python-version: "3.12"
# Named rather than --all-extras: the `analysis` extra holds review tools (bandit, pip-audit,
# pylint) that no CI job runs, so installing them here would be dead weight on every matrix
# entry. `examples` is needed because tests/test_end_to_end.py imports matplotlib.
- run: uv sync --extra dev --extra docs --extra examples --extra cli
- name: ruff check
run: uv run ruff check --output-format=github .
- name: ruff format
run: uv run ruff format --check .
- name: mypy
run: uv run mypy
test:
name: test (py${{ matrix.python-version }})
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
python-version: ["3.10", "3.11", "3.12", "3.13"]
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
with:
enable-cache: true
python-version: ${{ matrix.python-version }}
# Named rather than --all-extras: the `analysis` extra holds review tools (bandit, pip-audit,
# pylint) that no CI job runs, so installing them here would be dead weight on every matrix
# entry. `examples` is needed because tests/test_end_to_end.py imports matplotlib.
- run: uv sync --extra dev --extra docs --extra examples --extra cli
- name: pytest
run: uv run pytest --cov --cov-report=term-missing --cov-report=xml
fast-extra:
name: the accelerated path
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
with:
enable-cache: true
python-version: "3.12"
# numba is NOT a declared extra of this package, deliberately: uv resolves every extra into
# one lockfile, so declaring it would cap NumPy below 2.5 for the whole project and the test
# matrix would then run against an older NumPy than the package releases against. It is
# installed ad hoc here instead, which is also how a user installs it.
- run: uv sync --extra dev
# These must PASS, not skip. tests/test_numba_kernel.py is guarded by numba being importable,
# so without this job it would skip everywhere and the second implementation of the hottest
# code in the package would go unchecked.
- name: the differential tests run rather than skip
run: |
uv run --with "numba<0.67" pytest tests/test_numba_kernel.py -v --no-header | tee result.txt
grep -q PASSED result.txt
if grep -q SKIPPED result.txt; then echo "numba tests skipped; numba did not install"; exit 1; fi
- name: the whole suite still passes with numba present
run: uv run --with "numba<0.67" pytest -q
no-extras:
name: numpy only, no extras
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
with:
enable-cache: true
python-version: "3.12"
# The claim the package leads with, checked rather than asserted: a plain install pulls NumPy
# and nothing else, and it resolves the *current* NumPy rather than the one numba caps us to.
- name: a plain install is numpy and nothing else
run: |
uv venv /tmp/plain
VIRTUAL_ENV=/tmp/plain uv pip install .
VIRTUAL_ENV=/tmp/plain uv pip list --format=freeze | grep -v '^python-som==' > installed.txt
cat installed.txt
test "$(wc -l < installed.txt)" -eq 1
grep -q '^numpy==2\.' installed.txt
/tmp/plain/bin/python -c "
import sys, numpy as np, python_som
som = python_som.SOM(x=8, y=8, input_len=3, random_seed=0)
som.train(np.random.default_rng(0).normal(size=(30, 3)), n_iteration=5, mode='batch')
assert 'numba' not in sys.modules
print('numpy-only install OK, numpy', np.__version__)
"
benchmarks:
name: benchmarks still run
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
with:
enable-cache: true
python-version: "3.12"
- run: uv sync --extra dev --extra bench
# Timings are deliberately NOT asserted on. A shared runner cannot measure anything: an
# earlier version of benchmarks/bench_vs_minisom.py swung a comparison from 2.56x to 1.01x
# between two runs on an *idle* machine. What this job protects is that the benchmark code
# still imports and executes, which is the thing that rots silently while nobody runs it.
#
# asv has no `check` subcommand, so the way to execute every benchmark once and record
# nothing is `run --quick --dry-run`. `--python=same` reuses this environment instead of
# building a wheel per revision. `asv machine --yes` is required first: without a machine
# profile asv exits 1 before running anything.
- name: every asv benchmark executes once
working-directory: asv_benchmarks
run: |
uv run --extra bench asv machine --yes
uv run --extra bench asv run --python=same --quick --dry-run
# The cross-library scripts are not run here. bench_vs_minisom.py trains real maps and takes
# minutes, and bench_vs_sompy.py needs an interpreter this environment cannot provide. Their
# correctness claims are covered by tests/test_minisom_agreement.py, which does run in CI.
# PYTHONPATH rather than sys.path.insert with a relative path, which silently resolves
# against whatever directory the step happens to start in.
- name: the comparison harnesses still import
env:
PYTHONPATH: benchmarks
run: |
uv run python -c "
import bench_update, bench_vs_minisom, bench_vs_sompy
print('benchmark scripts import OK')
"
- name: bench_vs_sompy degrades cleanly without its environment
run: |
test ! -d .venv-sompy
uv run python benchmarks/bench_vs_sompy.py | grep -q "uv venv .venv-sompy"
build:
name: build and verify the distribution
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
with:
enable-cache: true
- run: uv build
- name: twine check
run: uv run --with twine==7.0.0 twine check dist/*
- name: install the wheel into a clean environment and import it
run: |
uv venv /tmp/fresh
VIRTUAL_ENV=/tmp/fresh uv pip install dist/*.whl
/tmp/fresh/bin/python -c "
import pathlib, python_som
assert (pathlib.Path(python_som.__file__).parent / 'py.typed').is_file(), 'py.typed missing from the wheel'
som = python_som.SOM(x=8, y=8, input_len=3, neighborhood_function='mexicanhat', random_seed=0)
h = som.neighborhood((4, 4), 2.0)
assert h[4, 4] == 1.0
assert h.min() < 0, 'the mexican hat must be inhibitory somewhere'
print('wheel import OK, version', python_som.__version__)
"
- name: the cli extra must keep resolving
run: |
uv venv /tmp/extra
VIRTUAL_ENV=/tmp/extra uv pip install "$(echo dist/*.whl)[cli]"
/tmp/extra/bin/python -c "import tqdm; print('cli extra OK,', tqdm.__version__)"
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: dist
path: dist/