Skip to content

Commit 1fa7eeb

Browse files
committed
fix: extend tests and normalize error types
1 parent b692390 commit 1fa7eeb

File tree

8 files changed

+59
-32
lines changed

8 files changed

+59
-32
lines changed

nitransforms/io/__init__.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
# vi: set ft=python sts=4 ts=4 sw=4 et:
33
"""Read and write transforms."""
44
from nitransforms.io import afni, fsl, itk, lta
5-
from nitransforms.io.base import TransformFileError
5+
from nitransforms.io.base import TransformIOError, TransformFileError
66

77
__all__ = [
88
"afni",
@@ -11,6 +11,7 @@
1111
"lta",
1212
"get_linear_factory",
1313
"TransformFileError",
14+
"TransformIOError",
1415
]
1516

1617
_IO_TYPES = {

nitransforms/io/afni.py

+9-5
Original file line numberDiff line numberDiff line change
@@ -95,12 +95,16 @@ def from_string(cls, string):
9595
if not lines:
9696
raise TransformFileError
9797

98-
parameters = np.vstack(
99-
(
100-
np.genfromtxt([lines[0].encode()], dtype="f8").reshape((3, 4)),
101-
(0.0, 0.0, 0.0, 1.0),
98+
try:
99+
parameters = np.vstack(
100+
(
101+
np.genfromtxt([lines[0].encode()], dtype="f8").reshape((3, 4)),
102+
(0.0, 0.0, 0.0, 1.0),
103+
)
102104
)
103-
)
105+
except ValueError as e:
106+
raise TransformFileError from e
107+
104108
sa["parameters"] = parameters
105109
return tf
106110

nitransforms/io/base.py

+6-2
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,12 @@
66
from ..patched import LabeledWrapStruct
77

88

9-
class TransformFileError(Exception):
10-
"""A custom exception for transform files."""
9+
class TransformIOError(IOError):
10+
"""General I/O exception while reading/writing transforms."""
11+
12+
13+
class TransformFileError(TransformIOError):
14+
"""Specific I/O exception when a file does not meet the expected format."""
1115

1216

1317
class StringBasedStruct(LabeledWrapStruct):

nitransforms/io/fsl.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
BaseLinearTransformList,
1111
LinearParameters,
1212
DisplacementsField,
13+
TransformIOError,
1314
TransformFileError,
1415
_ensure_image,
1516
)
@@ -40,7 +41,7 @@ def from_ras(cls, ras, moving=None, reference=None):
4041
moving = reference
4142

4243
if reference is None:
43-
raise ValueError("Cannot build FSL linear transform without a reference")
44+
raise TransformIOError("Cannot build FSL linear transform without a reference")
4445

4546
reference = _ensure_image(reference)
4647
moving = _ensure_image(moving)
@@ -77,7 +78,7 @@ def from_string(cls, string):
7778
def to_ras(self, moving=None, reference=None):
7879
"""Return a nitransforms internal RAS+ matrix."""
7980
if reference is None:
80-
raise ValueError("Cannot build FSL linear transform without a reference")
81+
raise TransformIOError("Cannot build FSL linear transform without a reference")
8182

8283
if moving is None:
8384
warnings.warn(

nitransforms/io/itk.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
BaseLinearTransformList,
99
DisplacementsField,
1010
LinearParameters,
11+
TransformIOError,
1112
TransformFileError,
1213
)
1314

@@ -306,7 +307,7 @@ def from_filename(cls, filename):
306307
from h5py import File as H5File
307308

308309
if not str(filename).endswith(".h5"):
309-
raise RuntimeError("Extension is not .h5")
310+
raise TransformFileError("Extension is not .h5")
310311

311312
with H5File(str(filename)) as f:
312313
return cls.from_h5obj(f)
@@ -355,7 +356,7 @@ def from_h5obj(cls, fileobj, check=True):
355356
)
356357
continue
357358

