From 6667a164011cd64f04df05cf7c661588381e2ca2 Mon Sep 17 00:00:00 2001 From: Cedric Conday Date: Tue, 30 Jun 2026 03:48:59 +0000 Subject: [PATCH 1/4] Fix pick channels crash when _orig_units is None (#11314) Picking/reordering channels built {k: v for ... in self._orig_units.items()} unconditionally, raising AttributeError when _orig_units is None (as produced by some readers / RawArray flows). Guard for a falsy _orig_units. Adds a test. --- doc/changes/dev/11314.bugfix.rst | 1 + mne/channels/channels.py | 7 ++++--- mne/channels/tests/test_channels.py | 14 ++++++++++++++ 3 files changed, 19 insertions(+), 3 deletions(-) create mode 100644 doc/changes/dev/11314.bugfix.rst diff --git a/doc/changes/dev/11314.bugfix.rst b/doc/changes/dev/11314.bugfix.rst new file mode 100644 index 00000000000..8a30fec7520 --- /dev/null +++ b/doc/changes/dev/11314.bugfix.rst @@ -0,0 +1 @@ +Fix a crash (``AttributeError`` on ``NoneType``) when picking channels on a :class:`~mne.io.Raw` whose ``_orig_units`` is ``None``, by `Cedric Conday`_. diff --git a/mne/channels/channels.py b/mne/channels/channels.py index c1a3d462525..c18ac8d253c 100644 --- a/mne/channels/channels.py +++ b/mne/channels/channels.py @@ -646,9 +646,10 @@ def _pick_drop_channels(self, idx, *, verbose=None): if isinstance(self, BaseRaw): self.annotations._prune_ch_names(self.info, on_missing="ignore") - self._orig_units = { - k: v for k, v in self._orig_units.items() if k in self.ch_names - } + if self._orig_units: + self._orig_units = { + k: v for k, v in self._orig_units.items() if k in self.ch_names + } self._pick_projs() return self diff --git a/mne/channels/tests/test_channels.py b/mne/channels/tests/test_channels.py index 138d2b325c7..b35aa68495c 100644 --- a/mne/channels/tests/test_channels.py +++ b/mne/channels/tests/test_channels.py @@ -696,3 +696,17 @@ def test_combine_channels_metadata(): good = dict(foo=[0, 1, 3, 4], bar=[5, 2]) # good grad and mag combined_epochs = combine_channels(epochs, good) pd.testing.assert_frame_equal(epochs.metadata, combined_epochs.metadata) + + +def test_pick_channels_orig_units_none(): + """Picking channels must not crash when _orig_units is None (gh-11314).""" + import numpy as np + + import mne + + info = mne.create_info(["Fp1", "Fp2", "F3", "F4"], 100.0, "eeg") + raw = mne.io.RawArray(np.zeros((4, 100)), info) + raw._orig_units = None # the state reported by some readers / RawArray flows + raw.pick(["Fp1", "Fp2"]) # previously raised AttributeError on None.items() + assert raw.ch_names == ["Fp1", "Fp2"] + assert raw._orig_units is None From 98b78c2660a158cbf19f8ae53f4ecc369e138093 Mon Sep 17 00:00:00 2001 From: Cedric Conday Date: Tue, 30 Jun 2026 03:49:52 +0000 Subject: [PATCH 2/4] rename changelog fragment to PR number --- doc/changes/dev/{11314.bugfix.rst => 14006.bugfix.rst} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename doc/changes/dev/{11314.bugfix.rst => 14006.bugfix.rst} (100%) diff --git a/doc/changes/dev/11314.bugfix.rst b/doc/changes/dev/14006.bugfix.rst similarity index 100% rename from doc/changes/dev/11314.bugfix.rst rename to doc/changes/dev/14006.bugfix.rst From 68f6d50c57da2db2a321d3d0d5085cd4c09e3b77 Mon Sep 17 00:00:00 2001 From: CedricConday Date: Wed, 1 Jul 2026 05:31:24 +0000 Subject: [PATCH 3/4] test: hoist numpy/mne imports to module level per review --- mne/channels/tests/test_channels.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/mne/channels/tests/test_channels.py b/mne/channels/tests/test_channels.py index b35aa68495c..2c2bfc6db6f 100644 --- a/mne/channels/tests/test_channels.py +++ b/mne/channels/tests/test_channels.py @@ -14,6 +14,7 @@ from numpy.testing import assert_allclose, assert_array_equal, assert_equal from scipy.io import savemat +import mne from mne import ( Epochs, EpochsArray, @@ -700,10 +701,6 @@ def test_combine_channels_metadata(): def test_pick_channels_orig_units_none(): """Picking channels must not crash when _orig_units is None (gh-11314).""" - import numpy as np - - import mne - info = mne.create_info(["Fp1", "Fp2", "F3", "F4"], 100.0, "eeg") raw = mne.io.RawArray(np.zeros((4, 100)), info) raw._orig_units = None # the state reported by some readers / RawArray flows From 27644192d6f93862dbd9217f1d4d508e85dfbe3a Mon Sep 17 00:00:00 2001 From: Cedric Conday <277679649+CedricConday@users.noreply.github.com> Date: Fri, 10 Jul 2026 22:01:29 +0000 Subject: [PATCH 4/4] test: use direct create_info/RawArray imports, drop unused import mne Address @drammock review on #14006: create_info and RawArray are already imported directly at the top of the test module, so call them directly and remove the now-unused `import mne`. --- mne/channels/tests/test_channels.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/mne/channels/tests/test_channels.py b/mne/channels/tests/test_channels.py index 2c2bfc6db6f..20f8e536ff9 100644 --- a/mne/channels/tests/test_channels.py +++ b/mne/channels/tests/test_channels.py @@ -14,7 +14,6 @@ from numpy.testing import assert_allclose, assert_array_equal, assert_equal from scipy.io import savemat -import mne from mne import ( Epochs, EpochsArray, @@ -701,8 +700,8 @@ def test_combine_channels_metadata(): def test_pick_channels_orig_units_none(): """Picking channels must not crash when _orig_units is None (gh-11314).""" - info = mne.create_info(["Fp1", "Fp2", "F3", "F4"], 100.0, "eeg") - raw = mne.io.RawArray(np.zeros((4, 100)), info) + info = create_info(["Fp1", "Fp2", "F3", "F4"], 100.0, "eeg") + raw = RawArray(np.zeros((4, 100)), info) raw._orig_units = None # the state reported by some readers / RawArray flows raw.pick(["Fp1", "Fp2"]) # previously raised AttributeError on None.items() assert raw.ch_names == ["Fp1", "Fp2"]