Skip to content

Commit 5254601

Browse files
committed
ENH: Allow path-like objects for tractogram load/saving
Allow path-like objects for tractogram load/saving. Crucially, this patch set uses the capabilities of `pathlib.Path` to get the suffix of a filename when detecing the format instead of relying on the `splitext` function of the `os.path` module, which requires a `str` instance. Add the corresponding tests. Change the docstrings to mark the types of the filenames as `path-like` objects instead of only `str` objects.
1 parent 30a5f3f commit 5254601

File tree

4 files changed

+50
-22
lines changed

4 files changed

+50
-22
lines changed

nibabel/streamlines/__init__.py

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import os
44
import warnings
5+
from pathlib import Path
56

67
from .array_sequence import ArraySequence
78
from .header import Field
@@ -22,8 +23,8 @@ def is_supported(fileobj):
2223
2324
Parameters
2425
----------
25-
fileobj : string or file-like object
26-
If string, a filename; otherwise an open file-like object pointing
26+
fileobj : path-like or file-like object
27+
If path-like, a filename; otherwise an open file-like object pointing
2728
to a streamlines file (and ready to read from the beginning of the
2829
header)
2930
@@ -39,8 +40,8 @@ def detect_format(fileobj):
3940
4041
Parameters
4142
----------
42-
fileobj : string or file-like object
43-
If string, a filename; otherwise an open file-like object pointing
43+
fileobj : path-like or file-like object
44+
If path-like, a filename; otherwise an open file-like object pointing
4445
to a tractogram file (and ready to read from the beginning of the
4546
header)
4647
@@ -56,8 +57,8 @@ def detect_format(fileobj):
5657
except OSError:
5758
pass
5859

59-
if isinstance(fileobj, str):
60-
_, ext = os.path.splitext(fileobj)
60+
if isinstance(fileobj, (str, Path)):
61+
ext = Path(fileobj).suffix
6162
return FORMATS.get(ext.lower())
6263

