Skip to content

Commit eb54bde

Browse files
cbrnrlarsonerdrammock
authored
Improve read_raw file extension detection (mne-tools#11521)
Co-authored-by: Eric Larson <[email protected]> Co-authored-by: Daniel McCloy <[email protected]>
1 parent fa618df commit eb54bde

File tree

3 files changed

+41
-5
lines changed

3 files changed

+41
-5
lines changed

doc/changes/latest.inc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ Bugs
5454
- All functions accepting paths can now correctly handle :class:`~pathlib.Path` as input. Historically, we expected strings (instead of "proper" path objects), and only added :class:`~pathlib.Path` support in a few select places, leading to inconsistent behavior. (:gh:`11473` and :gh:`11499` by `Mathieu Scheltienne`_)
5555
- Fix visualization dialog compatibility with matplotlib 3.7 (:gh:`11409` by `Daniel McCloy`_ and `Eric Larson`_)
5656
- Expand tilde (user directory) in config keys (:gh:`11537` by `Clemens Brunner`_)
57+
- Fix :func:`mne.io.read_raw` for file names containing multiple dots (:gh:`11521` by `Clemens Brunner`_)
5758
5859
API changes
5960
~~~~~~~~~~~

mne/io/_read_raw.py

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@
1212
read_raw_fif, read_raw_eeglab, read_raw_cnt, read_raw_egi,
1313
read_raw_eximia, read_raw_nirx, read_raw_fieldtrip,
1414
read_raw_artemis123, read_raw_nicolet, read_raw_kit,
15-
read_raw_ctf, read_raw_boxy, read_raw_snirf, read_raw_fil)
15+
read_raw_ctf, read_raw_boxy, read_raw_snirf, read_raw_fil,
16+
read_raw_nihon)
1617
from ..utils import fill_doc
1718

1819

@@ -29,6 +30,7 @@ def _read_unsupported(fname, **kwargs):
2930
# supported read file formats
3031
supported = {
3132
".edf": dict(EDF=read_raw_edf),
33+
".eeg": dict(NihonKoden=read_raw_nihon),
3234
".bdf": dict(BDF=read_raw_bdf),
3335
".gdf": dict(GDF=read_raw_gdf),
3436
".vhdr": dict(brainvision=read_raw_brainvision),
@@ -57,13 +59,23 @@ def _read_unsupported(fname, **kwargs):
5759
suggested = {
5860
".vmrk": dict(brainvision=partial(_read_unsupported, suggest=".vhdr")),
5961
".amrk": dict(brainvision=partial(_read_unsupported, suggest=".ahdr")),
60-
".eeg": dict(brainvision=partial(_read_unsupported, suggest=".vhdr")),
6162
}
6263

6364
# all known file formats
6465
readers = {**supported, **suggested}
6566

6667

68+
def split_name_ext(fname):
69+
"""Return name and supported file extension."""
70+
maxsuffixes = max(ext.count(".") for ext in supported)
71+
suffixes = Path(fname).suffixes
72+
for si in range(-maxsuffixes, 0):
73+
ext = "".join(suffixes[si:]).lower()
74+
if ext in readers:
75+
return Path(fname).name[:-len(ext)], ext
76+
return fname, None # unknown file extension
77+
78+
6779
@fill_doc
6880
def read_raw(fname, *, preload=False, verbose=None, **kwargs):
6981
"""Read raw file.
@@ -99,7 +111,7 @@ def read_raw(fname, *, preload=False, verbose=None, **kwargs):
99111
raw : mne.io.Raw
100112
Raw object.
101113
"""
102-
ext = "".join(Path(fname).suffixes)
114+
_, ext = split_name_ext(fname)
103115
kwargs['verbose'] = verbose
104116
kwargs['preload'] = preload
105117
if ext not in readers:

mne/io/tests/test_read_raw.py

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,13 @@
55
# License: BSD-3-Clause
66

77
from pathlib import Path
8+
from shutil import copyfile
89

910
import pytest
1011

11-
from mne.io import read_raw
1212
from mne.datasets import testing
13+
from mne.io import read_raw
14+
from mne.io._read_raw import split_name_ext, readers
1315

1416

1517
base = Path(__file__).parent.parent
@@ -32,7 +34,7 @@ def test_read_raw_unsupported_multi(fname, tmp_path):
3234
read_raw(fname)
3335

3436

35-
@pytest.mark.parametrize('fname', ['x.vmrk', 'x.eeg'])
37+
@pytest.mark.parametrize('fname', ['x.vmrk', 'y.amrk'])
3638
def test_read_raw_suggested(fname):
3739
"""Test handling of unsupported file types with suggested alternatives."""
3840
with pytest.raises(ValueError, match='Try reading'):
@@ -43,6 +45,8 @@ def test_read_raw_suggested(fname):
4345

4446

4547
@pytest.mark.parametrize('fname', [
48+
base / 'tests/data/test_raw.fif',
49+
base / 'tests/data/test_raw.fif.gz',
4650
base / 'edf/tests/data/test.edf',
4751
base / 'edf/tests/data/test.bdf',
4852
base / 'brainvision/tests/data/test.vhdr',
@@ -66,3 +70,22 @@ def test_read_raw_supported(fname):
6670
read_raw(fname, verbose=False)
6771
raw = read_raw(fname, preload=True)
6872
assert "data loaded" in str(raw)
73+
74+
75+
def test_split_name_ext():
76+
"""Test file name extension splitting."""
77+
# test known extensions
78+
for ext in readers:
79+
assert split_name_ext(f"test{ext}")[1] == ext
80+
81+
# test unsupported extensions
82+
for ext in ("this.is.not.supported", "a.b.c.d.e", "fif.gz.xyz"):
83+
assert split_name_ext(f"test{ext}")[1] is None
84+
85+
86+
def test_read_raw_multiple_dots(tmp_path):
87+
"""Test if file names with multiple dots work correctly."""
88+
src = base / 'edf/tests/data/test.edf'
89+
dst = tmp_path / "test.this.file.edf"
90+
copyfile(src, dst)
91+
read_raw(dst)

0 commit comments

Comments
 (0)