358-
raise NotImplementedError(
359+
raise TransformIOError(
359360
f"Unsupported transform type {xfm['TransformType'][0]}"
360361
)
361362

nitransforms/linear.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -211,11 +211,11 @@ def from_filename(cls, filename, fmt=None, reference=None, moving=None):
211211
raise TypeError("Cannot load transform array '%s'" % filename)
212212
matrix = matrix[0]
213213
return cls(matrix, reference=reference)
214-
except TransformFileError:
214+
except (TransformFileError, FileNotFoundError):
215215
continue
216216

217217
raise TransformFileError(
218-
f"Could not open <{filename}> (formats tried: {', '.join(fmtlist)}."
218+
f"Could not open <{filename}> (formats tried: {', '.join(fmtlist)})."
219219
)
220220

221221
def __repr__(self):
@@ -468,11 +468,11 @@ def load(filename, fmt=None, reference=None, moving=None):
468468
469469
Examples
470470
--------
471-
>>> xfm = load(regress_dir / "affine-LAS.itk.tfm", fmt="itk")
471+
>>> xfm = load(regress_dir / "affine-LAS.itk.tfm")
472472
>>> isinstance(xfm, Affine)
473473
True
474474
475-
>>> xfm = load(regress_dir / "itktflist.tfm", fmt="itk")
475+
>>> xfm = load(regress_dir / "itktflist.tfm")
476476
>>> isinstance(xfm, LinearTransformsMapping)
477477
True
478478

nitransforms/tests/test_io.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
FSLinearTransform as LT,
2525
FSLinearTransformArray as LTA,
2626
)
27-
from ..io.base import LinearParameters, TransformFileError
27+
from ..io.base import LinearParameters, TransformIOError, TransformFileError
2828

2929
LPS = np.diag([-1, -1, 1, 1])
3030
ITK_MAT = LPS.dot(np.ones((4, 4)).dot(LPS))
@@ -224,7 +224,7 @@ def test_Linear_common(tmpdir, data_path, sw, image_orientation, get_testdata):
224224

225225
# Test without images
226226
if sw == "fsl":
227-
with pytest.raises(ValueError):
227+
with pytest.raises(TransformIOError):
228228
factory.from_ras(RAS)
229229
else:
230230
xfm = factory.from_ras(RAS)
@@ -422,7 +422,7 @@ def test_itk_h5(testdata_path):
422422
== 2
423423
)
424424

