From d1b2dc967065f55c9c37ee8a7ff3cf5453be1370 Mon Sep 17 00:00:00 2001 From: Clemens Brunner Date: Thu, 2 Mar 2023 09:35:30 +0100 Subject: [PATCH 01/13] Improve read_raw file extension detection --- mne/io/_read_raw.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/mne/io/_read_raw.py b/mne/io/_read_raw.py index e70a571e03b..2706477f1b5 100644 --- a/mne/io/_read_raw.py +++ b/mne/io/_read_raw.py @@ -64,6 +64,17 @@ def _read_unsupported(fname, **kwargs): readers = {**supported, **suggested} +def split_name_ext(fname): + """Return name and supported file extension.""" + maxsuffixes = max([ext.count(".") for ext in supported]) + suffixes = Path(fname).suffixes + for i in range(-maxsuffixes, 0): + ext = "".join(suffixes[i:]).lower() + if ext in readers.keys(): + return Path(fname).name[:-len(ext)], ext + return fname, None # unknown file extension + + @fill_doc def read_raw(fname, *, preload=False, verbose=None, **kwargs): """Read raw file. @@ -99,7 +110,7 @@ def read_raw(fname, *, preload=False, verbose=None, **kwargs): raw : mne.io.Raw Raw object. """ - ext = "".join(Path(fname).suffixes) + _, ext = split_name_ext(fname) kwargs['verbose'] = verbose kwargs['preload'] = preload if ext not in readers: From 948c46d2c76ff1cf3b94c6ca13e428c1a7ed7070 Mon Sep 17 00:00:00 2001 From: Clemens Brunner Date: Fri, 3 Mar 2023 11:27:52 +0100 Subject: [PATCH 02/13] Add test --- mne/io/tests/test_read_raw.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/mne/io/tests/test_read_raw.py b/mne/io/tests/test_read_raw.py index da95e816a6e..d861c86e9a4 100644 --- a/mne/io/tests/test_read_raw.py +++ b/mne/io/tests/test_read_raw.py @@ -8,8 +8,9 @@ import pytest -from mne.io import read_raw from mne.datasets import testing +from mne.io import read_raw +from mne.io._read_raw import split_name_ext, supported base = Path(__file__).parent.parent @@ -43,6 +44,8 @@ def test_read_raw_suggested(fname): @pytest.mark.parametrize('fname', [ + base / 'tests/data/test_raw.fif', + base / 'tests/data/test_raw.fif.gz', base / 'edf/tests/data/test.edf', base / 'edf/tests/data/test.bdf', base / 'brainvision/tests/data/test.vhdr', @@ -66,3 +69,14 @@ def test_read_raw_supported(fname): read_raw(fname, verbose=False) raw = read_raw(fname, preload=True) assert "data loaded" in str(raw) + + +def test_split_name_ext(): + """Test file name extension splitting.""" + # test supported extensions + for ext in supported.keys(): + assert split_name_ext(f"test{ext}")[1] == ext + + # test unsupported extensions + for ext in ("this.is.not.supported", "a.b.c.d.e", "fif.gz.xyz"): + assert split_name_ext(f"test{ext}")[1] is None From d0dff67a599c2b6c2d69f936695532e50bd23de1 Mon Sep 17 00:00:00 2001 From: Clemens Brunner Date: Fri, 3 Mar 2023 11:29:13 +0100 Subject: [PATCH 03/13] Add Nihon Kohden --- mne/io/_read_raw.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mne/io/_read_raw.py b/mne/io/_read_raw.py index 2706477f1b5..e52b5f93694 100644 --- a/mne/io/_read_raw.py +++ b/mne/io/_read_raw.py @@ -10,7 +10,7 @@ from . import (read_raw_edf, read_raw_bdf, read_raw_gdf, read_raw_brainvision, read_raw_fif, read_raw_eeglab, read_raw_cnt, read_raw_egi, - read_raw_eximia, read_raw_nirx, read_raw_fieldtrip, + read_raw_eximia, read_raw_nirx, read_raw_fieldtrip, read_raw_nihon, read_raw_artemis123, read_raw_nicolet, read_raw_kit, read_raw_ctf, read_raw_boxy, read_raw_snirf, read_raw_fil) from ..utils import fill_doc @@ -29,6 +29,7 @@ def _read_unsupported(fname, **kwargs): # supported read file formats supported = { ".edf": dict(EDF=read_raw_edf), + ".eeg": dict(NihonKoden=read_raw_nihon), ".bdf": dict(BDF=read_raw_bdf), ".gdf": dict(GDF=read_raw_gdf), ".vhdr": dict(brainvision=read_raw_brainvision), @@ -57,7 +58,6 @@ def _read_unsupported(fname, **kwargs): suggested = { ".vmrk": dict(brainvision=partial(_read_unsupported, suggest=".vhdr")), ".amrk": dict(brainvision=partial(_read_unsupported, suggest=".ahdr")), - ".eeg": dict(brainvision=partial(_read_unsupported, suggest=".vhdr")), } # all known file formats From 4eafc9e8254530bfd4c83025f8a4f482de1524ad Mon Sep 17 00:00:00 2001 From: Clemens Brunner Date: Fri, 3 Mar 2023 11:31:35 +0100 Subject: [PATCH 04/13] Add changelog entry --- doc/changes/latest.inc | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/changes/latest.inc b/doc/changes/latest.inc index 18f5c7e29e7..a052252f576 100644 --- a/doc/changes/latest.inc +++ b/doc/changes/latest.inc @@ -53,6 +53,7 @@ Bugs - Fix bug with :func:`mne.gui.locate_ieeg` where Freesurfer ``?h.pial.T1`` was not recognized and suppress excess logging (:gh:`11489` by `Alex Rockhill`_) - 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`_) - Fix visualization dialog compatibility with matplotlib 3.7 (:gh:`11409` by `Daniel McCloy`_ and `Eric Larson`_) +- Fix :func:`mne.io.read_raw` for file name containing multiple dots (:gh:`11521` by `Clemens Brunner`_) API changes ~~~~~~~~~~~ From 46bf2d30ca411763b58bc4c6aa7a9c4e3cf2e19c Mon Sep 17 00:00:00 2001 From: Clemens Brunner Date: Fri, 3 Mar 2023 11:31:51 +0100 Subject: [PATCH 05/13] Typo --- doc/changes/latest.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/changes/latest.inc b/doc/changes/latest.inc index a052252f576..b505a9a306a 100644 --- a/doc/changes/latest.inc +++ b/doc/changes/latest.inc @@ -53,7 +53,7 @@ Bugs - Fix bug with :func:`mne.gui.locate_ieeg` where Freesurfer ``?h.pial.T1`` was not recognized and suppress excess logging (:gh:`11489` by `Alex Rockhill`_) - 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`_) - Fix visualization dialog compatibility with matplotlib 3.7 (:gh:`11409` by `Daniel McCloy`_ and `Eric Larson`_) -- Fix :func:`mne.io.read_raw` for file name containing multiple dots (:gh:`11521` by `Clemens Brunner`_) +- Fix :func:`mne.io.read_raw` for file names containing multiple dots (:gh:`11521` by `Clemens Brunner`_) API changes ~~~~~~~~~~~ From 56e3ab769cba40768daa64b583a5fca742fdd618 Mon Sep 17 00:00:00 2001 From: Clemens Brunner Date: Fri, 3 Mar 2023 12:38:56 +0100 Subject: [PATCH 06/13] Fix PEP8 --- mne/io/_read_raw.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/mne/io/_read_raw.py b/mne/io/_read_raw.py index e52b5f93694..78b4b636243 100644 --- a/mne/io/_read_raw.py +++ b/mne/io/_read_raw.py @@ -10,9 +10,10 @@ from . import (read_raw_edf, read_raw_bdf, read_raw_gdf, read_raw_brainvision, read_raw_fif, read_raw_eeglab, read_raw_cnt, read_raw_egi, - read_raw_eximia, read_raw_nirx, read_raw_fieldtrip, read_raw_nihon, + read_raw_eximia, read_raw_nirx, read_raw_fieldtrip, read_raw_artemis123, read_raw_nicolet, read_raw_kit, - read_raw_ctf, read_raw_boxy, read_raw_snirf, read_raw_fil) + read_raw_ctf, read_raw_boxy, read_raw_snirf, read_raw_fil, + read_raw_nihon) from ..utils import fill_doc From 8888aa81a4a19bed67b03021ed4c224ea89e750e Mon Sep 17 00:00:00 2001 From: Clemens Brunner Date: Fri, 3 Mar 2023 12:40:20 +0100 Subject: [PATCH 07/13] Fix test --- mne/io/tests/test_read_raw.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mne/io/tests/test_read_raw.py b/mne/io/tests/test_read_raw.py index d861c86e9a4..a81e18dc701 100644 --- a/mne/io/tests/test_read_raw.py +++ b/mne/io/tests/test_read_raw.py @@ -33,7 +33,7 @@ def test_read_raw_unsupported_multi(fname, tmp_path): read_raw(fname) -@pytest.mark.parametrize('fname', ['x.vmrk', 'x.eeg']) +@pytest.mark.parametrize('fname', ['x.vmrk', 'y.amrk']) def test_read_raw_suggested(fname): """Test handling of unsupported file types with suggested alternatives.""" with pytest.raises(ValueError, match='Try reading'): From 418f375adf6e1340adff55fbd0e70693db70d685 Mon Sep 17 00:00:00 2001 From: Clemens Brunner Date: Fri, 3 Mar 2023 17:13:01 +0100 Subject: [PATCH 08/13] Update mne/io/_read_raw.py Co-authored-by: Eric Larson --- mne/io/_read_raw.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mne/io/_read_raw.py b/mne/io/_read_raw.py index 78b4b636243..1dc5ba4af58 100644 --- a/mne/io/_read_raw.py +++ b/mne/io/_read_raw.py @@ -69,8 +69,8 @@ def split_name_ext(fname): """Return name and supported file extension.""" maxsuffixes = max([ext.count(".") for ext in supported]) suffixes = Path(fname).suffixes - for i in range(-maxsuffixes, 0): - ext = "".join(suffixes[i:]).lower() + for si in range(-maxsuffixes, 0): + ext = "".join(suffixes[si:]).lower() if ext in readers.keys(): return Path(fname).name[:-len(ext)], ext return fname, None # unknown file extension From fbce81dbd35530bcdd9cce9ad34de152facba5e3 Mon Sep 17 00:00:00 2001 From: Clemens Brunner Date: Tue, 7 Mar 2023 08:44:45 +0100 Subject: [PATCH 09/13] Add new test --- mne/io/tests/test_read_raw.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/mne/io/tests/test_read_raw.py b/mne/io/tests/test_read_raw.py index a81e18dc701..b0251a6e6cd 100644 --- a/mne/io/tests/test_read_raw.py +++ b/mne/io/tests/test_read_raw.py @@ -5,6 +5,7 @@ # License: BSD-3-Clause from pathlib import Path +from shutil import copyfile import pytest @@ -80,3 +81,12 @@ def test_split_name_ext(): # test unsupported extensions for ext in ("this.is.not.supported", "a.b.c.d.e", "fif.gz.xyz"): assert split_name_ext(f"test{ext}")[1] is None + + +def test_read_raw_multiple_dots(tmp_path): + """Test if file names with multiple dots work correctly.""" + src = base / 'edf/tests/data/test.edf' + dst = tmp_path / "test.this.file.edf" + copyfile(src, dst) + read_raw(dst) + \ No newline at end of file From fabf86f5e3d18c94699ca8fe3a75cdfbebe685af Mon Sep 17 00:00:00 2001 From: Clemens Brunner Date: Tue, 7 Mar 2023 14:46:35 +0100 Subject: [PATCH 10/13] Fix PEP8 --- mne/io/tests/test_read_raw.py | 1 - 1 file changed, 1 deletion(-) diff --git a/mne/io/tests/test_read_raw.py b/mne/io/tests/test_read_raw.py index b0251a6e6cd..faedbdd7c41 100644 --- a/mne/io/tests/test_read_raw.py +++ b/mne/io/tests/test_read_raw.py @@ -89,4 +89,3 @@ def test_read_raw_multiple_dots(tmp_path): dst = tmp_path / "test.this.file.edf" copyfile(src, dst) read_raw(dst) - \ No newline at end of file From 87dafebbdb6b87b2591206e541be31fadc999b0a Mon Sep 17 00:00:00 2001 From: Clemens Brunner Date: Tue, 7 Mar 2023 18:44:02 +0100 Subject: [PATCH 11/13] Generator instead of list comprehension Co-authored-by: Eric Larson --- mne/io/_read_raw.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mne/io/_read_raw.py b/mne/io/_read_raw.py index 1dc5ba4af58..d21cb741c16 100644 --- a/mne/io/_read_raw.py +++ b/mne/io/_read_raw.py @@ -67,7 +67,7 @@ def _read_unsupported(fname, **kwargs): def split_name_ext(fname): """Return name and supported file extension.""" - maxsuffixes = max([ext.count(".") for ext in supported]) + maxsuffixes = max(ext.count(".") for ext in supported) suffixes = Path(fname).suffixes for si in range(-maxsuffixes, 0): ext = "".join(suffixes[si:]).lower() From 2ed53f6aca21be1c7eb558daf8c7f5c1ac6d0297 Mon Sep 17 00:00:00 2001 From: Clemens Brunner Date: Tue, 7 Mar 2023 18:44:16 +0100 Subject: [PATCH 12/13] Update mne/io/_read_raw.py Co-authored-by: Eric Larson --- mne/io/_read_raw.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mne/io/_read_raw.py b/mne/io/_read_raw.py index d21cb741c16..5227b33ae86 100644 --- a/mne/io/_read_raw.py +++ b/mne/io/_read_raw.py @@ -71,7 +71,7 @@ def split_name_ext(fname): suffixes = Path(fname).suffixes for si in range(-maxsuffixes, 0): ext = "".join(suffixes[si:]).lower() - if ext in readers.keys(): + if ext in readers: return Path(fname).name[:-len(ext)], ext return fname, None # unknown file extension From f15fecb2c0d43952e3efd59030190b598d740900 Mon Sep 17 00:00:00 2001 From: Clemens Brunner Date: Wed, 8 Mar 2023 08:28:02 +0100 Subject: [PATCH 13/13] Test all readers --- mne/io/tests/test_read_raw.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mne/io/tests/test_read_raw.py b/mne/io/tests/test_read_raw.py index faedbdd7c41..13c696f0f17 100644 --- a/mne/io/tests/test_read_raw.py +++ b/mne/io/tests/test_read_raw.py @@ -11,7 +11,7 @@ from mne.datasets import testing from mne.io import read_raw -from mne.io._read_raw import split_name_ext, supported +from mne.io._read_raw import split_name_ext, readers base = Path(__file__).parent.parent @@ -74,8 +74,8 @@ def test_read_raw_supported(fname): def test_split_name_ext(): """Test file name extension splitting.""" - # test supported extensions - for ext in supported.keys(): + # test known extensions + for ext in readers: assert split_name_ext(f"test{ext}")[1] == ext # test unsupported extensions