Skip to content

Commit ba70673

Browse files
committed
address #966: better error messages for non-gzip files with gz ext
1 parent 5c4f39c commit ba70673

File tree

2 files changed

+59
-0
lines changed

2 files changed

+59
-0
lines changed

nibabel/loadsave.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,47 @@
2020
from .deprecated import deprecate_with_version
2121

2222

23+
def _signature_matches_extension(filename):
24+
"""Check if signature aka magic number matches filename extension.
25+
26+
Parameters
27+
----------
28+
filename : str or os.PathLike
29+
Path to the file to check
30+
31+
Returns
32+
-------
33+
matches : bool
34+
- `True` if the filename extension is not recognized (not .gz nor .bz2)
35+
- `True` if the magic number was successfully read and corresponds to
36+
the format indicated by the extension.
37+
- `False` otherwise.
38+
error_message : str
39+
An error message if opening the file failed or a mismatch is detected;
40+
the empty string otherwise.
41+
42+
"""
43+
signatures = {
44+
".gz": {"signature": b"\x1f\x8b", "format_name": "gzip"},
45+
".bz2": {"signature": b"\x42\x5a\x68", "format_name": "bzip2"}
46+
}
47+
filename = _stringify_path(filename)
48+
*_, ext = splitext_addext(filename)
49+
ext = ext.lower()
50+
if ext not in signatures:
51+
return True, ""
52+
expected_signature = signatures[ext]["signature"]
53+
try:
54+
with open(filename, "rb") as fh:
55+
found_signature = fh.read(len(expected_signature))
56+
except Exception:
57+
return False, f"Could not read file: {filename}"
58+
if found_signature == expected_signature:
59+
return True, ""
60+
format_name = signatures[ext]["format_name"]
61+
return False, f"File {filename} is not a {format_name} file"
62+
63+
2364
def load(filename, **kwargs):
2465
r""" Load file given filename, guessing at file type
2566
@@ -44,6 +85,9 @@ def load(filename, **kwargs):
4485
raise FileNotFoundError(f"No such file or no access: '{filename}'")
4586
if stat_result.st_size <= 0:
4687
raise ImageFileError(f"Empty file: '{filename}'")
88+
matches, msg = _signature_matches_extension(filename)
89+
if not matches:
90+
raise ImageFileError(msg)
4791

4892
sniff = None
4993
for image_klass in all_image_classes:

nibabel/tests/test_loadsave.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,21 @@ def test_load_empty_image():
7676
assert str(err.value).startswith('Empty file: ')
7777

7878

79+
@pytest.mark.parametrize("extension", [".gz", ".bz2"])
80+
def test_load_bad_compressed_extension(tmp_path, extension):
81+
file_path = tmp_path.joinpath(f"img.nii{extension}")
82+
file_path.write_bytes(b"bad")
83+
with pytest.raises(ImageFileError, match=".*is not a .* file"):
84+
load(file_path)
85+
86+
87+
def test_load_file_that_cannot_be_read(tmp_path):
88+
subdir = tmp_path.joinpath("img.nii.gz")
89+
subdir.mkdir()
90+
with pytest.raises(ImageFileError, match="Could not read"):
91+
load(subdir)
92+
93+
7994
def test_read_img_data_nifti():
8095
shape = (2, 3, 4)
8196
data = np.random.normal(size=shape)

0 commit comments

Comments
 (0)