From f667079f1d322ff2efedab53915493dee95ea291 Mon Sep 17 00:00:00 2001 From: Ishaan Maheshwari Date: Tue, 13 Oct 2026 22:05:36 +0530 Subject: [PATCH 1/4] FIX: Convert array-like inputs in eyetracking Calibration --- mne/preprocessing/eyetracking/calibration.py | 15 ++++++++++-- .../eyetracking/tests/test_calibration.py | 23 +++++++++++++++++++ 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/mne/preprocessing/eyetracking/calibration.py b/mne/preprocessing/eyetracking/calibration.py index 390176816d4..4485e1908c9 100644 --- a/mne/preprocessing/eyetracking/calibration.py +++ b/mne/preprocessing/eyetracking/calibration.py @@ -75,6 +75,12 @@ def __init__( screen_distance=None, screen_resolution=None, ): + if positions is not None: + positions = np.asarray(positions, dtype=float) + if offsets is not None: + offsets = np.asarray(offsets, dtype=float) + if gaze is not None: + gaze = np.asarray(gaze, dtype=float) super().__init__( onset=onset, model=model, @@ -137,8 +143,13 @@ def plot(self, show_offsets=True, axes=None, show=True): from ...viz.utils import plt_show msg = "positions and gaze keys must both be 2D numpy arrays." - assert isinstance(self["positions"], np.ndarray), msg - assert isinstance(self["gaze"], np.ndarray), msg + if ( + not isinstance(self["positions"], np.ndarray) + or not isinstance(self["gaze"], np.ndarray) + or self["positions"].ndim != 2 + or self["gaze"].ndim != 2 + ): + raise ValueError(msg) if axes is not None: from matplotlib.axes import Axes diff --git a/mne/preprocessing/eyetracking/tests/test_calibration.py b/mne/preprocessing/eyetracking/tests/test_calibration.py index 0e40fedf28b..496df4d94cf 100644 --- a/mne/preprocessing/eyetracking/tests/test_calibration.py +++ b/mne/preprocessing/eyetracking/tests/test_calibration.py @@ -52,6 +52,19 @@ 0.065, (1920, 1080), ), + ( + 0, + "H3", + "right", + 0.5, + 1.0, + POSITIONS.tolist(), + OFFSETS.tolist(), + GAZES.tolist(), + (0.531, 0.298), + 0.065, + (1920, 1080), + ), (None, None, None, None, None, None, None, None, None, None, None), ], ) @@ -115,6 +128,16 @@ def test_create_calibration( if cal["onset"] is not None: assert repr(cal) == EXPECTED_REPR # test __repr__ + # test plot + if cal["positions"] is not None: + import matplotlib.pyplot as plt + + fig = cal.plot(show=False) + plt.close(fig) + else: + with pytest.raises(ValueError, match="positions and gaze keys"): + cal.plot(show=False) + @requires_testing_data @pytest.mark.parametrize("fname", [(fname)]) From c292c0624175ae820549b405339d7715e1f3461f Mon Sep 17 00:00:00 2001 From: Ishaan Maheshwari Date: Tue, 13 Oct 2026 22:19:07 +0530 Subject: [PATCH 2/4] DOC: Add changelog entry for #14413 --- doc/changes/dev/14413.bugfix.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 doc/changes/dev/14413.bugfix.rst diff --git a/doc/changes/dev/14413.bugfix.rst b/doc/changes/dev/14413.bugfix.rst new file mode 100644 index 00000000000..1f026e297c8 --- /dev/null +++ b/doc/changes/dev/14413.bugfix.rst @@ -0,0 +1 @@ +Fix :class:`~mne.preprocessing.eyetracking.Calibration` to accept array-like inputs, by `Ishaan Maheshwari`_. From 182931e468c9f28c1c9b2b043fbcf39f16e04bf2 Mon Sep 17 00:00:00 2001 From: Scott Huberty Date: Thu, 15 Oct 2026 08:15:14 -0700 Subject: [PATCH 3/4] TST: dryer --- .../eyetracking/tests/test_calibration.py | 104 ++++++------------ 1 file changed, 31 insertions(+), 73 deletions(-) diff --git a/mne/preprocessing/eyetracking/tests/test_calibration.py b/mne/preprocessing/eyetracking/tests/test_calibration.py index 496df4d94cf..67cfa106c6e 100644 --- a/mne/preprocessing/eyetracking/tests/test_calibration.py +++ b/mne/preprocessing/eyetracking/tests/test_calibration.py @@ -33,88 +33,46 @@ ) -@pytest.mark.parametrize( - ( - "onset, model, eye, avg_error, max_error, positions, offsets, gaze," - " screen_size, screen_distance, screen_resolution" - ), - [ - ( - 0, - "H3", - "right", - 0.5, - 1.0, - POSITIONS, - OFFSETS, - GAZES, - (0.531, 0.298), - 0.065, - (1920, 1080), - ), - ( - 0, - "H3", - "right", - 0.5, - 1.0, - POSITIONS.tolist(), - OFFSETS.tolist(), - GAZES.tolist(), - (0.531, 0.298), - 0.065, - (1920, 1080), - ), - (None, None, None, None, None, None, None, None, None, None, None), - ], -) -def test_create_calibration( - onset, - model, - eye, - avg_error, - max_error, - positions, - offsets, - gaze, - screen_size, - screen_distance, - screen_resolution, -): +@pytest.mark.parametrize("input_type", ["array", "list", None]) +def test_create_calibration(input_type): """Test creating a Calibration object.""" - kwargs = dict( - onset=onset, - model=model, - eye=eye, - avg_error=avg_error, - max_error=max_error, - positions=positions, - offsets=offsets, - gaze=gaze, - screen_size=screen_size, - screen_distance=screen_distance, - screen_resolution=screen_resolution, + kwargs_template = dict( + onset=0, + model="H3", + eye="right", + avg_error=0.5, + max_error=1.0, + positions=POSITIONS, + offsets=OFFSETS, + gaze=GAZES, + screen_size=(0.531, 0.298), + screen_distance=0.065, + screen_resolution=(1920, 1080), ) + kwargs = kwargs_template.copy() + if input_type is None: + kwargs = {k: None for k in kwargs} + elif input_type == "list": + for key in ("positions", "offsets", "gaze"): + kwargs[key] = kwargs[key].tolist() + cal = Calibration(**kwargs) - assert cal["onset"] == onset - assert cal["model"] == model - assert cal["eye"] == eye - assert cal["avg_error"] == avg_error - assert cal["max_error"] == max_error - if positions is not None: + + for key in ("onset", "model", "eye", "avg_error", "max_error"): + assert cal[key] == kwargs[key] + if input_type is not None: assert isinstance(cal["positions"], np.ndarray) assert np.array_equal(cal["positions"], np.array(POSITIONS)) - else: - assert cal["positions"] is None - if offsets is not None: assert isinstance(cal["offsets"], np.ndarray) assert np.array_equal(cal["offsets"], np.array(OFFSETS)) - if gaze is not None: assert isinstance(cal["gaze"], np.ndarray) assert np.array_equal(cal["gaze"], np.array(GAZES)) - assert cal["screen_size"] == screen_size - assert cal["screen_distance"] == screen_distance - assert cal["screen_resolution"] == screen_resolution + else: + assert cal["positions"] is None + + assert cal["screen_size"] == kwargs["screen_size"] + assert cal["screen_distance"] == kwargs["screen_distance"] + assert cal["screen_resolution"] == kwargs["screen_resolution"] # test copy method copied_obj = cal.copy() # Check if the copied object is an instance of Calibration From 0ca09ee7844bb36f7ce412da05859621055998eb Mon Sep 17 00:00:00 2001 From: Scott Huberty Date: Thu, 15 Oct 2026 08:46:15 -0700 Subject: [PATCH 4/4] DOC, FIX: name typo caught by CI in .mailmap, but names.inc needed to be updated https://github.com/mne-tools/mne-python/pull/14413#issuecomment-6195765351 --- doc/changes/names.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/changes/names.inc b/doc/changes/names.inc index 4be0327e6f8..22e77da39d0 100644 --- a/doc/changes/names.inc +++ b/doc/changes/names.inc @@ -240,7 +240,7 @@ .. _Kaisu Lankinen: http://bishoplab.berkeley.edu/Kaisu.html .. _Kalle Makela: https://github.com/Kallemakela .. _Kalle Mäkelä: https://github.com/kalomak -.. _Kambiz Tabavi: https://github.com/ktavabi +.. _Kambiz Tavabi: https://github.com/ktavabi .. _Katarina Slama: https://github.com/katarinaslama .. _Katia Al-Amir: https://github.com/katia-sentry .. _Katrin Leinweber: https://github.com/katrinleinweber