Skip to content

Commit 56e1ea4

Browse files
authored
Update Test Structure to use uv (#350)
* updates test markers * updated lock files * update workflow to check if it works * update workflow to check if it works * update workflow to check if it works * update workflow to check if it works * update workflow to check if it works * update --system flag * update --sync flag * update tabpfn marker * updates workflows * updates workflows * update caching * updated cache again * updated cache again * test cache again * update uv.lock * updated caching according to pyproject.toml and lock
1 parent c88a1d5 commit 56e1ea4

13 files changed

+2825
-186
lines changed

.github/dependabot.yml

+3-2
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,15 @@ updates:
88
- package-ecosystem: "pip"
99
directory: "/"
1010
schedule:
11-
interval: "monthly"
11+
interval: "weekly"
1212
open-pull-requests-limit: 12
1313
groups:
1414
pip-all-updates:
1515
patterns:
1616
- "*"
17+
1718
- package-ecosystem: "github-actions"
1819
directory: "/"
1920
schedule:
20-
interval: "monthly"
21+
interval: "weekly"
2122
open-pull-requests-limit: 12

.github/workflows/code-quality.yml

+3-3
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@ jobs:
3030

3131
- name: Install pre-commit
3232
run: |
33-
uv pip install --system pre-commit ruff
34-
pre-commit install
33+
uv sync --only-group lint
34+
uv run pre-commit install
3535
3636
- name: Run code-quality checks
37-
run: SKIP=mypy pre-commit run --all-files
37+
run: uv run pre-commit run --all-files

.github/workflows/coverage.yml

+10-6
Original file line numberDiff line numberDiff line change
@@ -30,15 +30,19 @@ jobs:
3030
with:
3131
enable-cache: true
3232

33+
- name: Cache UV packages
34+
uses: actions/cache@v4
35+
with:
36+
path: ~/.cache/uv
37+
key: uv-${{ runner.os }}-${{ matrix.python-version }}-${{ hashFiles('pyproject.toml', 'uv.lock') }}
38+
restore-keys: |
39+
uv-${{ runner.os }}-${{ matrix.python-version }}-
40+
3341
- name: Install dependencies
34-
run: |
35-
uv pip install --system --no-deps .
36-
uv pip install --system -r requirements.txt
37-
uv pip install --system pytest pytest-cov
42+
run: uv sync --all-extras --dev
3843

3944
- name: Measure coverage
40-
run: |
41-
pytest --cov=shapiq --cov-report=xml
45+
run: uv run pytest --cov=shapiq --cov-report=xml -n logical
4246

4347
- name: Coveralls
4448
uses: coverallsapp/github-action@v2

.github/workflows/unit-tests-current.yml

-39
This file was deleted.

.github/workflows/unit-tests.yml

+8-12
Original file line numberDiff line numberDiff line change
@@ -21,22 +21,18 @@ jobs:
2121
steps:
2222
- uses: actions/checkout@v4
2323

24-
- name: Set up Python ${{ matrix.python-version }}
25-
uses: actions/setup-python@v5
26-
with:
27-
python-version: ${{ matrix.python-version }}
28-
29-
- name: Install uv
24+
- name: Set up Python ${{ matrix.python-version }} and uv
25+
id: setup-uv-python
3026
uses: astral-sh/setup-uv@v5
3127
with:
28+
python-version: ${{ matrix.python-version }}
3229
enable-cache: true
30+
cache-dependency-glob: |
31+
**/pyproject.toml
32+
**/uv.lock
3333
3434
- name: Install dependencies
35-
run: |
36-
uv pip install --system --no-deps .
37-
uv pip install --system -r tests/requirements/requirements.txt
38-
uv pip install --system pytest
35+
run: uv sync --all-extras --dev
3936

4037
- name: Test with pytest
41-
run: |
42-
pytest tests/
38+
run: uv run pytest -n logical

pyproject.toml

+26-30
Original file line numberDiff line numberDiff line change
@@ -72,40 +72,22 @@ tracker = "https://github.com/mmschlk/shapiq/issues"
7272
changelog = "https://github.com/mmschlk/shapiq/blob/main/CHANGELOG.md"
7373

7474
[project.optional-dependencies]
75-
dev = [
76-
# Lint/format
77-
"pre-commit",
78-
"ruff",
79-
# "mypy", # TODO: incorporate mypy
80-
# Test
81-
"pytest",
82-
"coverage",
83-
"build",
84-
"twine",
85-
]
86-
87-
doc = [
88-
"sphinx",
89-
"sphinx-autodoc-typehints",
90-
"sphinx_rtd_theme",
91-
"sphinx_toolbox",
92-
# "myst_nb",
93-
"nbsphinx", # for rendering jupyter notebooks
94-
"pandoc", # for rendering jupyter notebooks
95-
"furo", # theme of the docs
96-
"sphinx-copybutton", # easier copy-pasting of code snippets from docs
97-
"myst-parser", # parse md and rst files
75+
ml = [
76+
"tabpfn",
77+
"torchvision",
78+
"torch",
79+
"xgboost",
80+
"lightgbm",
81+
"transformers",
82+
"scikit-image",
83+
"tensorflow; python_version < '3.13'",
84+
"tf-keras; python_version < '3.13'",
9885
]
9986

10087
[tool.pytest.ini_options]
10188
testpaths = ["tests"]
10289
minversion = "8.0"
10390

104-
105-
[tool.black]
106-
line-length = 100
107-
target-version = ['py310', 'py311', 'py312', 'py313']
108-
10991
[tool.ruff]
11092
lint.select = ["E", "F", "I", "UP"] # https://beta.ruff.rs/docs/rules/
11193
lint.ignore = ["E501"]
@@ -118,5 +100,19 @@ extra-standard-library = ["typing_extensions"]
118100
combine-as-imports = true
119101
force-wrap-aliases = true
120102

121-
[tool.uv.workspace]
122-
members = ["shapiq/shapiq"]
103+
[dependency-groups]
104+
test = [
105+
"pytest>=8.3.5",
106+
"pytest-cov>=6.0.0",
107+
"pytest-xdist>=3.6.1",
108+
]
109+
lint = [
110+
"ruff>=0.11.2",
111+
"pre-commit>=4.2.0",
112+
]
113+
dev = [
114+
"build>=1.2.2.post1",
115+
"twine>=6.1.0",
116+
{include-group = "test"},
117+
{include-group = "lint"},
118+
]

requirements.txt

-24
This file was deleted.

tests/fixtures/models.py

+18-18
Original file line numberDiff line numberDiff line change
@@ -59,11 +59,11 @@ def custom_model(background_reg_dataset) -> CustomModel:
5959
@pytest.fixture
6060
def lightgbm_basic(background_reg_dataset) -> Model:
6161
"""Return a lgm basic booster"""
62-
import lightgbm as lgb
62+
lightgbm = pytest.importorskip("lightgbm")
6363

6464
X, y = background_reg_dataset
65-
train_data = lgb.Dataset(X, label=y)
66-
model = lgb.train(params={}, train_set=train_data, num_boost_round=1)
65+
train_data = lightgbm.Dataset(X, label=y)
66+
model = lightgbm.train(params={}, train_set=train_data, num_boost_round=1)
6767
return model
6868

6969

@@ -87,7 +87,7 @@ def sequential_model_3_classes() -> Model:
8787

8888
def _sequential_model(output_shape_nr, background_reg_dataset) -> Model:
8989
"""Return a keras nn with specified output dimension"""
90-
import keras
90+
keras = pytest.importorskip("keras")
9191

9292
model = keras.Sequential(
9393
[
@@ -105,10 +105,10 @@ def _sequential_model(output_shape_nr, background_reg_dataset) -> Model:
105105
@pytest.fixture
106106
def xgb_reg_model(background_reg_dataset) -> Model:
107107
"""Return a simple xgboost regression model."""
108-
from xgboost import XGBRegressor
108+
xgboost = pytest.importorskip("xgboost")
109109

110110
X, y = background_reg_dataset
111-
model = XGBRegressor(random_state=42, n_estimators=3)
111+
model = xgboost.XGBRegressor(random_state=42, n_estimators=3)
112112
model.fit(X, y)
113113
return model
114114

@@ -125,18 +125,18 @@ def rf_clf_binary_model(background_clf_dataset_binary) -> RandomForestClassifier
125125
@pytest.fixture
126126
def xgb_clf_model(background_clf_dataset) -> Model:
127127
"""Return a simple xgboost classification model."""
128-
from xgboost import XGBClassifier
128+
xgboost = pytest.importorskip("xgboost")
129129

130130
X, y = background_clf_dataset
131-
model = XGBClassifier(random_state=42, n_estimators=3)
131+
model = xgboost.XGBClassifier(random_state=42, n_estimators=3)
132132
model.fit(X, y)
133133
return model
134134

135135

136136
@pytest.fixture
137137
def torch_clf_model() -> Model:
138138
"""Return a simple torch model."""
139-
import torch
139+
torch = pytest.importorskip("torch")
140140

141141
model = torch.nn.Sequential(
142142
torch.nn.Linear(7, 10),
@@ -150,7 +150,7 @@ def torch_clf_model() -> Model:
150150
@pytest.fixture
151151
def torch_reg_model() -> Model:
152152
"""Return a simple torch model."""
153-
import torch
153+
torch = pytest.importorskip("torch")
154154

155155
model = torch.nn.Sequential(
156156
torch.nn.Linear(7, 10),
@@ -166,11 +166,11 @@ def tabpfn_classification_problem(
166166
background_clf_dataset_binary_small,
167167
) -> tuple[Model, np.ndarray, np.ndarray, np.ndarray]:
168168
"""Returns a very simple tabpfn classifier and dataset."""
169-
from tabpfn import TabPFNClassifier
169+
tabpfn = pytest.importorskip("tabpfn")
170170

171171
data, labels = background_clf_dataset_binary_small
172172
data, x_test, labels, _ = train_test_split(data, labels, random_state=42, train_size=8)
173-
model = TabPFNClassifier()
173+
model = tabpfn.TabPFNClassifier()
174174
model.fit(data, labels)
175175
return model, data, labels, x_test
176176

@@ -180,11 +180,11 @@ def tabpfn_regression_problem(
180180
background_reg_dataset_small,
181181
) -> tuple[Model, np.ndarray, np.ndarray, np.ndarray]:
182182
"""Returns a very simple tabpfn regressor and dataset."""
183-
from tabpfn import TabPFNRegressor
183+
tabpfn = pytest.importorskip("tabpfn")
184184

185185
data, labels = background_reg_dataset_small
186186
data, x_test, labels, _ = train_test_split(data, labels, random_state=42, train_size=8)
187-
model = TabPFNRegressor()
187+
model = tabpfn.TabPFNRegressor()
188188
model.fit(data, labels)
189189
return model, data, labels, x_test
190190

@@ -228,21 +228,21 @@ def lr_reg_model(background_reg_dataset) -> LinearRegression:
228228
@pytest.fixture
229229
def lightgbm_reg_model(background_reg_dataset) -> Model:
230230
"""Return a simple lightgbm regression model."""
231-
from lightgbm import LGBMRegressor
231+
lightgbm = pytest.importorskip("lightgbm")
232232

233233
X, y = background_reg_dataset
234-
model = LGBMRegressor(random_state=42, n_estimators=3)
234+
model = lightgbm.LGBMRegressor(random_state=42, n_estimators=3)
235235
model.fit(X, y)
236236
return model
237237

238238

239239
@pytest.fixture
240240
def lightgbm_clf_model(background_clf_dataset) -> Model:
241241
"""Return a simple lightgbm classification model."""
242-
from lightgbm import LGBMClassifier
242+
lightgbm = pytest.importorskip("lightgbm")
243243

244244
X, y = background_clf_dataset
245-
model = LGBMClassifier(random_state=42, n_estimators=3)
245+
model = lightgbm.LGBMClassifier(random_state=42, n_estimators=3)
246246
model.fit(X, y)
247247
return model
248248

tests/markers.py

+38-6
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,43 @@
11
"""This module contains all markers for the tests."""
22

3+
import importlib.util
4+
35
import pytest
46

5-
try:
6-
# marker if the tabpfn library is installed
7-
import tabpfn # noqa: F401
7+
__all__ = [
8+
"skip_if_no_tabpfn",
9+
"skip_if_no_tensorflow",
10+
"skip_if_no_keras",
11+
"skip_if_no_xgboost",
12+
"skip_if_no_lightgbm",
13+
]
14+
15+
16+
def is_installed(pkg_name: str) -> bool:
17+
"""Check if a package is installed without importing it."""
18+
return importlib.util.find_spec(pkg_name) is not None
19+
20+
21+
skip_if_no_tabpfn = pytest.mark.skipif(
22+
not is_installed("tabpfn"), reason="TabPFN is not available."
23+
)
24+
25+
skip_if_no_tensorflow = pytest.mark.skipif(
26+
not is_installed("tensorflow"),
27+
reason="tensorflow is not installed"
28+
)
29+
30+
skip_if_no_xgboost = pytest.mark.skipif(
31+
not is_installed("xgboost"),
32+
reason="xgboost is not installed"
33+
)
34+
35+
skip_if_no_keras = pytest.mark.skipif(
36+
not is_installed("keras"),
37+
reason="keras is not installed"
38+
)
839

9-
importorskip_tabpfn = pytest.mark.skipif(False, reason="tabpfn is installed")
10-
except ImportError:
11-
importorskip_tabpfn = pytest.mark.skip(reason="tabpfn is not installed")
40+
skip_if_no_lightgbm = pytest.mark.skipif(
41+
not is_installed("lightgbm"),
42+
reason="lightgbm is not installed"
43+
)

0 commit comments

Comments
 (0)