Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BUG: Test collection for Transformer fails #30237

Closed
larsoner opened this issue Nov 7, 2024 · 10 comments · Fixed by #30248
Closed

BUG: Test collection for Transformer fails #30237

larsoner opened this issue Nov 7, 2024 · 10 comments · Fixed by #30248
Labels
Bug Developer API Third party developer API related
Milestone

Comments

@larsoner
Copy link
Contributor

larsoner commented Nov 7, 2024

Describe the bug

On latest scientific-python-nightly-wheels wheel things were passing yesterday but now we now get the following when using parametrize_with_checks:

/opt/hostedtoolcache/Python/3.12.7/x64/lib/python3.12/site-packages/sklearn/utils/estimator_checks.py:[26](https://github.com/mne-tools/mne-python/actions/runs/11720293695/job/32675716039#step:17:27)9: in _yield_transformer_checks
    if tags.transformer_tags.preserves_dtype:
E   AttributeError: 'NoneType' object has no attribute 'preserves_dtype'
Full traceback
  Downloading https://pypi.anaconda.org/scientific-python-nightly-wheels/simple/scikit-learn/1.6.dev0/scikit_learn-1.6.dev0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (13.1 MB)
...
$ mne sys_info
...
├☑ sklearn              1.6.dev0
...
$ pytest -m 'not (ultraslowtest or pgtest)' --tb=short --cov=mne --cov-report xml --color=yes --junit-xml=junit-results.xml -vv mne/
============================= test session starts ==============================
platform linux -- Python 3.12.7, pytest-8.3.3, pluggy-1.5.0 -- /opt/hostedtoolcache/Python/3.12.7/x64/bin/python
cachedir: .pytest_cache
PyQt6 6.7.1 -- Qt runtime 6.7.3 -- Qt compiled 6.7.1
MNE 1.9.0.dev108+gcc0a15c0b -- /home/runner/work/mne-python/mne-python/mne
rootdir: /home/runner/work/mne-python/mne-python
configfile: pyproject.toml
plugins: timeout-2.3.1, qt-4.4.0, cov-6.0.0
collecting ... collected 4694 items / 1 error / 70 deselected / 5 skipped / 4624 selected



==================================== ERRORS ====================================
___________ ERROR collecting mne/decoding/tests/test_search_light.py ___________
/opt/hostedtoolcache/Python/3.12.7/x64/lib/python3.12/site-packages/pluggy/_hooks.py:513: in __call__
    return self._hookexec(self.name, self._hookimpls.copy(), kwargs, firstresult)
/opt/hostedtoolcache/Python/3.12.7/x64/lib/python3.12/site-packages/pluggy/_manager.py:120: in _hookexec
    return self._inner_hookexec(hook_name, methods, kwargs, firstresult)
/opt/hostedtoolcache/Python/3.12.7/x64/lib/python3.12/site-packages/_pytest/python.py:245: in pytest_pycollect_makeitem
    return list(collector._genfunctions(name, obj))
/opt/hostedtoolcache/Python/3.12.7/x64/lib/python3.12/site-packages/_pytest/python.py:462: in _genfunctions
    self.ihook.pytest_generate_tests.call_extra(methods, dict(metafunc=metafunc))
/opt/hostedtoolcache/Python/3.12.7/x64/lib/python3.12/site-packages/pluggy/_hooks.py:574: in call_extra
    return self._hookexec(self.name, hookimpls, kwargs, firstresult)
/opt/hostedtoolcache/Python/3.12.7/x64/lib/python3.12/site-packages/pluggy/_manager.py:120: in _hookexec
    return self._inner_hookexec(hook_name, methods, kwargs, firstresult)
/opt/hostedtoolcache/Python/3.12.7/x64/lib/python3.12/site-packages/_pytest/python.py:115: in pytest_generate_tests
    metafunc.parametrize(*marker.args, **marker.kwargs, _param_mark=marker)
/opt/hostedtoolcache/Python/3.12.7/x64/lib/python3.12/site-packages/_pytest/python.py:1206: in parametrize
    argnames, parametersets = ParameterSet._for_parametrize(
/opt/hostedtoolcache/Python/3.12.7/x64/lib/python3.12/site-packages/_pytest/mark/structures.py:159: in _for_parametrize
    parameters = cls._parse_parametrize_parameters(argvalues, force_tuple)
/opt/hostedtoolcache/Python/3.12.7/x64/lib/python3.12/site-packages/_pytest/mark/structures.py:146: in _parse_parametrize_parameters
    ParameterSet.extract_from(x, force_tuple=force_tuple) for x in argvalues
/opt/hostedtoolcache/Python/3.12.7/x64/lib/python3.12/site-packages/sklearn/utils/estimator_checks.py:518: in checks_generator
    for check in _yield_all_checks(estimator, legacy=legacy):
/opt/hostedtoolcache/Python/3.12.7/x64/lib/python3.12/site-packages/sklearn/utils/estimator_checks.py:369: in _yield_all_checks
    for check in _yield_transformer_checks(estimator):
/opt/hostedtoolcache/Python/3.12.7/x64/lib/python3.12/site-packages/sklearn/utils/estimator_checks.py:[26](https://github.com/mne-tools/mne-python/actions/runs/11720293695/job/32675716039#step:17:27)9: in _yield_transformer_checks
    if tags.transformer_tags.preserves_dtype:
E   AttributeError: 'NoneType' object has no attribute 'preserves_dtype'

According to a local git bisect this was introduced by #30122. I see some API entries and maybe we need to adjust some code, but it seems like an API change should at least emit a future or deprecation warning rather than fail hard like this.

Steps/Code to Reproduce

$ pytest mne/decoding/tests/test_search_light.py

Expected Results

Tests pass (or at least we get an informative error about our misuse of something!)

Actual Results

☝️

Versions

System:
    python: 3.12.3 (main, Sep 11 2024, 14:17:37) [GCC 13.2.0]
executable: /home/larsoner/python/virtualenvs/base/bin/python
   machine: Linux-6.8.0-48-generic-x86_64-with-glibc2.39

Python dependencies:
      sklearn: 1.6.dev0
          pip: 24.0
   setuptools: 75.3.0
        numpy: 2.2.0.dev0
        scipy: 1.15.0.dev0
       Cython: 3.0.10
       pandas: 3.0.0.dev0+1597.g9e10119dc8
   matplotlib: 3.10.0.dev491+gf7b3def52b
       joblib: 1.4.2
threadpoolctl: 3.5.0

Built with OpenMP: True

threadpoolctl info:
       user_api: blas
   internal_api: openblas
    num_threads: 1
         prefix: libscipy_openblas
       filepath: /home/larsoner/python/virtualenvs/base/lib/python3.12/site-packages/numpy.libs/libscipy_openblas64_-6bb31eeb.so
        version: 0.3.28
threading_layer: pthreads
   architecture: Haswell

       user_api: blas
   internal_api: openblas
    num_threads: 1
         prefix: libscipy_openblas
       filepath: /home/larsoner/python/virtualenvs/base/lib/python3.12/site-packages/scipy.libs/libscipy_openblas-68440149.so
        version: 0.3.28
threading_layer: pthreads
   architecture: Haswell

       user_api: openmp
   internal_api: openmp
    num_threads: 8
         prefix: libgomp
       filepath: /usr/lib/x86_64-linux-gnu/libgomp.so.1.0.0
        version: None
@adrinjalali
Copy link
Member

It'd be useful to have more info in your log. You might want to add a -l to your pytest args in your CI.

However, you're right that this is coming from the tags infrastructure, and if I were to guess, it's because you have a transformer which doesn't inherit from TransformerMixin. You could try to implement the right tags yourself of course, but the mixins should be the way to go.

In any case, you're right that the message should be better and tell developers that we've switched our tags infrastructure. I'll push a PR for that.

@adrinjalali adrinjalali added Developer API Third party developer API related and removed Needs Triage Issue requires triage labels Nov 8, 2024
@adrinjalali adrinjalali added this to the 1.6 milestone Nov 8, 2024
@adrinjalali
Copy link
Member

Adding this to the milestone since this will affect many people, and it'd be nice for them to get a better error message.

@adrinjalali
Copy link
Member

FWIW, I can't get the tests pass on scikit-learn=1.5.2, since I get a segfault:

mne/gui/tests/test_coreg.py::test_splash_closed[pyvistaqt] Fatal Python error: Segmentation fault

Thread 0x00007a9a4cc006c0 (most recent call first):
  File "/home/adrin/micromamba/envs/mne-delete/lib/python3.12/threading.py", line 355 in wait
  File "/home/adrin/micromamba/envs/mne-delete/lib/python3.12/site-packages/joblib/externals/loky/backend/queues.py", line 147 in _feed
  File "/home/adrin/micromamba/envs/mne-delete/lib/python3.12/threading.py", line 1012 in run
  File "/home/adrin/micromamba/envs/mne-delete/lib/python3.12/threading.py", line 1075 in _bootstrap_inner
  File "/home/adrin/micromamba/envs/mne-delete/lib/python3.12/threading.py", line 1032 in _bootstrap

Thread 0x00007a9a4c2006c0 (most recent call first):
  File "/home/adrin/micromamba/envs/mne-delete/lib/python3.12/selectors.py", line 415 in select
  File "/home/adrin/micromamba/envs/mne-delete/lib/python3.12/multiprocessing/connection.py", line 1136 in wait
  File "/home/adrin/micromamba/envs/mne-delete/lib/python3.12/site-packages/joblib/externals/loky/process_executor.py", line 654 in wait_result_broken_or_wakeup
  File "/home/adrin/micromamba/envs/mne-delete/lib/python3.12/site-packages/joblib/externals/loky/process_executor.py", line 596 in run
  File "/home/adrin/micromamba/envs/mne-delete/lib/python3.12/threading.py", line 1075 in _bootstrap_inner
  File "/home/adrin/micromamba/envs/mne-delete/lib/python3.12/threading.py", line 1032 in _bootstrap

Thread 0x00007a9a49a006c0 (most recent call first):
  File "/home/adrin/micromamba/envs/mne-delete/lib/python3.12/threading.py", line 359 in wait
  File "/home/adrin/micromamba/envs/mne-delete/lib/python3.12/threading.py", line 655 in wait
  File "/home/adrin/micromamba/envs/mne-delete/lib/python3.12/site-packages/tqdm/_monitor.py", line 60 in run
  File "/home/adrin/micromamba/envs/mne-delete/lib/python3.12/threading.py", line 1075 in _bootstrap_inner
  File "/home/adrin/micromamba/envs/mne-delete/lib/python3.12/threading.py", line 1032 in _bootstrap

Current thread 0x00007a9b3ed51640 (most recent call first):
  File "/home/adrin/micromamba/envs/mne-delete/lib/python3.12/site-packages/pyvista/plotting/render_window_interactor.py", line 1372 in initialize
  File "/home/adrin/micromamba/envs/mne-delete/lib/python3.12/site-packages/pyvistaqt/plotting.py", line 287 in _setup_interactor
  File "/home/adrin/micromamba/envs/mne-delete/lib/python3.12/site-packages/pyvistaqt/plotting.py", line 247 in __init__
  File "/home/adrin/micromamba/envs/mne-delete/lib/python3.12/site-packages/pyvistaqt/plotting.py", line 572 in __init__
  File "/tmp/mne-python/mne/viz/backends/_pyvista.py", line 150 in _build
  File "/tmp/mne-python/mne/viz/backends/_pyvista.py", line 256 in __init__
  File "/tmp/mne-python/mne/viz/backends/_qt.py", line 1778 in __init__
  File "/tmp/mne-python/mne/viz/backends/_utils.py", line 391 in func
  File "/tmp/mne-python/mne/viz/backends/renderer.py", line 53 in _get_renderer
  File "/tmp/mne-python/mne/gui/_coreg.py", line 284 in __init__
  File "<decorator-gen-607>", line 12 in __init__
  File "/tmp/mne-python/mne/viz/backends/_utils.py", line 391 in func
  File "/tmp/mne-python/mne/gui/_gui.py", line 149 in coregistration
  File "<decorator-gen-606>", line 12 in coregistration
  File "/tmp/mne-python/mne/gui/tests/test_coreg.py", line 390 in test_splash_closed
  File "/home/adrin/micromamba/envs/mne-delete/lib/python3.12/site-packages/_pytest/python.py", line 159 in pytest_pyfunc_call
  File "/home/adrin/micromamba/envs/mne-delete/lib/python3.12/site-packages/pluggy/_callers.py", line 103 in _multicall
  File "/home/adrin/micromamba/envs/mne-delete/lib/python3.12/site-packages/pluggy/_manager.py", line 120 in _hookexec
  File "/home/adrin/micromamba/envs/mne-delete/lib/python3.12/site-packages/pluggy/_hooks.py", line 513 in __call__
  File "/home/adrin/micromamba/envs/mne-delete/lib/python3.12/site-packages/_pytest/python.py", line 1627 in runtest
  File "/home/adrin/micromamba/envs/mne-delete/lib/python3.12/site-packages/_pytest/runner.py", line 174 in pytest_runtest_call
  File "/home/adrin/micromamba/envs/mne-delete/lib/python3.12/site-packages/pluggy/_callers.py", line 103 in _multicall
  File "/home/adrin/micromamba/envs/mne-delete/lib/python3.12/site-packages/pluggy/_manager.py", line 120 in _hookexec
  File "/home/adrin/micromamba/envs/mne-delete/lib/python3.12/site-packages/pluggy/_hooks.py", line 513 in __call__
  File "/home/adrin/micromamba/envs/mne-delete/lib/python3.12/site-packages/_pytest/runner.py", line 242 in <lambda>
  File "/home/adrin/micromamba/envs/mne-delete/lib/python3.12/site-packages/_pytest/runner.py", line 341 in from_call
  File "/home/adrin/micromamba/envs/mne-delete/lib/python3.12/site-packages/_pytest/runner.py", line 241 in call_and_report
  File "/home/adrin/micromamba/envs/mne-delete/lib/python3.12/site-packages/_pytest/runner.py", line 132 in runtestprotocol
  File "/home/adrin/micromamba/envs/mne-delete/lib/python3.12/site-packages/_pytest/runner.py", line 113 in pytest_runtest_protocol
  File "/home/adrin/micromamba/envs/mne-delete/lib/python3.12/site-packages/pluggy/_callers.py", line 103 in _multicall
  File "/home/adrin/micromamba/envs/mne-delete/lib/python3.12/site-packages/pluggy/_manager.py", line 120 in _hookexec
  File "/home/adrin/micromamba/envs/mne-delete/lib/python3.12/site-packages/pluggy/_hooks.py", line 513 in __call__
  File "/home/adrin/micromamba/envs/mne-delete/lib/python3.12/site-packages/_pytest/main.py", line 362 in pytest_runtestloop
  File "/home/adrin/micromamba/envs/mne-delete/lib/python3.12/site-packages/pluggy/_callers.py", line 103 in _multicall
  File "/home/adrin/micromamba/envs/mne-delete/lib/python3.12/site-packages/pluggy/_manager.py", line 120 in _hookexec
  File "/home/adrin/micromamba/envs/mne-delete/lib/python3.12/site-packages/pluggy/_hooks.py", line 513 in __call__
  File "/home/adrin/micromamba/envs/mne-delete/lib/python3.12/site-packages/_pytest/main.py", line 337 in _main
  File "/home/adrin/micromamba/envs/mne-delete/lib/python3.12/site-packages/_pytest/main.py", line 283 in wrap_session
  File "/home/adrin/micromamba/envs/mne-delete/lib/python3.12/site-packages/_pytest/main.py", line 330 in pytest_cmdline_main
  File "/home/adrin/micromamba/envs/mne-delete/lib/python3.12/site-packages/pluggy/_callers.py", line 103 in _multicall
  File "/home/adrin/micromamba/envs/mne-delete/lib/python3.12/site-packages/pluggy/_manager.py", line 120 in _hookexec
  File "/home/adrin/micromamba/envs/mne-delete/lib/python3.12/site-packages/pluggy/_hooks.py", line 513 in __call__
  File "/home/adrin/micromamba/envs/mne-delete/lib/python3.12/site-packages/_pytest/config/__init__.py", line 175 in main
  File "/home/adrin/micromamba/envs/mne-delete/lib/python3.12/site-packages/_pytest/config/__init__.py", line 201 in console_main
  File "/home/adrin/micromamba/envs/mne-delete/bin/pytest", line 8 in <module>

Extension modules: numpy.core._multiarray_umath, numpy.core._multiarray_tests, numpy.linalg._umath_linalg, numpy.fft._pocketfft_internal, numpy.random._common, numpy.random.bit_generator, numpy.random._bounded_integers, numpy.random._mt19937, numpy.random.mtrand, numpy.random._philox, numpy.random._pcg64, numpy.random._sfc64, numpy.random._generator, scipy._lib._ccallback_c, yaml._yaml, numba.core.typeconv._typeconv, numba._helperlib, numba._dynfunc, numba._dispatcher, numba.core.runtime._nrt_python, numba.np.ufunc._internal, numba.experimental.jitclass._box, scipy.special._ufuncs_cxx, scipy.special._ufuncs, scipy.special._specfun, scipy.special._comb, scipy.linalg._fblas, scipy.linalg._flapack, scipy.linalg.cython_lapack, scipy.linalg._cythonized_array_utils, scipy.linalg._solve_toeplitz, scipy.linalg._decomp_lu_cython, scipy.linalg._matfuncs_sqrtm_triu, scipy.linalg.cython_blas, scipy.linalg._matfuncs_expm, scipy.linalg._decomp_update, scipy.sparse._sparsetools, _csparsetools, scipy.sparse._csparsetools, scipy.sparse.linalg._dsolve._superlu, scipy.sparse.linalg._eigen.arpack._arpack, scipy.sparse.linalg._propack._spropack, scipy.sparse.linalg._propack._dpropack, scipy.sparse.linalg._propack._cpropack, scipy.sparse.linalg._propack._zpropack, scipy.sparse.csgraph._tools, scipy.sparse.csgraph._shortest_path, scipy.sparse.csgraph._traversal, scipy.sparse.csgraph._min_spanning_tree, scipy.sparse.csgraph._flow, scipy.sparse.csgraph._matching, scipy.sparse.csgraph._reordering, scipy.special._ellip_harm_2, scipy.interpolate._fitpack, scipy.interpolate._dfitpack, scipy.optimize._group_columns, scipy._lib.messagestream, scipy.optimize._trlib._trlib, scipy.optimize._lbfgsb, _moduleTNC, scipy.optimize._moduleTNC, scipy.optimize._cobyla, scipy.optimize._slsqp, scipy.optimize._minpack, scipy.optimize._lsq.givens_elimination, scipy.optimize._zeros, scipy.optimize._highs.cython.src._highs_wrapper, scipy.optimize._highs._highs_wrapper, scipy.optimize._highs.cython.src._highs_constants, scipy.optimize._highs._highs_constants, scipy.linalg._interpolative, scipy.optimize._bglu_dense, scipy.optimize._lsap, scipy.spatial._ckdtree, scipy.spatial._qhull, scipy.spatial._voronoi, scipy.spatial._distance_wrap, scipy.spatial._hausdorff, scipy.spatial.transform._rotation, scipy.optimize._direct, scipy.interpolate._bspl, scipy.interpolate._ppoly, scipy.interpolate.interpnd, scipy.interpolate._rbfinterp_pythran, scipy.interpolate._rgi_cython, scipy.io.matlab._mio_utils, scipy.io.matlab._streams, scipy.io.matlab._mio5_utils, scipy.ndimage._nd_image, _ni_label, scipy.ndimage._ni_label, scipy.signal._sigtools, scipy._lib._uarray._uarray, scipy.signal._max_len_seq_inner, scipy.signal._upfirdn_apply, scipy.signal._spline, scipy.signal._sosfilt, scipy.signal._spectral, scipy.integrate._odepack, scipy.integrate._quadpack, scipy.integrate._vode, scipy.integrate._dop, scipy.integrate._lsoda, scipy.special.cython_special, scipy.stats._stats, scipy.stats._biasedurn, scipy.stats._levy_stable.levyst, scipy.stats._stats_pythran, scipy.stats._ansari_swilk_statistics, scipy.stats._sobol, scipy.stats._qmc_cy, scipy.stats._mvn, scipy.stats._rcont.rcont, scipy.stats._unuran.unuran_wrapper, scipy.signal._peak_finding_utils, PIL._imaging, kiwisolver._cext, shiboken6.Shiboken, PySide6.QtCore, PySide6.QtGui, PySide6.QtWidgets, PySide6.QtTest, sklearn.__check_build._check_build, psutil._psutil_linux, psutil._psutil_posix, pyarrow.lib, pandas._libs.tslibs.ccalendar, pandas._libs.tslibs.np_datetime, pandas._libs.tslibs.dtypes, pandas._libs.tslibs.base, pandas._libs.tslibs.nattype, pandas._libs.tslibs.timezones, pandas._libs.tslibs.fields, pandas._libs.tslibs.timedeltas, pandas._libs.tslibs.tzconversion, pandas._libs.tslibs.timestamps, pandas._libs.properties, pandas._libs.tslibs.offsets, pandas._libs.tslibs.strptime, pandas._libs.tslibs.parsing, pandas._libs.tslibs.conversion, pandas._libs.tslibs.period, pandas._libs.tslibs.vectorized, pandas._libs.ops_dispatch, pandas._libs.missing, pandas._libs.hashtable, pandas._libs.algos, pandas._libs.interval, pandas._libs.lib, pyarrow._compute, pandas._libs.ops, numexpr.interpreter, pandas._libs.hashing, pandas._libs.arrays, pandas._libs.tslib, pandas._libs.sparse, pandas._libs.internals, pandas._libs.indexing, pandas._libs.index, pandas._libs.writers, pandas._libs.join, pandas._libs.window.aggregations, pandas._libs.window.indexers, pandas._libs.reshape, pandas._libs.groupby, pandas._libs.json, pandas._libs.parsers, pandas._libs.testing, sklearn.utils._isfinite, sklearn.utils.sparsefuncs_fast, sklearn.utils.murmurhash, sklearn.utils._openmp_helpers, sklearn.metrics.cluster._expected_mutual_info_fast, sklearn.preprocessing._csr_polynomial_expansion, sklearn.preprocessing._target_encoder_fast, sklearn.metrics._dist_metrics, sklearn.metrics._pairwise_distances_reduction._datasets_pair, sklearn.utils._cython_blas, sklearn.metrics._pairwise_distances_reduction._base, sklearn.metrics._pairwise_distances_reduction._middle_term_computer, sklearn.utils._heap, sklearn.utils._sorting, sklearn.metrics._pairwise_distances_reduction._argkmin, sklearn.metrics._pairwise_distances_reduction._argkmin_classmode, sklearn.utils._vector_sentinel, sklearn.metrics._pairwise_distances_reduction._radius_neighbors, sklearn.metrics._pairwise_distances_reduction._radius_neighbors_classmode, sklearn.metrics._pairwise_fast, sklearn.utils._random, sklearn.utils._seq_dataset, sklearn.linear_model._cd_fast, _loss, sklearn._loss._loss, sklearn.utils.arrayfuncs, sklearn.svm._liblinear, sklearn.svm._libsvm, sklearn.svm._libsvm_sparse, sklearn.utils._weight_vector, sklearn.linear_model._sgd_fast, sklearn.linear_model._sag_fast, sklearn.datasets._svmlight_format_fast, sklearn.feature_extraction._hashing_fast, sklearn.neighbors._partition_nodes, sklearn.neighbors._ball_tree, sklearn.neighbors._kd_tree, sklearn.decomposition._online_lda_fast, sklearn.decomposition._cdnmf_fast, sklearn.tree._utils, sklearn.neighbors._quad_tree, sklearn.tree._tree, sklearn.tree._splitter, sklearn.tree._criterion, sklearn.ensemble._gradient_boosting, sklearn.ensemble._hist_gradient_boosting.common, sklearn.ensemble._hist_gradient_boosting._gradient_boosting, sklearn.ensemble._hist_gradient_boosting._binning, sklearn.ensemble._hist_gradient_boosting._bitset, sklearn.ensemble._hist_gradient_boosting.histogram, sklearn.ensemble._hist_gradient_boosting._predictor, sklearn.ensemble._hist_gradient_boosting.splitting, openmeeg._openmeeg, antio.libeep.pyeep, h5py._errors, h5py.defs, h5py._objects, h5py.h5, h5py.utils, h5py.h5t, h5py.h5s, h5py.h5ac, h5py.h5p, h5py.h5r, h5py._proxy, h5py._conv, h5py.h5z, h5py.h5a, h5py.h5d, h5py.h5ds, h5py.h5g, h5py.h5i, h5py.h5o, h5py.h5f, h5py.h5fd, h5py.h5pl, h5py.h5l, h5py._selector, _cffi_backend, scipy.fftpack.convolve, vtkmodules.vtkCommonCore, vtkmodules.vtkCommonMath, vtkmodules.vtkCommonTransforms, vtkmodules.vtkCommonDataModel, vtkmodules.vtkCommonExecutionModel, vtkmodules.vtkImagingSources, vtkmodules.vtkCommonMisc, vtkmodules.vtkFiltersCore, vtkmodules.vtkFiltersPython, vtkmodules.vtkCommonComputationalGeometry, vtkmodules.vtkIOCore, vtkmodules.vtkIOLegacy, vtkmodules.vtkIOXMLParser, vtkmodules.vtkRenderingCore, vtkmodules.vtkDomainsChemistry, vtkmodules.vtkFiltersGeneral, vtkmodules.vtkFiltersExtraction, vtkmodules.vtkFiltersFlowPaths, vtkmodules.vtkFiltersGeometry, vtkmodules.vtkFiltersHybrid, vtkmodules.vtkFiltersModeling, vtkmodules.vtkFiltersHyperTree, vtkmodules.vtkFiltersSources, vtkmodules.vtkFiltersTexture, vtkmodules.vtkParallelCore, vtkmodules.vtkFiltersParallel, vtkmodules.vtkFiltersParallelDIY2, vtkmodules.vtkFiltersPoints, vtkmodules.vtkFiltersStatistics, vtkmodules.vtkFiltersVerdict, vtkmodules.vtkImagingCore, vtkmodules.vtkImagingGeneral, vtkmodules.vtkImagingHybrid, vtkmodules.vtkIOGeometry, vtkmodules.vtkIOXML, vtkmodules.vtkIOInfovis, vtkmodules.vtkIOPLY, vtkmodules.vtkImagingMorphological, vtkmodules.vtkRenderingContext2D, vtkmodules.vtkPythonContext2D, vtkmodules.vtkImagingFourier, vtkmodules.vtkIOParallelXML, vtkmodules.vtkRenderingFreeType, vtkmodules.vtkChartsCore, vtkmodules.vtkCommonColor, vtkmodules.vtkInteractionWidgets, vtkmodules.vtkRenderingAnnotation, vtkmodules.vtkRenderingLabel, vtkmodules.vtkRenderingUI, vtkmodules.vtkRenderingVolume, vtkmodules.vtkViewsCore, vtkmodules.vtkViewsContext2D, vtkmodules.vtkIOImage, vtkmodules.vtkRenderingHyperTreeGrid, vtkmodules.vtkRenderingOpenGL2, vtkmodules.vtkRenderingContextOpenGL2, vtkmodules.vtkImagingMath, vtkmodules.vtkRenderingVolumeOpenGL2, PySide6.QtOpenGL, PySide6.QtOpenGLWidgets, _brotli, zstandard.backend_c, markupsafe._speedups, PySide6.QtSvg, vtkmodules.vtkWebCore, vtkmodules.vtkRenderingSceneGraph, vtkmodules.vtkRenderingVtkJS, vtkmodules.vtkIOExport, vtkmodules.vtkWebGLExporter, vtkmodules.vtkTestingRendering, vtkmodules.vtkInteractionStyle, vtkmodules.vtkViewsInfovis, vtkmodules.vtkRenderingMatplotlib, vtkmodules.vtkRenderingLOD, vtkmodules.vtkRenderingLICOpenGL2, vtkmodules.vtkRenderingImage, vtkmodules.vtkFiltersCellGrid, vtkmodules.vtkRenderingCellGrid, vtkmodules.vtkIOXdmf2, vtkmodules.vtkIOVeraOut, vtkmodules.vtkIOTecplotTable, vtkmodules.vtkIOSegY, vtkmodules.vtkIOXdmf3, vtkmodules.vtkIOMovie, vtkmodules.vtkIOOggTheora, vtkmodules.vtkIONetCDF, vtkmodules.vtkIOMotionFX, vtkmodules.vtkIOParallel, vtkmodules.vtkIOMINC, vtkmodules.vtkIOLSDyna, vtkmodules.vtkIOImport, vtkmodules.vtkIOIOSS, vtkmodules.vtkIOFLUENTCFF, vtkmodules.vtkIOVideo, vtkmodules.vtkIOFFMPEG, vtkmodules.vtkIOExportPDF, vtkmodules.vtkRenderingGL2PSOpenGL2, vtkmodules.vtkIOExportGL2PS, vtkmodules.vtkIOExodus, vtkmodules.vtkIOEnSight, vtkmodules.vtkIOCityGML, vtkmodules.vtkIOChemistry, vtkmodules.vtkIOCesium3DTiles, vtkmodules.vtkIOCellGrid, vtkmodules.vtkIOCONVERGECFD, vtkmodules.vtkIOHDF, vtkmodules.vtkIOCGNSReader, vtkmodules.vtkIOAsynchronous, vtkmodules.vtkIOAMR, vtkmodules.vtkInteractionImage, vtkmodules.vtkImagingStencil, vtkmodules.vtkImagingStatistics, vtkmodules.vtkIOSQL, vtkmodules.vtkInfovisCore, vtkmodules.vtkGeovisCore, vtkmodules.vtkInfovisLayout, vtkmodules.vtkImagingColor, vtkmodules.vtkFiltersTopology, vtkmodules.vtkFiltersTensor, vtkmodules.vtkFiltersSelection, vtkmodules.vtkFiltersSMP, vtkmodules.vtkFiltersReduction, vtkmodules.vtkFiltersProgrammable, vtkmodules.vtkFiltersImaging, vtkmodules.vtkFiltersParallelImaging, vtkmodules.vtkCommonSystem, vtkmodules.vtkFiltersGeometryPreview, vtkmodules.vtkFiltersGeneric, vtkmodules.vtkFiltersAMR, vtkmodules.vtkDomainsChemistryOpenGL2, vtkmodules.vtkCommonPython, msgpack._cmsgpack, multidict._multidict, yarl._quoting_c, propcache._helpers_c, aiohttp._helpers, aiohttp._http_writer, aiohttp._http_parser, aiohttp._websocket, frozenlist._frozenlist, lxml._elementpath, lxml.etree (total: 390)
[1]    955371 segmentation fault (core dumped)  MNE_SKIP_TESTING_DATASET_TESTS=true pytest -m "not (ultraslowtest or pgtest)"

@adrinjalali
Copy link
Member

adrinjalali commented Nov 8, 2024

But I can reproduce the collection error with the nightly release. It comes from SlidingEstimator which is defined as:

https://github.com/mne-tools/mne-python/blob/46089954167343ff2e0d089b9a313674ef7475b6/mne/decoding/search_light.py#L19

Which has:

class SlidingEstimator(BaseEstimator, TransformerMixin):
    ...

But it needs to be:

class SlidingEstimator(TransformerMixin, BaseEstimator):
    ...

We just added a test to check the right order by the way, so you'd be getting an error for this soon anyway. But I'll fix the test collection as well.

@adrinjalali
Copy link
Member

With #30248, I get this on your repo:

$ MNE_SKIP_TESTING_DATASET_TESTS=true pytest -m "not (ultraslowtest or pgtest)" --tb=short --cov=mne --cov-report xml -vv -rfE mne/ -l
==================================================================================================================== test session starts =====================================================================================================================
platform linux -- Python 3.12.7, pytest-8.3.3, pluggy-1.5.0 -- /home/adrin/micromamba/envs/mne-delete/bin/python3.12
cachedir: .pytest_cache
PySide6 6.8.0.2 -- Qt runtime 6.8.0 -- Qt compiled 6.8.0
MNE 1.9.0.dev92+g460899541 -- /tmp/mne-python/mne
rootdir: /tmp/mne-python
configfile: pyproject.toml
plugins: timeout-2.3.1, cov-6.0.0, anyio-4.6.2.post1, qt-4.4.0
collecting 4086 items / 1 error                                                                                                                                                                                                                              Using default location ~/mne_data for testing...
Dataset testing version 0.0 out of date, latest version is 0.156
Dataset out of date but force_update=False and download=False, returning empty data_path
collected 4729 items / 1 error / 70 deselected / 4659 selected                                                                                                                                                                                               



=========================================================================================================================== ERRORS ===========================================================================================================================
__________________________________________________________________________________________________ ERROR collecting mne/decoding/tests/test_search_light.py __________________________________________________________________________________________________
/home/adrin/micromamba/envs/mne-delete/lib/python3.12/site-packages/pluggy/_hooks.py:513: in __call__
    return self._hookexec(self.name, self._hookimpls.copy(), kwargs, firstresult)
        firstresult = True
        kwargs     = {'collector': <Module test_search_light.py>,
 'name': 'test_sklearn_compliance',
 'obj': <function test_sklearn_compliance at 0x7e35a9886de0>}
        self       = <HookCaller 'pytest_pycollect_makeitem'>
/home/adrin/micromamba/envs/mne-delete/lib/python3.12/site-packages/pluggy/_manager.py:120: in _hookexec
    return self._inner_hookexec(hook_name, methods, kwargs, firstresult)
        firstresult = True
        hook_name  = 'pytest_pycollect_makeitem'
        kwargs     = {'collector': <Module test_search_light.py>,
 'name': 'test_sklearn_compliance',
 'obj': <function test_sklearn_compliance at 0x7e35a9886de0>}
        methods    = [<HookImpl plugin_name='python', plugin=<module '_pytest.python' from '/home/adrin/micromamba/envs/mne-delete/lib/python3.12/site-packages/_pytest/python.py'>>,
 <HookImpl plugin_name='unittest', plugin=<module '_pytest.unittest' from '/home/adrin/micromamba/envs/mne-delete/lib/python3.12/site-packages/_pytest/unittest.py'>>,
 <HookImpl plugin_name='anyio', plugin=<module 'anyio.pytest_plugin' from '/home/adrin/micromamba/envs/mne-delete/lib/python3.12/site-packages/anyio/pytest_plugin.py'>>]
        self       = <_pytest.config.PytestPluginManager object at 0x7e36616396a0>
/home/adrin/micromamba/envs/mne-delete/lib/python3.12/site-packages/_pytest/python.py:245: in pytest_pycollect_makeitem
    return list(collector._genfunctions(name, obj))
        collector  = <Module test_search_light.py>
        name       = 'test_sklearn_compliance'
        obj        = <function test_sklearn_compliance at 0x7e35a9886de0>
/home/adrin/micromamba/envs/mne-delete/lib/python3.12/site-packages/_pytest/python.py:462: in _genfunctions
    self.ihook.pytest_generate_tests.call_extra(methods, dict(metafunc=metafunc))
        cls        = None
        clscol     = None
        definition = <FunctionDefinition test_sklearn_compliance>
        fixtureinfo = FuncFixtureInfo(argnames=('estimator', 'check'),
                initialnames=('matplotlib_config',
                              'qt_config',
                              'protect_config',
                              'add_mne',
                              'check_verbose',
                              'close_all',
                              'estimator',
                              'check'),
                names_closure=['matplotlib_config',
                               'qt_config',
                               'protect_config',
                               'doctest_namespace',
                               'add_mne',
                               'check_verbose',
                               'close_all',
                               'estimator',
                               'check',
                               'request'],
                name2fixturedefs={'add_mne': (<FixtureDef argname='add_mne' scope='function' baseid='mne'>,),
                                  'check_verbose': (<FixtureDef argname='check_verbose' scope='function' baseid='mne'>,),
                                  'close_all': (<FixtureDef argname='close_all' scope='function' baseid='mne'>,),
                                  'doctest_namespace': (<FixtureDef argname='doctest_namespace' scope='session' baseid=''>,),
                                  'matplotlib_config': (<FixtureDef argname='matplotlib_config' scope='session' baseid='mne'>,),
                                  'protect_config': (<FixtureDef argname='protect_config' scope='session' baseid='mne'>,),
                                  'qt_config': (<FixtureDef argname='qt_config' scope='session' baseid='mne'>,)})
        funcobj    = <function test_sklearn_compliance at 0x7e35a9886de0>
        metafunc   = <_pytest.python.Metafunc object at 0x7e35a97111c0>
        methods    = []
        module     = <module 'mne.decoding.tests.test_search_light' from '/tmp/mne-python/mne/decoding/tests/test_search_light.py'>
        modulecol  = <Module test_search_light.py>
        name       = 'test_sklearn_compliance'
        self       = <Module test_search_light.py>
/home/adrin/micromamba/envs/mne-delete/lib/python3.12/site-packages/pluggy/_hooks.py:574: in call_extra
    return self._hookexec(self.name, hookimpls, kwargs, firstresult)
        firstresult = False
        hookimpls  = [<HookImpl plugin_name='python', plugin=<module '_pytest.python' from '/home/adrin/micromamba/envs/mne-delete/lib/python3.12/site-packages/_pytest/python.py'>>,
 <HookImpl plugin_name='funcmanage', plugin=<_pytest.fixtures.FixtureManager object at 0x7e35c2bb2210>>]
        kwargs     = {'metafunc': <_pytest.python.Metafunc object at 0x7e35a97111c0>}
        methods    = []
        opts       = {'hookwrapper': False,
 'optionalhook': False,
 'specname': None,
 'tryfirst': False,
 'trylast': False,
 'wrapper': False}
        self       = <HookCaller 'pytest_generate_tests'>
/home/adrin/micromamba/envs/mne-delete/lib/python3.12/site-packages/pluggy/_manager.py:120: in _hookexec
    return self._inner_hookexec(hook_name, methods, kwargs, firstresult)
        firstresult = False
        hook_name  = 'pytest_generate_tests'
        kwargs     = {'metafunc': <_pytest.python.Metafunc object at 0x7e35a97111c0>}
        methods    = [<HookImpl plugin_name='python', plugin=<module '_pytest.python' from '/home/adrin/micromamba/envs/mne-delete/lib/python3.12/site-packages/_pytest/python.py'>>,
 <HookImpl plugin_name='funcmanage', plugin=<_pytest.fixtures.FixtureManager object at 0x7e35c2bb2210>>]
        self       = <_pytest.config.PytestPluginManager object at 0x7e36616396a0>
/home/adrin/micromamba/envs/mne-delete/lib/python3.12/site-packages/_pytest/python.py:115: in pytest_generate_tests
    metafunc.parametrize(*marker.args, **marker.kwargs, _param_mark=marker)
        marker     = Mark(name='parametrize',
     args=('estimator, check',
           <generator object parametrize_with_checks.<locals>.checks_generator at 0x7e35a9afa200>),
     kwargs={'ids': <function _get_check_estimator_ids at 0x7e35a9982480>})
        metafunc   = <_pytest.python.Metafunc object at 0x7e35a97111c0>
/home/adrin/micromamba/envs/mne-delete/lib/python3.12/site-packages/_pytest/python.py:1206: in parametrize
    argnames, parametersets = ParameterSet._for_parametrize(
        _param_mark = Mark(name='parametrize',
     args=('estimator, check',
           <generator object parametrize_with_checks.<locals>.checks_generator at 0x7e35a9afa200>),
     kwargs={'ids': <function _get_check_estimator_ids at 0x7e35a9982480>})
        argnames   = 'estimator, check'
        argvalues  = <generator object parametrize_with_checks.<locals>.checks_generator at 0x7e35a9afa200>
        ids        = <function _get_check_estimator_ids at 0x7e35a9982480>
        indirect   = False
        scope      = None
        self       = <_pytest.python.Metafunc object at 0x7e35a97111c0>
/home/adrin/micromamba/envs/mne-delete/lib/python3.12/site-packages/_pytest/mark/structures.py:159: in _for_parametrize
    parameters = cls._parse_parametrize_parameters(argvalues, force_tuple)
        argnames   = ['estimator', 'check']
        argvalues  = <generator object parametrize_with_checks.<locals>.checks_generator at 0x7e35a9afa200>
        cls        = <class '_pytest.mark.structures.ParameterSet'>
        config     = <_pytest.config.Config object at 0x7e366196a930>
        force_tuple = False
        func       = <function test_sklearn_compliance at 0x7e35a9886de0>
        nodeid     = 'mne/decoding/tests/test_search_light.py::test_sklearn_compliance'
/home/adrin/micromamba/envs/mne-delete/lib/python3.12/site-packages/_pytest/mark/structures.py:146: in _parse_parametrize_parameters
    ParameterSet.extract_from(x, force_tuple=force_tuple) for x in argvalues
        argvalues  = <generator object parametrize_with_checks.<locals>.checks_generator at 0x7e35a9afa200>
        force_tuple = False
/home/adrin/micromamba/envs/mne-delete/lib/python3.12/site-packages/sklearn/utils/estimator_checks.py:520: in checks_generator
    for check in _yield_all_checks(estimator, legacy=legacy):
        check      = <function check_f_contiguous_array_estimator at 0x7e35a9983600>
        check_instance = <SlidingEstimator(allow_2d=True, base_estimator=LogisticRegression())>
        check_with_name = functools.partial(<function check_f_contiguous_array_estimator at 0x7e35a9983600>, 'SlidingEstimator')
        estimator  = <SlidingEstimator(allow_2d=True, base_estimator=LogisticRegression())>
        estimators = [<SlidingEstimator(allow_2d=True, base_estimator=LogisticRegression())>]
        legacy     = True
        name       = 'SlidingEstimator'
        pytest     = <module 'pytest' from '/home/adrin/micromamba/envs/mne-delete/lib/python3.12/site-packages/pytest/__init__.py'>
/home/adrin/micromamba/envs/mne-delete/lib/python3.12/site-packages/sklearn/utils/estimator_checks.py:371: in _yield_all_checks
    for check in _yield_transformer_checks(estimator):
        check      = <function check_f_contiguous_array_estimator at 0x7e35a9983600>
        estimator  = <SlidingEstimator(allow_2d=True, base_estimator=LogisticRegression())>
        legacy     = True
        name       = 'SlidingEstimator'
        tags       = Tags(estimator_type=None,
     target_tags=TargetTags(required=False,
                            one_d_labels=False,
                            two_d_labels=False,
                            positive_only=False,
                            multi_output=False,
                            single_output=True),
     transformer_tags=None,
     classifier_tags=None,
     regressor_tags=None,
     array_api_support=False,
     no_validation=False,
     non_deterministic=False,
     requires_fit=True,
     _skip_test=False,
     _xfail_checks={},
     input_tags=InputTags(one_d_array=False,
                          two_d_array=True,
                          three_d_array=False,
                          sparse=False,
                          categorical=False,
                          string=False,
                          dict=False,
                          positive_only=False,
                          allow_nan=False,
                          pairwise=False))
/home/adrin/micromamba/envs/mne-delete/lib/python3.12/site-packages/sklearn/utils/estimator_checks.py:263: in _yield_transformer_checks
    _raise_for_missing_tags(transformer, "transformer_tags", TransformerMixin)
        transformer = <SlidingEstimator(allow_2d=True, base_estimator=LogisticRegression())>
/home/adrin/micromamba/envs/mne-delete/lib/python3.12/site-packages/sklearn/utils/estimator_checks.py:103: in _raise_for_missing_tags
    raise RuntimeError(
E   RuntimeError: Estimator SlidingEstimator seems to be a Transformer, but the `transformer_tags` tag is not set. Either set the tag manually or inherit from the TransformerMixin. Note that the order of inheritance matters, the TransformerMixin should come before BaseEstimator.
        Mixin      = <class 'sklearn.base.TransformerMixin'>
        estimator  = <SlidingEstimator(allow_2d=True, base_estimator=LogisticRegression())>
        estimator_type = 'Transformer'
        tag_name   = 'transformer_tags'
        tags       = Tags(estimator_type=None,
     target_tags=TargetTags(required=False,
                            one_d_labels=False,
                            two_d_labels=False,
                            positive_only=False,
                            multi_output=False,
                            single_output=True),
     transformer_tags=None,
     classifier_tags=None,
     regressor_tags=None,
     array_api_support=False,
     no_validation=False,
     non_deterministic=False,
     requires_fit=True,
     _skip_test=False,
     _xfail_checks={},
     input_tags=InputTags(one_d_array=False,
                          two_d_array=True,
                          three_d_array=False,
                          sparse=False,
                          categorical=False,
                          string=False,
                          dict=False,
                          positive_only=False,
                          allow_nan=False,
                          pairwise=False))
--------------------------------------------------------------------------------------------------- generated xml file: /tmp/mne-python/junit-results.xml ----------------------------------------------------------------------------------------------------

---------- coverage: platform linux, python 3.12.7-final-0 -----------
Coverage XML written to file coverage.xml

================================================================================================================== short test summary info ===================================================================================================================
ERROR mne/decoding/tests/test_search_light.py - RuntimeError: Estimator SlidingEstimator seems to be a Transformer, but the `transformer_tags` tag is not set. Either set the tag manually or inherit from the TransformerMixin. Note that the order of inheritance matters, the TransformerMixin should come before BaseEstimator.
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! Interrupted: 1 error during collection !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
============================================================================================================== 70 deselected, 1 error in 16.30s ==============================================================================================================

which seems rather nice and informative enough.

@larsoner
Copy link
Contributor Author

larsoner commented Nov 8, 2024

But it needs to be: ...

So on this branch regardless of whether or not I change the inheritance order in MNE-Python to TransformerMixin, BaseEstimator or leave it the other way, a whole bunch of stuff fails. I think it comes down to stuff like is_regressor(...) and is_classifier(...) failing when they used to succeed, for one example:

mne/decoding/tests/test_base.py:97: in test_get_coef
    assert is_classifier(lm_classification)
E   AssertionError: assert False
E    +  where False = is_classifier(LinearModel(model=LogisticRegression(solver='liblinear')))

where LinearModel is defined as:

class LinearModel(BaseEstimator):

This also based on bisect seems to be due to #30122, so maybe the advice needs to be to switch the order and set the tags (assuming that is the correct solution)? And maybe BaseEstimator should have some backward-compatible tags? Or was it an error / bug before that BaseEstimator subclasses would pass is_classifier checks?

@adrinjalali
Copy link
Member

Your implementation is a bit odd to me. The issue comes from here:

class LinearModel(BaseEstimator):
    _model_attr_wrap = (
        ...
        "_estimator_type",
        ...
    )

    ...

    def __getattr__(self, attr):
        """Wrap to model for some attributes."""
        if attr in LinearModel._model_attr_wrap:
            return getattr(self.model, attr)
        elif attr == "fit_transform" and hasattr(self.model, "fit_transform"):
            return super().__getattr__(self, "_fit_transform")
        return super().__getattr__(self, attr)

you're delegating _estimator_type to LinearModel, which used to have that attribute, and now it doesn't and a tag instead defines the estimator type. That's why your test is now failing.

Your class definition here needs to be

class LinearModel(ClassifierMixin, BaseEstimator):
    ...

And when you hack into python's machinery to get attributes from the sub-estimator like you're doing, it's not something we really support 😅

@larsoner
Copy link
Contributor Author

larsoner commented Nov 8, 2024

Yeah... 🤦 Thanks for the additional help here!

In the meantime because LinearModel can wrap both classifiers and regressors and we don't know which it will be... I'm going to make the problem potentially works by adding __tags__ to _model_attr_wrap and see how long the band-aid holds!

@adrinjalali
Copy link
Member

The right way to handle this is more like:

class MyEstimator(MetaEstimatorMixin, BaseEstimator):
    ...
    def __sklearn_tags__(self):
        tags = super().__sklearn_tags__()

        sub_tags = get_tags(self.estimator)
        tags.estimator_type = sub_tags.estimator_type
        ...
        return tags

@larsoner
Copy link
Contributor Author

larsoner commented Nov 8, 2024

Thanks! Added a new issue in our repo mne-tools/mne-python#12952 to remind us to fix it properly at some point.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug Developer API Third party developer API related
Development

Successfully merging a pull request may close this issue.

2 participants