parameterrun 0.3.0__tar.gz → 0.4.0__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.
- parameterrun-0.4.0/.github/workflows/ci.yml +65 -0
- {parameterrun-0.3.0 → parameterrun-0.4.0}/.github/workflows/publish.yml +1 -1
- {parameterrun-0.3.0 → parameterrun-0.4.0}/CHANGELOG.md +9 -10
- {parameterrun-0.3.0 → parameterrun-0.4.0}/CONTRIBUTING.md +10 -1
- {parameterrun-0.3.0 → parameterrun-0.4.0}/PKG-INFO +11 -9
- {parameterrun-0.3.0 → parameterrun-0.4.0}/README.md +10 -8
- parameterrun-0.4.0/examples/minimal_example_mpi.py +21 -0
- {parameterrun-0.3.0 → parameterrun-0.4.0}/pyproject.toml +3 -0
- {parameterrun-0.3.0 → parameterrun-0.4.0}/src/parameterrun/__init__.py +1 -1
- {parameterrun-0.3.0 → parameterrun-0.4.0}/src/parameterrun/parallel_utils.py +33 -20
- parameterrun-0.4.0/tests/test_mpi_integration.py +30 -0
- parameterrun-0.3.0/.github/workflows/ci.yml +0 -40
- {parameterrun-0.3.0 → parameterrun-0.4.0}/.github/workflows/release.yml +0 -0
- {parameterrun-0.3.0 → parameterrun-0.4.0}/.gitignore +0 -0
- {parameterrun-0.3.0 → parameterrun-0.4.0}/LICENSE +0 -0
- {parameterrun-0.3.0 → parameterrun-0.4.0}/examples/minimal_example.py +0 -0
- {parameterrun-0.3.0 → parameterrun-0.4.0}/scripts/bump_version.py +0 -0
- /parameterrun-0.3.0/tests/test_dephs.py → /parameterrun-0.4.0/tests/test_depths.py +0 -0
- {parameterrun-0.3.0 → parameterrun-0.4.0}/tests/test_format_input.py +0 -0
- {parameterrun-0.3.0 → parameterrun-0.4.0}/tests/test_get_iteration.py +0 -0
- {parameterrun-0.3.0 → parameterrun-0.4.0}/tests/test_joblib.py +0 -0
- {parameterrun-0.3.0 → parameterrun-0.4.0}/tests/test_normalized_time.py +0 -0
- {parameterrun-0.3.0 → parameterrun-0.4.0}/tests/test_parameter_run.py +0 -0
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
branches: [ main ]
|
|
6
|
+
|
|
7
|
+
permissions:
|
|
8
|
+
contents: read
|
|
9
|
+
|
|
10
|
+
jobs:
|
|
11
|
+
test:
|
|
12
|
+
name: non-mpi / ${{ matrix.os }} / py${{ matrix.python-version }}
|
|
13
|
+
runs-on: ${{ matrix.os }}
|
|
14
|
+
strategy:
|
|
15
|
+
fail-fast: false
|
|
16
|
+
matrix:
|
|
17
|
+
os: [ ubuntu-latest, macos-latest, windows-latest ]
|
|
18
|
+
python-version: [ "3.9", "3.10", "3.11", "3.12", "3.13" ]
|
|
19
|
+
|
|
20
|
+
steps:
|
|
21
|
+
- uses: actions/checkout@v6
|
|
22
|
+
|
|
23
|
+
- uses: actions/setup-python@v6
|
|
24
|
+
with:
|
|
25
|
+
python-version: ${{ matrix.python-version }}
|
|
26
|
+
|
|
27
|
+
- name: Install uv
|
|
28
|
+
run: python -m pip install uv
|
|
29
|
+
|
|
30
|
+
- name: Sync environment
|
|
31
|
+
run: uv sync
|
|
32
|
+
|
|
33
|
+
- name: Tests
|
|
34
|
+
run: uv run pytest -m "not mpi"
|
|
35
|
+
|
|
36
|
+
- name: Lint
|
|
37
|
+
run: uv run ruff check .
|
|
38
|
+
|
|
39
|
+
- name: Type check
|
|
40
|
+
run: uv run mypy
|
|
41
|
+
|
|
42
|
+
test-mpi-linux:
|
|
43
|
+
name: mpi / ubuntu-latest / py3.12 / 4 ranks
|
|
44
|
+
runs-on: ubuntu-latest
|
|
45
|
+
|
|
46
|
+
steps:
|
|
47
|
+
- uses: actions/checkout@v6
|
|
48
|
+
|
|
49
|
+
- uses: actions/setup-python@v6
|
|
50
|
+
with:
|
|
51
|
+
python-version: "3.12"
|
|
52
|
+
|
|
53
|
+
- name: Setup MPI
|
|
54
|
+
uses: mpi4py/setup-mpi@v1
|
|
55
|
+
with:
|
|
56
|
+
mpi: 'openmpi'
|
|
57
|
+
|
|
58
|
+
- name: Install uv
|
|
59
|
+
run: python -m pip install uv
|
|
60
|
+
|
|
61
|
+
- name: Sync environment with MPI extra
|
|
62
|
+
run: uv sync --extra mpi
|
|
63
|
+
|
|
64
|
+
- name: MPI tests (4 ranks)
|
|
65
|
+
run: mpirun -n 4 uv run pytest -m mpi -o addopts="-q -m mpi"
|
|
@@ -4,22 +4,21 @@ All notable changes to this project are documented in this file.
|
|
|
4
4
|
|
|
5
5
|
The format is based on Keep a Changelog and the project follows Semantic Versioning.
|
|
6
6
|
|
|
7
|
-
## [
|
|
8
|
-
|
|
9
|
-
### Added
|
|
10
|
-
- (Your changes here)
|
|
7
|
+
## [Unreleased]
|
|
11
8
|
|
|
12
|
-
## [0.
|
|
9
|
+
## [0.4.0] - 2026-04-21
|
|
13
10
|
|
|
14
11
|
### Added
|
|
15
|
-
- (Your changes here)
|
|
16
12
|
|
|
17
|
-
|
|
13
|
+
- Added `tests/test_mpi_integration.py` with MPI numerical-equivalence coverage for `parameterrun(..., backend="mpi")`.
|
|
14
|
+
- Added a dedicated Linux PR CI job that runs MPI tests with 4 ranks.
|
|
18
15
|
|
|
19
|
-
###
|
|
20
|
-
- (Your changes here)
|
|
16
|
+
### Changed
|
|
21
17
|
|
|
22
|
-
|
|
18
|
+
- Updated default test commands in docs and CI to run non-MPI tests with `-m "not mpi"`.
|
|
19
|
+
- Registered a pytest `mpi` marker in `pyproject.toml` for explicit MPI test selection.
|
|
20
|
+
- Update how the indices to compute are shared among workers. Previously, all the indices where computed by all workers,
|
|
21
|
+
which caused a significan overhead in the memory usage. Now, the indices are computed on the fly.
|
|
23
22
|
|
|
24
23
|
## [0.3.0] - 2026-04-01
|
|
25
24
|
|
|
@@ -13,13 +13,22 @@ If you do not use `uv`, create a virtual environment and install equivalent dev
|
|
|
13
13
|
## Run quality checks
|
|
14
14
|
|
|
15
15
|
```bash
|
|
16
|
-
uv run pytest
|
|
16
|
+
uv run pytest -m "not mpi"
|
|
17
17
|
uv run ruff check .
|
|
18
18
|
uv run mypy
|
|
19
19
|
uv run python -m build
|
|
20
20
|
uv run python -m twine check dist/*
|
|
21
21
|
```
|
|
22
22
|
|
|
23
|
+
## Run MPI checks (required on PR CI)
|
|
24
|
+
|
|
25
|
+
The PR workflow includes a dedicated Linux MPI job that runs with 4 ranks.
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
uv sync --extra mpi
|
|
29
|
+
mpirun -n 4 uv run pytest -m mpi -o addopts="-q -m mpi"
|
|
30
|
+
```
|
|
31
|
+
|
|
23
32
|
## Pull request guidelines
|
|
24
33
|
|
|
25
34
|
- Keep PRs focused and small when possible.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: parameterrun
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: Parallel parameter sweeps with joblib/MPI and tqdm progress bars.
|
|
5
5
|
Project-URL: Homepage, https://github.com/Davtax/ParameterRun
|
|
6
6
|
Project-URL: Repository, https://github.com/Davtax/ParameterRun
|
|
@@ -210,12 +210,6 @@ By default, progress bars are enabled:
|
|
|
210
210
|
result = parameterrun(my_function, param_names="x", param_values=[1, 2, 3], pbar_bool=True, )
|
|
211
211
|
```
|
|
212
212
|
|
|
213
|
-
You can also pass extra keyword arguments to `tqdm` through `pbar_kwargs`:
|
|
214
|
-
|
|
215
|
-
```python
|
|
216
|
-
result = parameterrun(my_function, param_names="x", param_values=[1, 2, 3], pbar_kwargs={"leave": False}, )
|
|
217
|
-
```
|
|
218
|
-
|
|
219
213
|
Verbose logging can be enabled with:
|
|
220
214
|
|
|
221
215
|
```python
|
|
@@ -279,9 +273,17 @@ param_values = [[[1, 2], [10, 20]], [[100, 200, 300]], ]
|
|
|
279
273
|
Before opening a PR or publishing a release, run:
|
|
280
274
|
|
|
281
275
|
```bash
|
|
282
|
-
pytest
|
|
276
|
+
pytest -m "not mpi"
|
|
283
277
|
ruff check .
|
|
284
|
-
mypy
|
|
278
|
+
mypy
|
|
285
279
|
python -m build
|
|
286
280
|
python -m twine check dist/*
|
|
287
281
|
```
|
|
282
|
+
|
|
283
|
+
For MPI numerical-equivalence checks (Linux/OpenMPI, 4 ranks):
|
|
284
|
+
|
|
285
|
+
```bash
|
|
286
|
+
uv sync --extra mpi
|
|
287
|
+
mpirun -n 4 uv run pytest -m mpi -o addopts="-q -m mpi"
|
|
288
|
+
```
|
|
289
|
+
|
|
@@ -178,12 +178,6 @@ By default, progress bars are enabled:
|
|
|
178
178
|
result = parameterrun(my_function, param_names="x", param_values=[1, 2, 3], pbar_bool=True, )
|
|
179
179
|
```
|
|
180
180
|
|
|
181
|
-
You can also pass extra keyword arguments to `tqdm` through `pbar_kwargs`:
|
|
182
|
-
|
|
183
|
-
```python
|
|
184
|
-
result = parameterrun(my_function, param_names="x", param_values=[1, 2, 3], pbar_kwargs={"leave": False}, )
|
|
185
|
-
```
|
|
186
|
-
|
|
187
181
|
Verbose logging can be enabled with:
|
|
188
182
|
|
|
189
183
|
```python
|
|
@@ -247,9 +241,17 @@ param_values = [[[1, 2], [10, 20]], [[100, 200, 300]], ]
|
|
|
247
241
|
Before opening a PR or publishing a release, run:
|
|
248
242
|
|
|
249
243
|
```bash
|
|
250
|
-
pytest
|
|
244
|
+
pytest -m "not mpi"
|
|
251
245
|
ruff check .
|
|
252
|
-
mypy
|
|
246
|
+
mypy
|
|
253
247
|
python -m build
|
|
254
248
|
python -m twine check dist/*
|
|
255
249
|
```
|
|
250
|
+
|
|
251
|
+
For MPI numerical-equivalence checks (Linux/OpenMPI, 4 ranks):
|
|
252
|
+
|
|
253
|
+
```bash
|
|
254
|
+
uv sync --extra mpi
|
|
255
|
+
mpirun -n 4 uv run pytest -m mpi -o addopts="-q -m mpi"
|
|
256
|
+
```
|
|
257
|
+
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
from mpi4py import MPI
|
|
3
|
+
|
|
4
|
+
from parameterrun import parameterrun
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def f(x, y, scale=1):
|
|
8
|
+
return scale * (x + y)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
comm = MPI.COMM_WORLD
|
|
12
|
+
rank = comm.Get_rank()
|
|
13
|
+
size = comm.Get_size()
|
|
14
|
+
|
|
15
|
+
xs = np.linspace(0, 10, 100)
|
|
16
|
+
ys = np.linspace(0, 20, 50)
|
|
17
|
+
|
|
18
|
+
result = parameterrun(f, param_names=[["x"], ["y"]], param_values=[[xs], [ys]], scale=2, backend='mpi')
|
|
19
|
+
|
|
20
|
+
if rank == 0:
|
|
21
|
+
print(np.all(result == 2 * (xs[:, None] + ys[None, :])))
|
|
@@ -64,6 +64,9 @@ packages = ["src/parameterrun"]
|
|
|
64
64
|
[tool.pytest.ini_options]
|
|
65
65
|
testpaths = ["tests"]
|
|
66
66
|
addopts = "-q --cov=parameterrun --cov-report=term-missing --cov-fail-under=70"
|
|
67
|
+
markers = [
|
|
68
|
+
"mpi: tests that require mpi4py and an MPI launcher",
|
|
69
|
+
]
|
|
67
70
|
|
|
68
71
|
[tool.ruff]
|
|
69
72
|
target-version = "py39"
|
|
@@ -4,6 +4,7 @@ import sys
|
|
|
4
4
|
from collections.abc import Iterable
|
|
5
5
|
from datetime import datetime
|
|
6
6
|
from itertools import product
|
|
7
|
+
from math import prod
|
|
7
8
|
from time import time
|
|
8
9
|
from typing import Any, Callable, List, Optional, Tuple, Union, cast
|
|
9
10
|
|
|
@@ -58,12 +59,8 @@ def tqdm_joblib(tqdm_object: tqdm):
|
|
|
58
59
|
|
|
59
60
|
|
|
60
61
|
def _parameterrun_joblib(fun: Callable[..., Any], param_names: List[List[str]], param_values: List[List[List]],
|
|
61
|
-
indices_iterate:
|
|
62
|
+
indices_iterate: Iterable, desc, n_workers: Optional[int] = -1, pbar_bool: bool = True,
|
|
62
63
|
leave: Optional[bool] = True, **fun_kwargs) -> Tuple[List[List], int]:
|
|
63
|
-
n_total = len(indices_iterate)
|
|
64
|
-
if n_total == 0:
|
|
65
|
-
raise ValueError('No parameter combinations generated. Please provide at least one value per group.')
|
|
66
|
-
|
|
67
64
|
result = []
|
|
68
65
|
if n_workers == 1: # If only one worker, do not use joblib
|
|
69
66
|
pbar = tqdm(indices_iterate, desc=desc, leave=leave, disable=not pbar_bool)
|
|
@@ -75,6 +72,8 @@ def _parameterrun_joblib(fun: Callable[..., Any], param_names: List[List[str]],
|
|
|
75
72
|
delayed(fun)(**{**_get_iteration(index, param_names, param_values), **fun_kwargs}) for index in
|
|
76
73
|
indices_iterate)
|
|
77
74
|
|
|
75
|
+
n_total = len(result)
|
|
76
|
+
|
|
78
77
|
n_output = len(result[0]) if isinstance(result[0], tuple) else 1
|
|
79
78
|
if n_output == 1:
|
|
80
79
|
result = [(result_i,) for result_i in result]
|
|
@@ -86,9 +85,19 @@ def _parameterrun_joblib(fun: Callable[..., Any], param_names: List[List[str]],
|
|
|
86
85
|
return result_temp, n_output
|
|
87
86
|
|
|
88
87
|
|
|
88
|
+
def _product_element_from_index(lists: List[Any], k: int) -> List:
|
|
89
|
+
lengths = [len(L) for L in lists]
|
|
90
|
+
|
|
91
|
+
indices = [0] * len(lists)
|
|
92
|
+
for i in range(len(lists) - 1, -1, -1):
|
|
93
|
+
k, indices[i] = divmod(k, lengths[i])
|
|
94
|
+
|
|
95
|
+
return list(L[j] for L, j in zip(lists, indices))
|
|
96
|
+
|
|
97
|
+
|
|
89
98
|
def _parameterrun_mpi(fun: Callable[..., Any], param_names: List[List[str]], param_values: List[List[List]],
|
|
90
|
-
|
|
91
|
-
|
|
99
|
+
desc: Optional[str], pbar_bool: bool = True, verbose: Optional[bool] = False,
|
|
100
|
+
leave: Optional[bool] = True, seed=None, **fun_kwargs) -> Tuple[
|
|
92
101
|
List[List], int]: # pragma: no cover
|
|
93
102
|
from mpi4py import MPI
|
|
94
103
|
|
|
@@ -96,19 +105,25 @@ def _parameterrun_mpi(fun: Callable[..., Any], param_names: List[List[str]], par
|
|
|
96
105
|
rank = comm.Get_rank()
|
|
97
106
|
size = comm.Get_size()
|
|
98
107
|
|
|
99
|
-
|
|
108
|
+
n_values = [len(group[0]) for group in param_values]
|
|
109
|
+
indices = [list(range(n_value)) for n_value in n_values]
|
|
110
|
+
n_total = prod(n_values) # Compute the total number of iterations
|
|
100
111
|
|
|
101
112
|
# Shuffle the indices to avoid bias in the computation
|
|
102
113
|
if rank == 0:
|
|
114
|
+
rng = np.random.default_rng(seed=seed)
|
|
103
115
|
total_indices = np.arange(n_total)
|
|
104
|
-
|
|
116
|
+
rng.shuffle(total_indices)
|
|
105
117
|
else:
|
|
106
118
|
total_indices = None
|
|
107
119
|
|
|
108
120
|
total_indices = comm.bcast(total_indices, root=0)
|
|
109
121
|
indices_compute = np.array_split(total_indices, size)[rank]
|
|
110
122
|
|
|
111
|
-
groups_compute = np.array(groups_iterate)[indices_compute]
|
|
123
|
+
# groups_compute = np.array(groups_iterate)[indices_compute]
|
|
124
|
+
groups_compute = []
|
|
125
|
+
for index in indices_compute:
|
|
126
|
+
groups_compute.append(_product_element_from_index(indices, index))
|
|
112
127
|
|
|
113
128
|
pbar = tqdm(groups_compute, desc=desc, leave=leave, disable=not (pbar_bool and rank == 0), file=sys.stdout)
|
|
114
129
|
|
|
@@ -158,7 +173,7 @@ def _parameterrun_mpi(fun: Callable[..., Any], param_names: List[List[str]], par
|
|
|
158
173
|
|
|
159
174
|
pbar.close()
|
|
160
175
|
else:
|
|
161
|
-
comm.send(
|
|
176
|
+
comm.send(True, dest=0, tag=0) # Notify root that the work is done
|
|
162
177
|
|
|
163
178
|
n_outputs = comm.bcast(n_outputs, root=0)
|
|
164
179
|
|
|
@@ -327,7 +342,7 @@ def _convert_result_to_lists(result: Any) -> Any:
|
|
|
327
342
|
def parameterrun(fun: Callable[..., Any], param_names: Union[str, List[str], List[List[str]]],
|
|
328
343
|
param_values: Union[Iterable[Any], Iterable[Iterable[Any]], Iterable[Iterable[Iterable[Any]]]],
|
|
329
344
|
n_workers: Optional[int] = -1, pbar_bool: bool = True, verbose: Optional[bool] = False,
|
|
330
|
-
reshape:
|
|
345
|
+
reshape: bool = True, result_as_array: bool = True, backend: Optional[str] = None,
|
|
331
346
|
desc: Optional[str] = None, **kwargs) -> Union[list, np.ndarray, None]: # noqa: E501
|
|
332
347
|
"""
|
|
333
348
|
Run a function with multiple parameters in parallel. To indentify the parameters of interest, user must provide its
|
|
@@ -393,9 +408,6 @@ def parameterrun(fun: Callable[..., Any], param_names: Union[str, List[str], Lis
|
|
|
393
408
|
if any(n_value == 0 for n_value in n_values):
|
|
394
409
|
raise ValueError('Parameter groups must contain at least one value.')
|
|
395
410
|
|
|
396
|
-
indices = [list(range(n_value)) for n_value in n_values]
|
|
397
|
-
indices_iterate = product(*indices)
|
|
398
|
-
|
|
399
411
|
# Choose the backed
|
|
400
412
|
if backend is not None:
|
|
401
413
|
available_backends = ['joblib', 'mpi']
|
|
@@ -437,16 +449,17 @@ def parameterrun(fun: Callable[..., Any], param_names: Union[str, List[str], Lis
|
|
|
437
449
|
if backend == 'joblib':
|
|
438
450
|
_log('Running under joblib', verbose)
|
|
439
451
|
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
452
|
+
indices = [list(range(n_value)) for n_value in n_values]
|
|
453
|
+
indices_iterate = product(*indices)
|
|
454
|
+
result, n_outputs = _parameterrun_joblib(fun, formatted_param_names, formatted_param_values, indices_iterate,
|
|
455
|
+
desc, n_workers=n_workers, pbar_bool=pbar_bool, **kwargs)
|
|
443
456
|
|
|
444
457
|
elif backend == 'mpi':
|
|
445
458
|
_log(f'Running under mpi with {size} workers', verbose and rank == 0,
|
|
446
459
|
hostname=mpi_module.Get_processor_name() if mpi_module is not None else None, )
|
|
447
460
|
|
|
448
|
-
result, n_outputs = _parameterrun_mpi(fun, formatted_param_names, formatted_param_values,
|
|
449
|
-
|
|
461
|
+
result, n_outputs = _parameterrun_mpi(fun, formatted_param_names, formatted_param_values, desc,
|
|
462
|
+
pbar_bool=pbar_bool, verbose=verbose, **kwargs)
|
|
450
463
|
|
|
451
464
|
else:
|
|
452
465
|
raise ValueError('Unknown backend')
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import pytest
|
|
3
|
+
|
|
4
|
+
import parameterrun.parallel_utils as pu
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@pytest.mark.mpi
|
|
8
|
+
def test_parameterrun_mpi_numerical_equivalence_and_root_return():
|
|
9
|
+
MPI = pytest.importorskip("mpi4py.MPI")
|
|
10
|
+
comm = MPI.COMM_WORLD
|
|
11
|
+
|
|
12
|
+
if comm.Get_size() != 4:
|
|
13
|
+
pytest.skip("Run this test with mpirun -n 4")
|
|
14
|
+
|
|
15
|
+
xs = np.array([0.0, 1.0, 2.0])
|
|
16
|
+
ys = np.array([10.0, 20.0])
|
|
17
|
+
|
|
18
|
+
def f(x, y, scale=1.0):
|
|
19
|
+
return scale * (x + y)
|
|
20
|
+
|
|
21
|
+
result = pu.parameterrun(f, param_names=[["x"], ["y"]], param_values=[[xs], [ys]], scale=2.0, backend="mpi",
|
|
22
|
+
pbar_bool=False, )
|
|
23
|
+
|
|
24
|
+
expected = 2.0 * (xs[:, None] + ys[None, :])
|
|
25
|
+
|
|
26
|
+
if comm.Get_rank() == 0:
|
|
27
|
+
assert isinstance(result, np.ndarray)
|
|
28
|
+
np.testing.assert_allclose(result, expected)
|
|
29
|
+
else:
|
|
30
|
+
assert result is None
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
name: CI
|
|
2
|
-
|
|
3
|
-
on:
|
|
4
|
-
pull_request:
|
|
5
|
-
branches: [ main ]
|
|
6
|
-
|
|
7
|
-
permissions:
|
|
8
|
-
contents: read
|
|
9
|
-
|
|
10
|
-
jobs:
|
|
11
|
-
test:
|
|
12
|
-
name: ${{ matrix.os }} / py${{ matrix.python-version }}
|
|
13
|
-
runs-on: ${{ matrix.os }}
|
|
14
|
-
strategy:
|
|
15
|
-
fail-fast: false
|
|
16
|
-
matrix:
|
|
17
|
-
os: [ ubuntu-latest, macos-latest, windows-latest ]
|
|
18
|
-
python-version: [ "3.9", "3.10", "3.11", "3.12", "3.13" ]
|
|
19
|
-
|
|
20
|
-
steps:
|
|
21
|
-
- uses: actions/checkout@v6
|
|
22
|
-
|
|
23
|
-
- uses: actions/setup-python@v5
|
|
24
|
-
with:
|
|
25
|
-
python-version: ${{ matrix.python-version }}
|
|
26
|
-
|
|
27
|
-
- name: Install uv
|
|
28
|
-
run: python -m pip install uv
|
|
29
|
-
|
|
30
|
-
- name: Sync environment
|
|
31
|
-
run: uv sync
|
|
32
|
-
|
|
33
|
-
- name: Tests
|
|
34
|
-
run: uv run pytest
|
|
35
|
-
|
|
36
|
-
- name: Lint
|
|
37
|
-
run: uv run ruff check .
|
|
38
|
-
|
|
39
|
-
- name: Type check
|
|
40
|
-
run: uv run mypy
|
|
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
|