6364
return None
@@ -68,8 +69,8 @@ def load(fileobj, lazy_load=False):
6869
6970
Parameters
7071
----------
71-
fileobj : string or file-like object
72-
If string, a filename; otherwise an open file-like object
72+
fileobj : path-like or file-like object
73+
If path-like, a filename; otherwise an open file-like object
7374
pointing to a streamlines file (and ready to read from the beginning
7475
of the streamlines file's header).
7576
lazy_load : {False, True}, optional
@@ -106,7 +107,7 @@ def save(tractogram, filename, **kwargs):
106107
provided keyword arguments.
107108
If :class:`TractogramFile` object, the file format is known and will
108109
be used to save its content to `filename`.
109-
filename : str
110+
filename : path-like object
110111
Name of the file where the tractogram will be saved.
111112
\*\*kwargs : keyword arguments
112113
Keyword arguments passed to :class:`TractogramFile` constructor.

nibabel/streamlines/tck.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -112,8 +112,8 @@ def load(cls, fileobj, lazy_load=False):
112112
113113
Parameters
114114
----------
115-
fileobj : string or file-like object
116-
If string, a filename; otherwise an open file-like object in
115+
fileobj : path-like or file-like object
116+
If path-like, a filename; otherwise an open file-like object in
117117
binary mode pointing to TCK file (and ready to read from the
118118
beginning of the TCK header). Note that calling this function
119119
does not change the file position.
@@ -167,8 +167,8 @@ def save(self, fileobj):
167167
168168
Parameters
169169
----------
170-
fileobj : string or file-like object
171-
If string, a filename; otherwise an open file-like object in
170+
fileobj : path-like or or file-like object
171+
If path-like, a filename; otherwise an open file-like object in
172172
binary mode pointing to TCK file (and ready to write from the
173173
beginning of the TCK header data).
174174
"""
@@ -403,8 +403,8 @@ def _read(cls, fileobj, header, buffer_size=4):
403403
404404
Parameters
405405
----------
406-
fileobj : string or file-like object
407-
If string, a filename; otherwise an open file-like object in
406+
fileobj : path-like or file-like object
407+
If path-like, a filename; otherwise an open file-like object in
408408
binary mode pointing to TCK file (and ready to read from the
409409
beginning of the TCK header). Note that calling this function
410410
does not change the file position.

nibabel/streamlines/tests/test_streamlines.py

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import os
22
import unittest
33
import warnings
4+
from pathlib import Path
45
from io import BytesIO
56
from os.path import join as pjoin
67

@@ -90,6 +91,7 @@ def test_is_supported_detect_format(tmp_path):
9091
assert not nib.streamlines.is_supported('')
9192
assert nib.streamlines.detect_format(f) is None
9293
assert nib.streamlines.detect_format('') is None
94+
assert nib.streamlines.detect_format(Path('')) is None
9395

9496
# Valid file without extension
9597
for tfile_cls in FORMATS.values():
@@ -128,6 +130,12 @@ def test_is_supported_detect_format(tmp_path):
128130
assert nib.streamlines.is_supported(f)
129131
assert nib.streamlines.detect_format(f) == tfile_cls
130132

133+
# Good extension, Path only
134+
for ext, tfile_cls in FORMATS.items():
135+
f = Path('my_tractogram' + ext)
136+
assert nib.streamlines.is_supported(f)
137+
assert nib.streamlines.detect_format(f) == tfile_cls
138+
131139
# Extension should not be case-sensitive.
132140
for ext, tfile_cls in FORMATS.items():
133141
f = 'my_tractogram' + ext.upper()
@@ -149,7 +157,7 @@ def test_load_empty_file(self):
149157
with pytest.warns(Warning) if lazy_load else error_warnings():
150158
assert_tractogram_equal(tfile.tractogram, DATA['empty_tractogram'])
151159

152-
def test_load_simple_file(self):
160+
def test_load_simple_file_str(self):
153161
for lazy_load in [False, True]:
154162
for simple_filename in DATA['simple_filenames']:
155163
tfile = nib.streamlines.load(simple_filename, lazy_load=lazy_load)
@@ -163,6 +171,20 @@ def test_load_simple_file(self):
163171
with pytest.warns(Warning) if lazy_load else error_warnings():
164172
assert_tractogram_equal(tfile.tractogram, DATA['simple_tractogram'])
165173

174+
def test_load_simple_file_path(self):
175+
for lazy_load in [False, True]:
176+
for simple_filename in DATA['simple_filenames']:
177+
tfile = nib.streamlines.load(Path(simple_filename), lazy_load=lazy_load)
178+
assert isinstance(tfile, TractogramFile)
179+
180+
if lazy_load:
181+
assert type(tfile.tractogram), Tractogram
182+
else:
183+
assert type(tfile.tractogram), LazyTractogram
184+
185+
with pytest.warns(Warning) if lazy_load else error_warnings():
186+
assert_tractogram_equal(tfile.tractogram, DATA['simple_tractogram'])
187+
166188
def test_load_complex_file(self):
167189
for lazy_load in [False, True]:
168190
for complex_filename in DATA['complex_filenames']:
@@ -205,6 +227,11 @@ def test_save_tractogram_file(self):
205227
tfile = nib.streamlines.load('dummy.trk', lazy_load=False)
206228
assert_tractogram_equal(tfile.tractogram, tractogram)
207229

230+
with InTemporaryDirectory():
231+
nib.streamlines.save(trk_file, Path('dummy.trk'))
232+
tfile = nib.streamlines.load('dummy.trk', lazy_load=False)
233+
assert_tractogram_equal(tfile.tractogram, tractogram)
234+
208235
def test_save_empty_file(self):
209236
tractogram = Tractogram(affine_to_rasmm=np.eye(4))
210237
for ext, cls in FORMATS.items():

nibabel/streamlines/trk.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -294,8 +294,8 @@ def load(cls, fileobj, lazy_load=False):
294294
295295
Parameters
296296
----------
297-
fileobj : string or file-like object
298-
If string, a filename; otherwise an open file-like object
297+
fileobj : path-like or file-like object
298+
If path-like, a filename; otherwise an open file-like object
299299
pointing to TRK file (and ready to read from the beginning
300300
of the TRK header). Note that calling this function
301301
does not change the file position.
@@ -401,8 +401,8 @@ def save(self, fileobj):
401401
402402
Parameters
403403
----------
404-
fileobj : string or file-like object
405-
If string, a filename; otherwise an open file-like object
404+
fileobj : path-like or file-like object
405+
If path-like, a filename; otherwise an open file-like object
406406
pointing to TRK file (and ready to write from the beginning
407407
of the TRK header data).
408408
"""
@@ -550,8 +550,8 @@ def _read_header(fileobj):
550550
551551
Parameters
552552
----------
553-
fileobj : string or file-like object
554-
If string, a filename; otherwise an open file-like object
553+
fileobj : path-like or file-like object
554+
If path-like, a filename; otherwise an open file-like object
555555
pointing to TRK file (and ready to read from the beginning
556556
of the TRK header). Note that calling this function
557557
does not change the file position.

0 commit comments

Comments
 (0)