From 81d4b379fb5e0fe0911f6d5a3f6b162168376700 Mon Sep 17 00:00:00 2001 From: Deepesh Sonar <18deepnar@gmail.com> Date: Mon, 7 Sep 2026 19:17:56 +0530 Subject: [PATCH 1/2] FIX: Avoid pytest 10 warnings in sklearn estimator checks --- doc/changes/dev/14283.other.rst | 1 + doc/changes/names.inc | 1 + mne/conftest.py | 1 - mne/decoding/tests/__init__.py | 35 ++++++++++++++++++++++ mne/decoding/tests/test_base.py | 2 +- mne/decoding/tests/test_csp.py | 2 +- mne/decoding/tests/test_ems.py | 2 +- mne/decoding/tests/test_ged.py | 2 +- mne/decoding/tests/test_receptive_field.py | 2 +- mne/decoding/tests/test_search_light.py | 2 +- mne/decoding/tests/test_ssd.py | 2 +- mne/decoding/tests/test_time_frequency.py | 2 +- mne/decoding/tests/test_transformer.py | 2 +- mne/decoding/tests/test_xdawn.py | 2 +- 14 files changed, 47 insertions(+), 11 deletions(-) create mode 100644 doc/changes/dev/14283.other.rst diff --git a/doc/changes/dev/14283.other.rst b/doc/changes/dev/14283.other.rst new file mode 100644 index 00000000000..d4f9283dd8f --- /dev/null +++ b/doc/changes/dev/14283.other.rst @@ -0,0 +1 @@ +Avoid pytest 10 deprecation warnings when parametrizing scikit-learn estimator checks, by :newcontrib:`Deepesh Sonar`. diff --git a/doc/changes/names.inc b/doc/changes/names.inc index d28e1b19c9f..b203ade9ac1 100644 --- a/doc/changes/names.inc +++ b/doc/changes/names.inc @@ -101,6 +101,7 @@ .. _David Julien: https://github.com/Swy7ch .. _David Sabbagh: https://github.com/DavidSabbagh .. _Deep Kaur: https://github.com/Dpereaptkhamur-13 +.. _Deepesh Sonar: https://github.com/Deepnar .. _Denis Engemann: https://denis-engemann.de .. _Desislava Petkova: https://github.com/dipetkov .. _Dev Parikh: https://github.com/devparikh0506 diff --git a/mne/conftest.py b/mne/conftest.py index 3d4df0c491d..04ce75ad2d8 100644 --- a/mne/conftest.py +++ b/mne/conftest.py @@ -219,7 +219,6 @@ def pytest_configure(config: pytest.Config): # sklearn ignore:Python binding for RankQuantileOptions.*:RuntimeWarning ignore:.*The `disp` and `iprint` options of the L-BFGS-B solver.*:DeprecationWarning - ignore:Passing a non-Collection iterable to parametrize[.\n]*: # matplotlib<->nilearn ignore:[\S\s]*You are using the 'agg' matplotlib backend[\S\s]*:UserWarning # matplotlib<->pyparsing diff --git a/mne/decoding/tests/__init__.py b/mne/decoding/tests/__init__.py index 04c673b4e4d..827733b73b7 100644 --- a/mne/decoding/tests/__init__.py +++ b/mne/decoding/tests/__init__.py @@ -1,3 +1,38 @@ # Authors: The MNE-Python contributors. # License: BSD-3-Clause # Copyright the MNE-Python contributors. + +from functools import partial + +import pytest +from sklearn.utils.estimator_checks import ( + parametrize_with_checks as _sklearn_parametrize_with_checks, +) + + +# TODO VERSION: Remove once scikit-learn 1.10.0 is the minimum supported version +# scikit-learn/scikit-learn#34448 +def _materialize_parametrize_mark(sklearn_decorator, func): + """Materialize generated argvalues in sklearn's pytest mark.""" + marked_func = sklearn_decorator(func) + marks = marked_func.pytestmark + if not isinstance(marks, list): + marks = [marks] + else: + marks = list(marks) + for index, mark in enumerate(marks): + if mark.name == "parametrize": + args = (*mark.args[:1], list(mark.args[1]), *mark.args[2:]) + marks[index] = pytest.mark.parametrize(*args, **mark.kwargs).mark + break + marked_func.pytestmark = marks + return marked_func + + +def _parametrize_with_checks(estimators): + """Parametrize estimator checks with pytest-compatible argvalues.""" + sklearn_decorator = _sklearn_parametrize_with_checks(estimators) + return partial(_materialize_parametrize_mark, sklearn_decorator) + + +parametrize_with_checks = _parametrize_with_checks diff --git a/mne/decoding/tests/test_base.py b/mne/decoding/tests/test_base.py index adc439bd026..bf6cbe9b116 100644 --- a/mne/decoding/tests/test_base.py +++ b/mne/decoding/tests/test_base.py @@ -40,7 +40,6 @@ from sklearn.multiclass import OneVsRestClassifier from sklearn.pipeline import make_pipeline from sklearn.preprocessing import StandardScaler -from sklearn.utils.estimator_checks import parametrize_with_checks from mne import EpochsArray, create_info from mne.decoding import GeneralizingEstimator, Scaler, TransformerMixin, Vectorizer @@ -52,6 +51,7 @@ get_coef, ) from mne.decoding.search_light import SlidingEstimator +from mne.decoding.tests import parametrize_with_checks from mne.utils import check_version diff --git a/mne/decoding/tests/test_csp.py b/mne/decoding/tests/test_csp.py index 04fdd6956fc..479ac38b277 100644 --- a/mne/decoding/tests/test_csp.py +++ b/mne/decoding/tests/test_csp.py @@ -19,11 +19,11 @@ from sklearn.model_selection import StratifiedKFold, cross_val_score from sklearn.pipeline import Pipeline, make_pipeline from sklearn.svm import SVC -from sklearn.utils.estimator_checks import parametrize_with_checks from mne import Epochs, compute_proj_raw, io, pick_types, read_events from mne.decoding import CSP, LinearModel, Scaler, SPoC, get_coef, read_csp, read_spoc from mne.decoding.csp import _ajd_pham +from mne.decoding.tests import parametrize_with_checks from mne.utils import catch_logging, check_version data_dir = Path(__file__).parents[2] / "io" / "tests" / "data" diff --git a/mne/decoding/tests/test_ems.py b/mne/decoding/tests/test_ems.py index 6dadf5094c3..1a82594890f 100644 --- a/mne/decoding/tests/test_ems.py +++ b/mne/decoding/tests/test_ems.py @@ -11,10 +11,10 @@ pytest.importorskip("sklearn") from sklearn.model_selection import StratifiedKFold -from sklearn.utils.estimator_checks import parametrize_with_checks from mne import Epochs, io, pick_types, read_events from mne.decoding import EMS, compute_ems +from mne.decoding.tests import parametrize_with_checks data_dir = Path(__file__).parents[2] / "io" / "tests" / "data" raw_fname = data_dir / "test_raw.fif" diff --git a/mne/decoding/tests/test_ged.py b/mne/decoding/tests/test_ged.py index fdec15d775a..8d8f795e6e2 100644 --- a/mne/decoding/tests/test_ged.py +++ b/mne/decoding/tests/test_ged.py @@ -14,7 +14,6 @@ from sklearn.model_selection import ParameterGrid from sklearn.utils._testing import assert_allclose -from sklearn.utils.estimator_checks import parametrize_with_checks from mne import Epochs, compute_rank, create_info, pick_types, read_events from mne._fiff.proj import make_eeg_average_ref_proj @@ -29,6 +28,7 @@ ) from mne.decoding._mod_ged import _no_op_mod from mne.decoding.base import _GEDTransformer +from mne.decoding.tests import parametrize_with_checks from mne.io import read_raw data_dir = Path(__file__).parents[2] / "io" / "tests" / "data" diff --git a/mne/decoding/tests/test_receptive_field.py b/mne/decoding/tests/test_receptive_field.py index bef99f12887..84e9d4d5b1e 100644 --- a/mne/decoding/tests/test_receptive_field.py +++ b/mne/decoding/tests/test_receptive_field.py @@ -13,7 +13,6 @@ pytest.importorskip("sklearn") from sklearn.linear_model import Ridge -from sklearn.utils.estimator_checks import parametrize_with_checks from mne.decoding import ReceptiveField, TimeDelayingRidge from mne.decoding.receptive_field import ( @@ -22,6 +21,7 @@ _delays_to_slice, _times_to_delays, ) +from mne.decoding.tests import parametrize_with_checks from mne.decoding.time_delaying_ridge import _compute_corrs, _compute_reg_neighbors data_dir = Path(__file__).parents[2] / "io" / "tests" / "data" diff --git a/mne/decoding/tests/test_search_light.py b/mne/decoding/tests/test_search_light.py index 429f96aee97..e48ceae75d6 100644 --- a/mne/decoding/tests/test_search_light.py +++ b/mne/decoding/tests/test_search_light.py @@ -20,9 +20,9 @@ from sklearn.multiclass import OneVsRestClassifier from sklearn.pipeline import make_pipeline from sklearn.svm import SVC -from sklearn.utils.estimator_checks import parametrize_with_checks from mne.decoding.search_light import GeneralizingEstimator, SlidingEstimator +from mne.decoding.tests import parametrize_with_checks from mne.decoding.transformer import Vectorizer from mne.utils import check_version, use_log_level diff --git a/mne/decoding/tests/test_ssd.py b/mne/decoding/tests/test_ssd.py index 55a3dbb2b8e..fe9dcd3bfcc 100644 --- a/mne/decoding/tests/test_ssd.py +++ b/mne/decoding/tests/test_ssd.py @@ -12,13 +12,13 @@ pytest.importorskip("sklearn") from sklearn.pipeline import Pipeline -from sklearn.utils.estimator_checks import parametrize_with_checks from mne import Epochs, create_info, io, pick_types, read_events from mne._fiff.pick import _picks_to_idx from mne.decoding import CSP from mne.decoding._mod_ged import _get_spectral_ratio from mne.decoding.ssd import SSD, read_ssd +from mne.decoding.tests import parametrize_with_checks from mne.filter import filter_data from mne.time_frequency import psd_array_welch diff --git a/mne/decoding/tests/test_time_frequency.py b/mne/decoding/tests/test_time_frequency.py index 6187b4b0b32..8fbf7e48105 100644 --- a/mne/decoding/tests/test_time_frequency.py +++ b/mne/decoding/tests/test_time_frequency.py @@ -10,8 +10,8 @@ pytest.importorskip("sklearn") from sklearn.base import clone -from sklearn.utils.estimator_checks import parametrize_with_checks +from mne.decoding.tests import parametrize_with_checks from mne.decoding.time_frequency import TimeFrequency diff --git a/mne/decoding/tests/test_transformer.py b/mne/decoding/tests/test_transformer.py index f6d4bcc6031..d29fc62bc0f 100644 --- a/mne/decoding/tests/test_transformer.py +++ b/mne/decoding/tests/test_transformer.py @@ -19,7 +19,6 @@ from sklearn.kernel_ridge import KernelRidge from sklearn.pipeline import make_pipeline from sklearn.preprocessing import StandardScaler -from sklearn.utils.estimator_checks import parametrize_with_checks from mne import Epochs, EpochsArray, create_info, io, pick_types, read_events from mne.decoding import ( @@ -31,6 +30,7 @@ UnsupervisedSpatialFilter, Vectorizer, ) +from mne.decoding.tests import parametrize_with_checks from mne.defaults import DEFAULTS from mne.utils import use_log_level diff --git a/mne/decoding/tests/test_xdawn.py b/mne/decoding/tests/test_xdawn.py index baae48ed111..7295cb3ee2c 100644 --- a/mne/decoding/tests/test_xdawn.py +++ b/mne/decoding/tests/test_xdawn.py @@ -7,9 +7,9 @@ from numpy.testing import assert_array_almost_equal pytest.importorskip("sklearn") -from sklearn.utils.estimator_checks import parametrize_with_checks from mne.decoding import XdawnTransformer, read_xdawn_transformer +from mne.decoding.tests import parametrize_with_checks @pytest.mark.filterwarnings("ignore:.*Only one sample available.*") From 28fed81a00bc5c15b184a47ff0fad18e610b875a Mon Sep 17 00:00:00 2001 From: Deepesh Sonar <18deepnar@gmail.com> Date: Tue, 8 Sep 2026 22:46:28 +0530 Subject: [PATCH 2/2] FIX: Avoid optional sklearn import during test collection --- mne/decoding/tests/__init__.py | 35 -------------------- mne/decoding/tests/_sklearn.py | 38 ++++++++++++++++++++++ mne/decoding/tests/test_base.py | 2 +- mne/decoding/tests/test_csp.py | 2 +- mne/decoding/tests/test_ems.py | 2 +- mne/decoding/tests/test_ged.py | 2 +- mne/decoding/tests/test_receptive_field.py | 2 +- mne/decoding/tests/test_search_light.py | 2 +- mne/decoding/tests/test_ssd.py | 2 +- mne/decoding/tests/test_time_frequency.py | 2 +- mne/decoding/tests/test_transformer.py | 2 +- mne/decoding/tests/test_xdawn.py | 2 +- 12 files changed, 48 insertions(+), 45 deletions(-) create mode 100644 mne/decoding/tests/_sklearn.py diff --git a/mne/decoding/tests/__init__.py b/mne/decoding/tests/__init__.py index 827733b73b7..04c673b4e4d 100644 --- a/mne/decoding/tests/__init__.py +++ b/mne/decoding/tests/__init__.py @@ -1,38 +1,3 @@ # Authors: The MNE-Python contributors. # License: BSD-3-Clause # Copyright the MNE-Python contributors. - -from functools import partial - -import pytest -from sklearn.utils.estimator_checks import ( - parametrize_with_checks as _sklearn_parametrize_with_checks, -) - - -# TODO VERSION: Remove once scikit-learn 1.10.0 is the minimum supported version -# scikit-learn/scikit-learn#34448 -def _materialize_parametrize_mark(sklearn_decorator, func): - """Materialize generated argvalues in sklearn's pytest mark.""" - marked_func = sklearn_decorator(func) - marks = marked_func.pytestmark - if not isinstance(marks, list): - marks = [marks] - else: - marks = list(marks) - for index, mark in enumerate(marks): - if mark.name == "parametrize": - args = (*mark.args[:1], list(mark.args[1]), *mark.args[2:]) - marks[index] = pytest.mark.parametrize(*args, **mark.kwargs).mark - break - marked_func.pytestmark = marks - return marked_func - - -def _parametrize_with_checks(estimators): - """Parametrize estimator checks with pytest-compatible argvalues.""" - sklearn_decorator = _sklearn_parametrize_with_checks(estimators) - return partial(_materialize_parametrize_mark, sklearn_decorator) - - -parametrize_with_checks = _parametrize_with_checks diff --git a/mne/decoding/tests/_sklearn.py b/mne/decoding/tests/_sklearn.py new file mode 100644 index 00000000000..827733b73b7 --- /dev/null +++ b/mne/decoding/tests/_sklearn.py @@ -0,0 +1,38 @@ +# Authors: The MNE-Python contributors. +# License: BSD-3-Clause +# Copyright the MNE-Python contributors. + +from functools import partial + +import pytest +from sklearn.utils.estimator_checks import ( + parametrize_with_checks as _sklearn_parametrize_with_checks, +) + + +# TODO VERSION: Remove once scikit-learn 1.10.0 is the minimum supported version +# scikit-learn/scikit-learn#34448 +def _materialize_parametrize_mark(sklearn_decorator, func): + """Materialize generated argvalues in sklearn's pytest mark.""" + marked_func = sklearn_decorator(func) + marks = marked_func.pytestmark + if not isinstance(marks, list): + marks = [marks] + else: + marks = list(marks) + for index, mark in enumerate(marks): + if mark.name == "parametrize": + args = (*mark.args[:1], list(mark.args[1]), *mark.args[2:]) + marks[index] = pytest.mark.parametrize(*args, **mark.kwargs).mark + break + marked_func.pytestmark = marks + return marked_func + + +def _parametrize_with_checks(estimators): + """Parametrize estimator checks with pytest-compatible argvalues.""" + sklearn_decorator = _sklearn_parametrize_with_checks(estimators) + return partial(_materialize_parametrize_mark, sklearn_decorator) + + +parametrize_with_checks = _parametrize_with_checks diff --git a/mne/decoding/tests/test_base.py b/mne/decoding/tests/test_base.py index bf6cbe9b116..6f1f38bb35c 100644 --- a/mne/decoding/tests/test_base.py +++ b/mne/decoding/tests/test_base.py @@ -51,7 +51,7 @@ get_coef, ) from mne.decoding.search_light import SlidingEstimator -from mne.decoding.tests import parametrize_with_checks +from mne.decoding.tests._sklearn import parametrize_with_checks from mne.utils import check_version diff --git a/mne/decoding/tests/test_csp.py b/mne/decoding/tests/test_csp.py index 479ac38b277..6c7bf22fb77 100644 --- a/mne/decoding/tests/test_csp.py +++ b/mne/decoding/tests/test_csp.py @@ -23,7 +23,7 @@ from mne import Epochs, compute_proj_raw, io, pick_types, read_events from mne.decoding import CSP, LinearModel, Scaler, SPoC, get_coef, read_csp, read_spoc from mne.decoding.csp import _ajd_pham -from mne.decoding.tests import parametrize_with_checks +from mne.decoding.tests._sklearn import parametrize_with_checks from mne.utils import catch_logging, check_version data_dir = Path(__file__).parents[2] / "io" / "tests" / "data" diff --git a/mne/decoding/tests/test_ems.py b/mne/decoding/tests/test_ems.py index 1a82594890f..4660634525f 100644 --- a/mne/decoding/tests/test_ems.py +++ b/mne/decoding/tests/test_ems.py @@ -14,7 +14,7 @@ from mne import Epochs, io, pick_types, read_events from mne.decoding import EMS, compute_ems -from mne.decoding.tests import parametrize_with_checks +from mne.decoding.tests._sklearn import parametrize_with_checks data_dir = Path(__file__).parents[2] / "io" / "tests" / "data" raw_fname = data_dir / "test_raw.fif" diff --git a/mne/decoding/tests/test_ged.py b/mne/decoding/tests/test_ged.py index 8d8f795e6e2..00c18250460 100644 --- a/mne/decoding/tests/test_ged.py +++ b/mne/decoding/tests/test_ged.py @@ -28,7 +28,7 @@ ) from mne.decoding._mod_ged import _no_op_mod from mne.decoding.base import _GEDTransformer -from mne.decoding.tests import parametrize_with_checks +from mne.decoding.tests._sklearn import parametrize_with_checks from mne.io import read_raw data_dir = Path(__file__).parents[2] / "io" / "tests" / "data" diff --git a/mne/decoding/tests/test_receptive_field.py b/mne/decoding/tests/test_receptive_field.py index 84e9d4d5b1e..f7658172680 100644 --- a/mne/decoding/tests/test_receptive_field.py +++ b/mne/decoding/tests/test_receptive_field.py @@ -21,7 +21,7 @@ _delays_to_slice, _times_to_delays, ) -from mne.decoding.tests import parametrize_with_checks +from mne.decoding.tests._sklearn import parametrize_with_checks from mne.decoding.time_delaying_ridge import _compute_corrs, _compute_reg_neighbors data_dir = Path(__file__).parents[2] / "io" / "tests" / "data" diff --git a/mne/decoding/tests/test_search_light.py b/mne/decoding/tests/test_search_light.py index e48ceae75d6..9878daa471c 100644 --- a/mne/decoding/tests/test_search_light.py +++ b/mne/decoding/tests/test_search_light.py @@ -22,7 +22,7 @@ from sklearn.svm import SVC from mne.decoding.search_light import GeneralizingEstimator, SlidingEstimator -from mne.decoding.tests import parametrize_with_checks +from mne.decoding.tests._sklearn import parametrize_with_checks from mne.decoding.transformer import Vectorizer from mne.utils import check_version, use_log_level diff --git a/mne/decoding/tests/test_ssd.py b/mne/decoding/tests/test_ssd.py index fe9dcd3bfcc..f77bc7a3f20 100644 --- a/mne/decoding/tests/test_ssd.py +++ b/mne/decoding/tests/test_ssd.py @@ -18,7 +18,7 @@ from mne.decoding import CSP from mne.decoding._mod_ged import _get_spectral_ratio from mne.decoding.ssd import SSD, read_ssd -from mne.decoding.tests import parametrize_with_checks +from mne.decoding.tests._sklearn import parametrize_with_checks from mne.filter import filter_data from mne.time_frequency import psd_array_welch diff --git a/mne/decoding/tests/test_time_frequency.py b/mne/decoding/tests/test_time_frequency.py index 8fbf7e48105..6d0dd684319 100644 --- a/mne/decoding/tests/test_time_frequency.py +++ b/mne/decoding/tests/test_time_frequency.py @@ -11,7 +11,7 @@ from sklearn.base import clone -from mne.decoding.tests import parametrize_with_checks +from mne.decoding.tests._sklearn import parametrize_with_checks from mne.decoding.time_frequency import TimeFrequency diff --git a/mne/decoding/tests/test_transformer.py b/mne/decoding/tests/test_transformer.py index d29fc62bc0f..4017ceefb51 100644 --- a/mne/decoding/tests/test_transformer.py +++ b/mne/decoding/tests/test_transformer.py @@ -30,7 +30,7 @@ UnsupervisedSpatialFilter, Vectorizer, ) -from mne.decoding.tests import parametrize_with_checks +from mne.decoding.tests._sklearn import parametrize_with_checks from mne.defaults import DEFAULTS from mne.utils import use_log_level diff --git a/mne/decoding/tests/test_xdawn.py b/mne/decoding/tests/test_xdawn.py index 7295cb3ee2c..1353041d9fc 100644 --- a/mne/decoding/tests/test_xdawn.py +++ b/mne/decoding/tests/test_xdawn.py @@ -9,7 +9,7 @@ pytest.importorskip("sklearn") from mne.decoding import XdawnTransformer, read_xdawn_transformer -from mne.decoding.tests import parametrize_with_checks +from mne.decoding.tests._sklearn import parametrize_with_checks @pytest.mark.filterwarnings("ignore:.*Only one sample available.*")