425-
with pytest.raises(RuntimeError):
425+
with pytest.raises(TransformFileError):
426426
list(
427427
itk.ITKCompositeH5.from_filename(
428428
testdata_path

nitransforms/tests/test_linear.py

+29-13
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,18 @@ def test_linear_valueerror():
5454
nitl.Affine(np.ones((4, 4)))
5555

5656

57+
def test_linear_load_unsupported(data_path):
58+
"""Exercise loading transform without I/O implementation."""
59+
with pytest.raises(TypeError):
60+
nitl.load(data_path / "itktflist2.tfm", fmt="X5")
61+
62+
63+
def test_linear_load_mistaken(data_path):
64+
"""Exercise loading transform without I/O implementation."""
65+
with pytest.raises(io.TransformFileError):
66+
nitl.load(data_path / "itktflist2.tfm", fmt="afni")
67+
68+
5769
def test_loadsave_itk(tmp_path, data_path, testdata_path):
5870
"""Test idempotency."""
5971
ref_file = testdata_path / "someones_anatomy.nii.gz"
@@ -73,9 +85,13 @@ def test_loadsave_itk(tmp_path, data_path, testdata_path):
7385
)
7486

7587

88+
@pytest.mark.parametrize("autofmt", (False, True))
7689
@pytest.mark.parametrize("fmt", ["itk", "fsl", "afni", "lta"])
77-
def test_loadsave(tmp_path, data_path, testdata_path, fmt):
90+
def test_loadsave(tmp_path, data_path, testdata_path, autofmt, fmt):
7891
"""Test idempotency."""
92+
supplied_fmt = None if autofmt else fmt
93+
94+
# Load reference transform
7995
ref_file = testdata_path / "someones_anatomy.nii.gz"
8096
xfm = nitl.load(data_path / "itktflist2.tfm", fmt="itk")
8197
xfm.reference = ref_file
@@ -85,33 +101,33 @@ def test_loadsave(tmp_path, data_path, testdata_path, fmt):
85101

86102
if fmt == "fsl":
87103
# FSL should not read a transform without reference
88-
with pytest.raises(ValueError):
89-
nitl.load(fname, fmt=fmt)
90-
nitl.load(fname, fmt=fmt, moving=ref_file)
104+
with pytest.raises(io.TransformIOError):
105+
nitl.load(fname, fmt=supplied_fmt)
106+
nitl.load(fname, fmt=supplied_fmt, moving=ref_file)
91107

92108
with pytest.warns(UserWarning):
93109
assert np.allclose(
94110
xfm.matrix,
95-
nitl.load(fname, fmt=fmt, reference=ref_file).matrix,
111+
nitl.load(fname, fmt=supplied_fmt, reference=ref_file).matrix,
96112
)
97113

98114
assert np.allclose(
99115
xfm.matrix,
100-
nitl.load(fname, fmt=fmt, reference=ref_file, moving=ref_file).matrix,
116+
nitl.load(fname, fmt=supplied_fmt, reference=ref_file, moving=ref_file).matrix,
101117
)
102118
else:
103-
assert xfm == nitl.load(fname, fmt=fmt, reference=ref_file)
119+
assert xfm == nitl.load(fname, fmt=supplied_fmt, reference=ref_file)
104120

105121
xfm.to_filename(fname, fmt=fmt, moving=ref_file)
106122

107123
if fmt == "fsl":
108124
assert np.allclose(
109125
xfm.matrix,
110-
nitl.load(fname, fmt=fmt, reference=ref_file, moving=ref_file).matrix,
126+
nitl.load(fname, fmt=supplied_fmt, reference=ref_file, moving=ref_file).matrix,
111127
rtol=1e-2, # FSL incurs into large errors due to rounding
112128
)
113129
else:
114-
assert xfm == nitl.load(fname, fmt=fmt, reference=ref_file)
130+
assert xfm == nitl.load(fname, fmt=supplied_fmt, reference=ref_file)
115131

116132
ref_file = testdata_path / "someones_anatomy.nii.gz"
117133
xfm = nitl.load(data_path / "affine-LAS.itk.tfm", fmt="itk")
@@ -121,21 +137,21 @@ def test_loadsave(tmp_path, data_path, testdata_path, fmt):
121137
if fmt == "fsl":
122138
assert np.allclose(
123139
xfm.matrix,
124-
nitl.load(fname, fmt=fmt, reference=ref_file, moving=ref_file).matrix,
140+
nitl.load(fname, fmt=supplied_fmt, reference=ref_file, moving=ref_file).matrix,
125141
rtol=1e-2, # FSL incurs into large errors due to rounding
126142
)
127143
else:
128-
assert xfm == nitl.load(fname, fmt=fmt, reference=ref_file)
144+
assert xfm == nitl.load(fname, fmt=supplied_fmt, reference=ref_file)
129145

130146
xfm.to_filename(fname, fmt=fmt, moving=ref_file)
131147
if fmt == "fsl":
132148
assert np.allclose(
133149
xfm.matrix,
134-
nitl.load(fname, fmt=fmt, reference=ref_file, moving=ref_file).matrix,
150+
nitl.load(fname, fmt=supplied_fmt, reference=ref_file, moving=ref_file).matrix,
135151
rtol=1e-2, # FSL incurs into large errors due to rounding
136152
)
137153
else:
138-
assert xfm == nitl.load(fname, fmt=fmt, reference=ref_file)
154+
assert xfm == nitl.load(fname, fmt=supplied_fmt, reference=ref_file)
139155

140156

141157
@pytest.mark.parametrize("image_orientation", ["RAS", "LAS", "LPS", "oblique"])

0 commit comments

Comments
 (0)