Skip to content

Commit

Permalink
Merge pull request #78 from effigies/fix/py310
Browse files Browse the repository at this point in the history
fix: Test on Python 3.10+, use typing_extensions backports
  • Loading branch information
jhlegarreta authored Jan 27, 2025
2 parents a2d6eac + 2008a6c commit 6355737
Show file tree
Hide file tree
Showing 6 changed files with 23 additions and 14 deletions.
5 changes: 4 additions & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,9 @@ jobs:
# Unit tests only on Linux/Python 3.12
runs-on: 'ubuntu-latest'
needs: ['cache-test-data']
strategy:
matrix:
python-version: ['3.10', '3.11', '3.12']
steps:
- uses: actions/checkout@v4
with:
Expand All @@ -93,7 +96,7 @@ jobs:
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: '3.12'
python-version: ${{ matrix.python-version }}
- name: Install the latest version of uv
uses: astral-sh/setup-uv@v5
- name: Install ANTs
Expand Down
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ dependencies = [
"scikit-image>=0.15.0",
"scikit_learn>=1.3.0",
"scipy>=1.8.0",
"typing_extensions >=4.12",
]
dynamic = ["version"]

Expand Down
10 changes: 5 additions & 5 deletions src/nifreeze/data/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,15 @@
from collections import namedtuple
from pathlib import Path
from tempfile import mkdtemp
from typing import Any, Generic, TypeVarTuple
from typing import Any, Generic

import attr
import h5py
import nibabel as nb
import numpy as np
from nibabel.spatialimages import SpatialHeader, SpatialImage
from nitransforms.linear import Affine
from typing_extensions import TypeVarTuple, Unpack

from nifreeze.utils.ndimage import load_api

Expand All @@ -58,7 +59,7 @@ def _cmp(lh: Any, rh: Any) -> bool:


@attr.s(slots=True)
class BaseDataset(Generic[*Ts]):
class BaseDataset(Generic[Unpack[Ts]]):
"""
Base dataset representation structure.
Expand Down Expand Up @@ -99,13 +100,12 @@ def __len__(self) -> int:

return self.dataobj.shape[-1]

def _getextra(self, idx: int | slice | tuple | np.ndarray) -> tuple[*Ts]:
# PY312: Default values for TypeVarTuples are not yet supported
def _getextra(self, idx: int | slice | tuple | np.ndarray) -> tuple[Unpack[Ts]]:
return () # type: ignore[return-value]

def __getitem__(
self, idx: int | slice | tuple | np.ndarray
) -> tuple[np.ndarray, np.ndarray | None, *Ts]:
) -> tuple[np.ndarray, np.ndarray | None, Unpack[Ts]]:
"""
Returns volume(s) and corresponding affine(s) through fancy indexing.
Expand Down
3 changes: 2 additions & 1 deletion src/nifreeze/estimator.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,10 @@

from pathlib import Path
from tempfile import TemporaryDirectory
from typing import Self, TypeVar
from typing import TypeVar

from tqdm import tqdm
from typing_extensions import Self

from nifreeze.data.base import BaseDataset
from nifreeze.model.base import BaseModel, ModelFactory
Expand Down
12 changes: 6 additions & 6 deletions test/test_data_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,25 +53,25 @@ def test_len(random_dataset: BaseDataset):
assert len(random_dataset) == 5 # last dimension is 5 volumes


def test_getitem_volume_index(random_dataset: BaseDataset[()]):
def test_getitem_volume_index(random_dataset: BaseDataset):
"""
Test that __getitem__ returns the correct (volume, affine) tuple.
By default, motion_affines is None, so we expect to get None for the affine.
"""
# Single volume
volume0, aff0 = random_dataset[0]
# Single volume # Note that the type ignore can be removed once we can use *Ts
volume0, aff0 = random_dataset[0] # type: ignore[misc] # PY310
assert volume0.shape == (32, 32, 32)
# No transforms have been applied yet, so there's no motion_affines array
assert aff0 is None

# Slice of volumes
volume_slice, aff_slice = random_dataset[2:4]
volume_slice, aff_slice = random_dataset[2:4] # type: ignore[misc] # PY310
assert volume_slice.shape == (32, 32, 32, 2)
assert aff_slice is None


def test_set_transform(random_dataset: BaseDataset[()]):
def test_set_transform(random_dataset: BaseDataset):
"""
Test that calling set_transform changes the data and motion_affines.
For simplicity, we'll apply an identity transform and check that motion_affines is updated.
Expand All @@ -83,7 +83,7 @@ def test_set_transform(random_dataset: BaseDataset[()]):
random_dataset.set_transform(idx, affine, order=1)

# Data shouldn't have changed (since transform is identity).
volume0, aff0 = random_dataset[idx]
volume0, aff0 = random_dataset[idx] # type: ignore[misc] # PY310
assert np.allclose(data_before, volume0)

# motion_affines should be created and match the transform matrix.
Expand Down
6 changes: 5 additions & 1 deletion tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,17 @@
requires =
tox>=4
envlist =
py312, notebooks
py3{10,11,12,13}
notebooks
skip_missing_interpreters = true

# Configuration that allows us to split tests across GitHub runners effectively
[gh-actions]
python =
3.10: py310
3.11: py311
3.12: py312, notebooks
3.13: py313

[testenv]
description = Pytest with coverage
Expand Down

0 comments on commit 6355737

Please sign in to comment.