Repository navigation
308 lines (300 loc) · 10.8 KB
/
Copy pathpython.yml
File metadata and controls
308 lines (300 loc) · 10.8 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
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
name: Python
on:
# Branches only: a release tag runs this through its release workflow.
workflow_call:
push:
branches:
- '**'
paths:
- 'python/**'
- 'rust/**'
- 'conformance/**'
- 'formats/**'
- '.github/workflows/python.yml'
pull_request:
paths:
- 'python/**'
- 'rust/**'
- 'conformance/**'
- 'formats/**'
- '.github/workflows/python.yml'
permissions:
contents: read
concurrency:
group: python-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
lint:
runs-on: ubuntu-latest
defaults:
run:
shell: bash
working-directory: python
steps:
- uses: actions/checkout@v7
- uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt, clippy
- uses: astral-sh/setup-uv@v7
# ty resolves pytest and pdoc from .venv, and pyright and mypy need the package.
- name: Set up development environment
run: |
uv venv --python 3.15
uvx maturin@1.16.0 develop --uv
uv pip install --group test --group lint --group docs
- name: Check Python formatting
run: uv run --no-sync ruff format --check
- name: Lint Python
run: uv run --no-sync ruff check
- name: Type-check Python
run: uv run --no-sync ty check
- name: Type-check the t-string files as Python 3.14
run: uv run --no-sync ty check --python-version 3.14 tests/test_build_tstrings.py tests/typecheck/build_tstrings.py
- name: Check stubs accept the documented API
run: |
for file in tests/typecheck/data_usage.py tests/typecheck/build_usage.py; do
uv run --no-sync --with numpy mypy "$file"
uv run --no-sync --with numpy pyright "$file"
done
uv run --no-sync --with numpy mypy --python-version 3.14 tests/typecheck/build_tstrings.py
uv run --no-sync --with numpy pyright --pythonversion 3.14 tests/typecheck/build_tstrings.py
- name: Check stubs reject misuse on each marked line
run: |
for name in data_misuse build_misuse; do
file=tests/typecheck/$name.py
expected=$(grep -n '# error:' "$file" | cut -d: -f1)
status=0
mypy=$(uv run --no-sync --with numpy mypy "$file") || status=$?
test "$status" -eq 1
actual=$(sed -n "s|^tests/typecheck/$name\\.py:\\([0-9]*\\): error:.*|\\1|p" <<< "$mypy" | sort -nu)
diff <(echo "$expected") <(echo "$actual") || { echo "$mypy"; exit 1; }
status=0
pyright=$(uv run --no-sync --with numpy pyright "$file") || status=$?
test "$status" -eq 1
actual=$(sed -n "s|^ *.*/$name\\.py:\\([0-9]*\\):[0-9]* - error:.*|\\1|p" <<< "$pyright" | sort -nu)
diff <(echo "$expected") <(echo "$actual") || { echo "$pyright"; exit 1; }
done
- name: Check Rust formatting
run: cargo fmt --all -- --check
- name: Lint Rust crate
run: cargo clippy --all-targets --locked -- -D warnings
wheel:
name: Wheel (${{ matrix.wheel }})
strategy:
fail-fast: false
matrix:
include:
# Linux wheels cross-compile in maturin-action's containers.
- wheel: manylinux-x86_64
runner: ubuntu-latest
target: x86_64-unknown-linux-gnu
manylinux: auto
- wheel: manylinux-aarch64
runner: ubuntu-latest
target: aarch64-unknown-linux-gnu
manylinux: auto
- wheel: musllinux-x86_64
runner: ubuntu-latest
target: x86_64-unknown-linux-musl
manylinux: musllinux_1_2
- wheel: musllinux-aarch64
runner: ubuntu-latest
target: aarch64-unknown-linux-musl
manylinux: musllinux_1_2
- wheel: macos-arm64
runner: macos-15
target: aarch64-apple-darwin
- wheel: macos-x86_64
runner: macos-15-intel
target: x86_64-apple-darwin
- wheel: windows-x86_64
runner: windows-latest
target: x86_64-pc-windows-msvc
runs-on: ${{ matrix.runner }}
defaults:
run:
shell: bash
steps:
- uses: actions/checkout@v7
- name: Build wheel
uses: PyO3/maturin-action@v1
with:
working-directory: python
maturin-version: v1.16.0
target: ${{ matrix.target }}
manylinux: ${{ matrix.manylinux }}
# pyproject.toml sets locked = true; --locked repeats it here.
args: --release --locked --out dist
- name: Check wheel is abi3
run: |
wheels=(python/dist/*.whl)
ls -l "${wheels[@]}"
test "${#wheels[@]}" -eq 1
[[ "$(basename "${wheels[0]}")" == gecode_iris-*-cp310-abi3-*.whl ]]
- uses: actions/upload-artifact@v7
with:
name: wheel-${{ matrix.wheel }}
path: python/dist/*.whl
if-no-files-found: error
sdist:
runs-on: ubuntu-latest
defaults:
run:
shell: bash
steps:
- uses: actions/checkout@v7
- uses: dtolnay/rust-toolchain@stable
- uses: astral-sh/setup-uv@v7
- name: Build sdist
uses: PyO3/maturin-action@v1
with:
working-directory: python
maturin-version: v1.16.0
command: sdist
args: --out dist
# The sdist carries the rust/ crates it depends on; build it outside the checkout.
- name: Build wheel from sdist
run: |
cp python/dist/*.tar.gz "$RUNNER_TEMP/"
cd "$RUNNER_TEMP"
uv build --wheel --out-dir wheel ./*.tar.gz
uv venv --python 3.10 env
uv pip install --python env/bin/python wheel/*.whl
env/bin/python -I -c 'import gecode_iris as iris; assert iris.Spec.parse("n: int\n").read(b"3\n")["n"] == 3'
env/bin/iris --version
- uses: actions/upload-artifact@v7
with:
name: sdist
path: python/dist/*.tar.gz
if-no-files-found: error
test:
name: Test (${{ matrix.wheel }}, Python ${{ matrix.python }})
needs: wheel
strategy:
fail-fast: false
matrix:
wheel:
- manylinux-x86_64
- manylinux-aarch64
- musllinux-x86_64
- musllinux-aarch64
- macos-arm64
- macos-x86_64
- windows-x86_64
# The abi3 floor and the newest release.
python: ['3.10', '3.15']
include:
- wheel: manylinux-x86_64
runner: ubuntu-latest
conformance: true
- wheel: manylinux-aarch64
runner: ubuntu-24.04-arm
- wheel: musllinux-x86_64
runner: ubuntu-latest
alpine: true
- wheel: musllinux-aarch64
runner: ubuntu-24.04-arm
alpine: true
- wheel: macos-arm64
runner: macos-15
conformance: true
- wheel: macos-x86_64
runner: macos-15-intel
- wheel: windows-x86_64
runner: windows-latest
conformance: true
runs-on: ${{ matrix.runner }}
defaults:
run:
shell: bash
steps:
# The tests compare bytes with checked-in files, so keep LF line endings.
- name: Keep line endings
if: runner.os == 'Windows'
run: git config --global core.autocrlf false
- uses: actions/checkout@v7
- uses: actions/download-artifact@v8
with:
name: wheel-${{ matrix.wheel }}
path: dist
- uses: astral-sh/setup-uv@v7
if: ${{ !matrix.alpine }}
- name: Install wheel
if: ${{ !matrix.alpine }}
working-directory: python
run: |
env="$RUNNER_TEMP/iris-env"
if [[ "$RUNNER_OS" == Windows ]]; then
scripts="$env/Scripts" exe=.exe
else
scripts="$env/bin" exe=
fi
uv venv --python "${{ matrix.python }}" "$env"
uv pip install --python "$scripts/python$exe" ../dist/*.whl --group test
{ echo "PY=$scripts/python$exe"; echo "CLI=$scripts/iris$exe"; } >> "$GITHUB_ENV"
- name: Run pytest
if: ${{ !matrix.alpine }}
working-directory: python
run: '"$PY" -m pytest'
- name: Run pytest with numpy
if: ${{ !matrix.alpine }}
working-directory: python
run: |
uv pip install --python "$PY" numpy
"$PY" -m pytest
- name: Run console script
if: ${{ !matrix.alpine }}
run: |
"$CLI" --version
"$CLI" check formats/dimacs-cnf/dimacs-cnf.iris
- name: Run conformance cases
if: ${{ matrix.conformance && matrix.python == '3.15' }}
run: |
driver="\"$PY\" -I \"$GITHUB_WORKSPACE/python/tests/conformance_driver.py\""
"$CLI" conformance "$driver" conformance/cases/0.1
"$CLI" conformance "$driver" formats
# JavaScript actions do not run in Alpine containers, so start one directly.
- name: Test wheel in Alpine
if: ${{ matrix.alpine }}
run: |
docker run --rm -v "$GITHUB_WORKSPACE:/iris" -w /iris/python \
"ghcr.io/astral-sh/uv:python${{ matrix.python }}-alpine" sh -euxc '
uv venv --python-preference only-system /tmp/env
uv pip install --python /tmp/env/bin/python ../dist/*.whl --group test
/tmp/env/bin/python -m pytest -p no:cacheprovider
uv pip install --python /tmp/env/bin/python numpy
/tmp/env/bin/python -m pytest -p no:cacheprovider
/tmp/env/bin/iris --version
/tmp/env/bin/iris check ../formats/dimacs-cnf/dimacs-cnf.iris
'
# Every accepted corpus spec through the generated Python builder, which must
# pass ruff and ty unchanged; the round trip prints its counts.
builder-round-trip:
name: Builder round trip (Python ${{ matrix.python }})
needs: wheel
strategy:
fail-fast: false
matrix:
python: ['3.10', '3.15']
runs-on: ubuntu-latest
defaults:
run:
shell: bash
steps:
- uses: actions/checkout@v7
- uses: dtolnay/rust-toolchain@stable
- uses: astral-sh/setup-uv@v7
- uses: actions/download-artifact@v8
with:
name: wheel-manylinux-x86_64
path: dist
- name: Install wheel with ruff and ty
working-directory: python
run: |
uv venv --python "${{ matrix.python }}" "$RUNNER_TEMP/iris-env"
uv pip install --python "$RUNNER_TEMP/iris-env/bin/python" ../dist/*.whl --group lint
- name: Round-trip the corpus through the Python builder
working-directory: rust
run: |
IRIS_PYTHON="$RUNNER_TEMP/iris-env/bin/python" \
cargo test --locked -p gecode-iris --test python_round_trip -- --ignored --nocapture