From e17079e470265dbe505ca8dcfba938ea979750d1 Mon Sep 17 00:00:00 2001 From: Piotr Durka Date: Sun, 14 Jun 2026 19:23:15 +0200 Subject: [PATCH 1/2] FIX: read_annotations(.txt) on a single channel-specific annotation --- mne/annotations.py | 2 +- mne/tests/test_annotations.py | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/mne/annotations.py b/mne/annotations.py index c03e0610f28..b6d527ca9dc 100644 --- a/mne/annotations.py +++ b/mne/annotations.py @@ -2105,7 +2105,7 @@ def _read_annotations_txt(fname): if ch_names is not None: ch_names = [ _safe_name_list(ch.decode().strip(), "read", f"ch_names[{ci}]") - for ci, ch in enumerate(ch_names) + for ci, ch in enumerate(np.atleast_1d(ch_names)) ] annotations = Annotations( diff --git a/mne/tests/test_annotations.py b/mne/tests/test_annotations.py index 1c9a1416a29..a3cbd58c3d9 100644 --- a/mne/tests/test_annotations.py +++ b/mne/tests/test_annotations.py @@ -1084,6 +1084,18 @@ def test_io_annotation(dummy_annotation_file, tmp_path, fmt, ch_names, with_extr _assert_annotations_equal(annot, annot2) +def test_read_annotations_txt_single_channel_specific(tmp_path): + """Read a .txt with a SINGLE channel-specific annotation (gh-13961).""" + # np.loadtxt(..., unpack=True) squeezes a 1-row file to 0-D scalars; + # ch_names (unlike onset/duration/description) was not wrapped in + # np.atleast_1d, so it was iterated as bytes-ints -> AttributeError. + annot = Annotations([1.0], [0.5], ["BAD_x"], ch_names=[["EEG001"]]) + fname = tmp_path / "annotations.txt" + annot.save(fname) + annot_read = read_annotations(fname) + assert list(annot_read.ch_names) == [("EEG001",)] + + @pytest.mark.parametrize("fmt", [pytest.param("csv", marks=needs_pandas), "txt"]) def test_write_annotation_warn_heterogeneous(tmp_path, fmt): """Test that CSV, and TXT annotation writers warn on heterogeneous dtypes.""" From 0ac973f1065d9db5611e80b8c36405edebcdd117 Mon Sep 17 00:00:00 2001 From: Piotr Durka Date: Sun, 14 Jun 2026 19:53:01 +0200 Subject: [PATCH 2/2] DOC: changelog entry for #13962 --- doc/changes/dev/13962.bugfix.rst | 1 + doc/changes/names.inc | 1 + 2 files changed, 2 insertions(+) create mode 100644 doc/changes/dev/13962.bugfix.rst diff --git a/doc/changes/dev/13962.bugfix.rst b/doc/changes/dev/13962.bugfix.rst new file mode 100644 index 00000000000..76b546fff0c --- /dev/null +++ b/doc/changes/dev/13962.bugfix.rst @@ -0,0 +1 @@ +Fix bug in :func:`mne.read_annotations` where reading a ``.txt`` file containing a single channel-specific annotation raised an ``AttributeError``, by :newcontrib:`Piotr Durka`. diff --git a/doc/changes/names.inc b/doc/changes/names.inc index d4c8b04b10b..b4127538551 100644 --- a/doc/changes/names.inc +++ b/doc/changes/names.inc @@ -272,6 +272,7 @@ .. _Pierre Guetschel: https://github.com/PierreGtch .. _Pierre-Antoine Bannier: https://github.com/PABannier .. _Ping-Keng Jao: https://github.com/nafraw +.. _Piotr Durka: https://github.com/pjdurka .. _Pragnya Khandelwal: https://github.com/PragnyaKhandelwal .. _Proloy Das: https://github.com/proloyd .. _Qian Chu: https://github.com/qian-chu