BUG: Test collection for Transformer fails

### Describe the bug

On latest `scientific-python-nightly-wheels` wheel things were passing yesterday but [now we now get the following](https://github.com/mne-tools/mne-python/actions/runs/11720293695/job/32675716039#step:17:68) when [using parametrize_with_checks](https://github.com/mne-tools/mne-python/blob/649857aacb24a0afc3b069f1e75bb3cf843a8766/mne/decoding/tests/test_search_light.py#L346):

```
/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'
```

<details>
<summary>Full traceback</summary>

```
  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'
```

</details>

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

:point_up: 

### Versions

```shell
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
```

COMMENT:
Your implementation is a bit odd to me. The issue comes from here:

```py
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

```py
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 😅

Labels: Bug, Developer API