internlm/SWE-Fixer-Train-110K huggingface dataset:
Repository: mne-tools/mne-python

Issue:
in CSD example, use matplotlib OO API instead of state machine
In `examples/time_frequency/compute_csd.py`, remove the calls to `plt.suptitle` and instead save the figure handle and use `fig.suptitle`.


Discussion:

Resolving patch:
diff --git a/examples/time_frequency/compute_csd.py b/examples/time_frequency/compute_csd.py
--- a/examples/time_frequency/compute_csd.py
+++ b/examples/time_frequency/compute_csd.py
@@ -21,8 +21,6 @@
 # License: BSD-3-Clause
 
 # %%
-from matplotlib import pyplot as plt
-
 import mne
 from mne.datasets import sample
 from mne.time_frequency import csd_fourier, csd_multitaper, csd_morlet
@@ -84,11 +82,13 @@
 # The resulting :class:`mne.time_frequency.CrossSpectralDensity` objects have a
 # plotting function we can use to compare the results of the different methods.
 # We're plotting the mean CSD across frequencies.
-csd_fft.mean().plot()
-plt.suptitle('short-term Fourier transform')
-
-csd_mt.mean().plot()
-plt.suptitle('adaptive multitapers')
-
-csd_wav.mean().plot()
-plt.suptitle('Morlet wavelet transform')
+# :func:`mne.time_frequency.CrossSpectralDensity.plot()` returns a list of
+# created figures; in this case, each returned list has only one figure
+# so we use a Python trick of including a comma after our variable name
+# to assign the figure (not the list) to our ``fig`` variable:
+plot_dict = {'Short-time Fourier transform': csd_fft,
+             'Adaptive multitapers': csd_mt,
+             'Morlet wavelet transform': csd_wav}
+for title, csd in plot_dict.items():
+    fig, = csd.mean().plot()
+    fig.suptitle(title)


Test patch:
Repository: mne-tools/mne-python

Issue:
Style of 'bool' x-ref is different than the style of other parameter types
The style for the `bool` x-ref is different than for other parameter types. See for instance the [`set_montage` method](https://mne.tools/dev/generated/mne.Epochs.html#mne.Epochs.set_montage).

So it turns out that `intersphinx` resolves `bool` to https://docs.python.org/3/library/stdtypes.html#boolean-values
and not to https://docs.python.org/3/library/functions.html#bool
which results in a different style.

I see 2 ways to fix it, both over my knowledge in sphinx-related stuff:
- fix the resolution: [`intersphinx_disabled_reftypes`](https://www.sphinx-doc.org/en/master/usage/extensions/intersphinx.html#confval-intersphinx_disabled_reftypes) seems like a good candidate
- overriding the CSS style: c.f. the note for `numpydoc_xref_param_type` on the [`numpydoc` documentation](https://numpydoc.readthedocs.io/en/latest/install.html).


Discussion:
We should be able to make an explicit link here I think

https://github.com/mne-tools/mne-python/blob/main/doc/conf.py#L178
Oh true, that's ten times easier. I'll give it a shot :)

Resolving patch:
diff --git a/doc/conf.py b/doc/conf.py
--- a/doc/conf.py
+++ b/doc/conf.py
@@ -182,6 +182,7 @@
     'path-like': ':term:`path-like`',
     'array-like': ':term:`array-like`',
     'Path': ':class:`python:pathlib.Path`',
+    'bool': ':class:`python:bool`',
     # Matplotlib
     'colormap': ':doc:`colormap <matplotlib:tutorials/colors/colormaps>`',
     'color': ':doc:`color <matplotlib:api/colors_api>`',


Test patch:
Repository: mne-tools/mne-python

Issue:
raw.drop_channels
raw.drop_channels crashes when the channels are not included in the data being processed. Would be convenient to have a warning message instead.


Discussion:
We should add a `on_missing='raise' | 'warn' | 'ignore'` as we do elsewhere. Should be an easy one for someone if they're motivated!

Resolving patch:
diff --git a/mne/channels/channels.py b/mne/channels/channels.py
--- a/mne/channels/channels.py
+++ b/mne/channels/channels.py
@@ -24,7 +24,8 @@
 from ..defaults import HEAD_SIZE_DEFAULT, _handle_default
 from ..utils import (verbose, logger, warn,
                      _check_preload, _validate_type, fill_doc, _check_option,
-                     _get_stim_channel, _check_fname, _check_dict_keys)
+                     _get_stim_channel, _check_fname, _check_dict_keys,
+                     _on_missing)
 from ..io.constants import FIFF
 from ..io.meas_info import (anonymize_info, Info, MontageMixin, create_info,
                             _rename_comps)
@@ -735,13 +736,15 @@ def reorder_channels(self, ch_names):
             idx.append(ii)
         return self._pick_drop_channels(idx)
 
-    def drop_channels(self, ch_names):
+    @fill_doc
+    def drop_channels(self, ch_names, on_missing='raise'):
         """Drop channel(s).
 
         Parameters
         ----------
         ch_names : iterable or str
             Iterable (e.g. list) of channel name(s) or channel name to remove.
+        %(on_missing_ch_names)s
 
         Returns
         -------
@@ -774,7 +777,7 @@ def drop_channels(self, ch_names):
         missing = [ch for ch in ch_names if ch not in self.ch_names]
         if len(missing) > 0:
             msg = "Channel(s) {0} not found, nothing dropped."
-            raise ValueError(msg.format(", ".join(missing)))
+            _on_missing(on_missing, msg.format(", ".join(missing)))
 
         bad_idx = [self.ch_names.index(ch) for ch in ch_names
                    if ch in self.ch_names]


Test patch:
diff --git a/mne/channels/tests/test_channels.py b/mne/channels/tests/test_channels.py
--- a/mne/channels/tests/test_channels.py
+++ b/mne/channels/tests/test_channels.py
@@ -449,6 +449,16 @@ def test_drop_channels():
     pytest.raises(ValueError, raw.drop_channels, ["MEG 0111", 5])
     pytest.raises(ValueError, raw.drop_channels, 5)  # must be list or str
 
+    # by default, drop channels raises a ValueError if a channel can't be found
+    m_chs = ["MEG 0111", "MEG blahblah"]
+    with pytest.raises(ValueError, match='not found, nothing dropped'):
+        raw.drop_channels(m_chs)
+    # ...but this can be turned to a warning
+    with pytest.warns(RuntimeWarning, match='not found, nothing dropped'):
+        raw.drop_channels(m_chs, on_missing='warn')
+    # ...or ignored altogether
+    raw.drop_channels(m_chs, on_missing='ignore')
+
 
 def test_pick_channels():
     """Test if picking channels works with various arguments."""
Repository: mne-tools/mne-python

Issue:
BUG: Example broken on main
@wmvanvliet I think #11080 broke `tutorials/clinical/30_ecog.py`:

https://app.circleci.com/pipelines/github/mne-tools/mne-python/16047/workflows/321560de-8d55-44f2-be22-e68382086eff/jobs/48440

Can you take a look?


Discussion:
Narrowed it down to MNE-BIDS trying to apply the montage, even though no fiducials are there yet. Before, the mri->head transform silently failed and channel positions were applied in MRI space (a situation we don't want). Then later in the example, this is fixed. I'll see if I can make MNE-BIDS be more clever about applying montages.

Resolving patch:
diff --git a/mne/channels/montage.py b/mne/channels/montage.py
--- a/mne/channels/montage.py
+++ b/mne/channels/montage.py
@@ -668,19 +668,11 @@ def transform_to_head(montage):
     # Get fiducial points and their coord_frame
     native_head_t = compute_native_head_t(montage)
     montage = montage.copy()  # to avoid inplace modification
-
     if native_head_t['from'] != FIFF.FIFFV_COORD_HEAD:
         for d in montage.dig:
             if d['coord_frame'] == native_head_t['from']:
                 d['r'] = apply_trans(native_head_t, d['r'])
                 d['coord_frame'] = FIFF.FIFFV_COORD_HEAD
-            elif d['kind'] == FIFF.FIFFV_POINT_EEG:
-                raise RuntimeError(
-                    f'Could not transform EEG channel {d["ident"]} position '
-                    f'from {_verbose_frames[d["coord_frame"]]} to head '
-                    'coordinates. Fiducial points are either missing or '
-                    'specified in a different coordinate frame than the EEG '
-                    'channel locations.')
     return montage
 
 


Test patch:
diff --git a/mne/channels/tests/test_montage.py b/mne/channels/tests/test_montage.py
--- a/mne/channels/tests/test_montage.py
+++ b/mne/channels/tests/test_montage.py
@@ -1210,17 +1210,6 @@ def test_transform_to_head_and_compute_dev_head_t():
             montage_polhemus
         ))
 
-    # Test errors when transforming without fiducials explicitly where points
-    # are tagged to be not in head or unknown coord space.
-    montage_without_fids = make_dig_montage(
-        ch_pos={"ch_1": np.array([1, 2, 3]),
-                "ch_2": np.array([4, 5, 6]),
-                "ch_3": np.array([7, 8, 9])},
-        coord_frame="mri")  # MRI coordinate space
-    with pytest.raises(RuntimeError, match='Could not transform EEG channel'):
-        with pytest.warns(RuntimeWarning, match='Fiducial point .* not found'):
-            transform_to_head(montage_without_fids)
-
 
 def test_set_montage_with_mismatching_ch_names():
     """Test setting a DigMontage with mismatching ch_names."""
Repository: mne-tools/mne-python

Issue:
ValueError for set_log_level('WARN') in example
https://github.com/mne-tools/mne-python/blob/fb80e1163ef1f89479ca7f07cb0e09e996b23cd8/mne/utils/_logging.py#L232

Example should read set_log_level('WARNING') instead of set_log_level('WARN')



Discussion:
Hello! 👋 Thanks for opening your first issue here! ❤️ We will try to get back to you soon. 🚴🏽‍♂️

Indeed ! Can you send us a PR to fix it? 🙏 

Resolving patch:
diff --git a/mne/utils/_logging.py b/mne/utils/_logging.py
--- a/mne/utils/_logging.py
+++ b/mne/utils/_logging.py
@@ -229,7 +229,7 @@ def set_log_file(fname=None, output_format='%(message)s', overwrite=None):
     ----------
     fname : str, or None
         Filename of the log to print to. If None, stdout is used.
-        To suppress log outputs, use set_log_level('WARN').
+        To suppress log outputs, use set_log_level('WARNING').
     output_format : str
         Format of the output messages. See the following for examples:
 


Test patch:
Repository: mne-tools/mne-python

Issue:
Add __eq__ and __ne__ to Projection
### Describe the new feature or enhancement

Add the `==` and `!=` method to the `Projection` class defined [here](https://github.com/mne-tools/mne-python/blob/aef49669fe1bdf19221e03e85cf961671508e0bb/mne/io/proj.py#L24-L103).

For now, comparing projectors raises:

```
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
```


Discussion:
Sure, seems like an easy one when someone is motivated!

Resolving patch:
diff --git a/mne/io/proj.py b/mne/io/proj.py
--- a/mne/io/proj.py
+++ b/mne/io/proj.py
@@ -18,7 +18,8 @@
                     write_float_matrix, end_block, start_block)
 from ..defaults import (_INTERPOLATION_DEFAULT, _BORDER_DEFAULT,
                         _EXTRAPOLATE_DEFAULT)
-from ..utils import logger, verbose, warn, fill_doc, _validate_type
+from ..utils import (logger, verbose, warn, fill_doc, _validate_type,
+                     object_diff)
 
 
 class Projection(dict):
@@ -70,6 +71,14 @@ def __deepcopy__(self, memodict):
                 result[k] = v  # kind, active, desc, explained_var immutable
         return result
 
+    def __eq__(self, other):
+        """Equality == method."""
+        return True if len(object_diff(self, other)) == 0 else False
+
+    def __ne__(self, other):
+        """Different != method."""
+        return not self.__eq__(other)
+
     @fill_doc
     def plot_topomap(self, info, cmap=None, sensors=True,
                      colorbar=False, res=64, size=1, show=True,


Test patch:
diff --git a/mne/io/tests/test_proj.py b/mne/io/tests/test_proj.py
new file mode 100644
--- /dev/null
+++ b/mne/io/tests/test_proj.py
@@ -0,0 +1,24 @@
+from copy import deepcopy
+
+from mne.datasets.testing import data_path, requires_testing_data
+from mne.io import read_raw_fif
+
+directory = data_path(download=False) / "MEG" / "sample"
+fname = directory / "sample_audvis_trunc_raw.fif"
+
+
+@requires_testing_data
+def test_eq_ne():
+    """Test == and != between projectors."""
+    raw = read_raw_fif(fname, preload=False)
+
+    pca1 = deepcopy(raw.info["projs"][0])
+    pca2 = deepcopy(raw.info["projs"][1])
+    car = deepcopy(raw.info["projs"][3])
+
+    assert pca1 != pca2
+    assert pca1 != car
+    assert pca2 != car
+    assert pca1 == raw.info["projs"][0]
+    assert pca2 == raw.info["projs"][1]
+    assert car == raw.info["projs"][3]
Repository: mne-tools/mne-python

Issue:
getting chpi information crashes when no hpi information are available
### Describe the new feature or enhancement

The new ( #11122 ) method `mne.chpi.get_active_chpi` calls `mne.chpi. get_chpi_info` to determine the number of active hpi coils. In case no information on hpis are available, the default behavior of `get_chpi_info` is raising an error. This is not ideal for all situations (see for example  [mne-bids #1067](https://github.com/mne-tools/mne-bids/pull/1067). Therefore it would be nice to be able to modify that behavior. 





### Describe your proposed implementation

I would say adding the `on_missing` keyword to  `mne.chpi.get_active_chpi` and passing it on to `mne.chpi. get_chpi_info` should be easy and sufficient. 

### Describe possible alternatives

alternatively, one could set the default behavior of the on_missing field to something less restrictive than raise. Not sure whether that would cause problems elsewhere.


Discussion:

Resolving patch:
diff --git a/mne/chpi.py b/mne/chpi.py
--- a/mne/chpi.py
+++ b/mne/chpi.py
@@ -1386,13 +1386,16 @@ def _compute_good_distances(hpi_coil_dists, new_pos, dist_limit=0.005):
     return use_mask, these_dists
 
 
-def get_active_chpi(raw):
+@verbose
+def get_active_chpi(raw, *, on_missing='raise', verbose=None):
     """Determine how many HPI coils were active for a time point.
 
     Parameters
     ----------
     raw : instance of Raw
         Raw data with cHPI information.
+    %(on_missing_chpi)s
+    %(verbose)s
 
     Returns
     -------
@@ -1412,7 +1415,7 @@ def get_active_chpi(raw):
                                    ' is not implemented for other systems'
                                    ' than neuromag.'))
     # extract hpi info
-    chpi_info = get_chpi_info(raw.info)
+    chpi_info = get_chpi_info(raw.info, on_missing=on_missing, verbose=verbose)
 
     # extract hpi time series and infer which one was on
     chpi_ts = raw[chpi_info[1]][0].astype(int)


Test patch:
Repository: mne-tools/mne-python

Issue:
Incorrect raw duration displayed in HTML repr

_Originally posted by @hoechenberger in https://github.com/mne-tools/mne-python/issues/11196#issuecomment-1257090412_

btw there's a bug in the HTML repr that is not present in the text repr:

<img width="492" alt="Screen Shot 2022-09-25 at 02 29 36" src="https://user-images.githubusercontent.com/2046265/192123297-c30445cf-061f-4a52-b497-0465ecd136d7.png">

```python
import numpy as np
import mne

n_seconds = 1
sfreq = 100
ch_names = ['1']
data = np.ones((len(ch_names), sfreq*n_seconds))
info = mne.create_info(ch_names=ch_names, sfreq=sfreq)
raw = mne.io.RawArray(data, info)
```


Discussion:
I think that's another argument for having just one repr. I don't like treating stuff separately for notebooks. At the very least, the HTML repr should take the normal repr and extend it when it makes sense, but duplicating what's already in the text repr is prone to such bugs.
Yes but I think we've agreed that your point is valid many times before, no? It's just someone has to go ahead and start implementing it already. 🤷‍♀️ 
I just wanted to make sure 😄.
The floor is yours Clemens 


Resolving patch:
diff --git a/mne/io/base.py b/mne/io/base.py
--- a/mne/io/base.py
+++ b/mne/io/base.py
@@ -1745,9 +1745,15 @@ def _repr_html_(self, caption=None):
         basenames = [
             os.path.basename(f) for f in self._filenames if f is not None
         ]
-        m, s = divmod(self._last_time - self.first_time, 60)
-        h, m = divmod(m, 60)
-        duration = f'{int(h):02d}:{int(m):02d}:{int(s):02d}'
+
+        # https://stackoverflow.com/a/10981895
+        duration = timedelta(seconds=self.times[-1])
+        hours, remainder = divmod(duration.seconds, 3600)
+        minutes, seconds = divmod(remainder, 60)
+        seconds += duration.microseconds / 1e6
+        seconds = np.ceil(seconds)  # always take full seconds
+
+        duration = f'{int(hours):02d}:{int(minutes):02d}:{int(seconds):02d}'
         raw_template = repr_templates_env.get_template('raw.html.jinja')
         return raw_template.render(
             info_repr=self.info._repr_html_(caption=caption),


Test patch:
Repository: mne-tools/mne-python

Issue:
MAINT: Use cirrus-ci for arm64 builds
https://cirrus-ci.org/guide/quick-start/
https://github.com/scipy/scipy/pull/17029

We could do linux_aarch64 if we wanted, too. But I haven't seen any demand for that yet so I'm thinking just arm64 for now


Discussion:

Resolving patch:
diff --git a/mne/conftest.py b/mne/conftest.py
--- a/mne/conftest.py
+++ b/mne/conftest.py
@@ -919,5 +919,8 @@ def run(nbexec=nbexec, code=code):
 def nirx_snirf(request):
     """Return a (raw_nirx, raw_snirf) matched pair."""
     pytest.importorskip('h5py')
+    skipper = request.param[2].marks[0].mark
+    if skipper.args[0]:  # will skip
+        pytest.skip(skipper.kwargs['reason'])
     return (read_raw_nirx(request.param[0], preload=True),
             read_raw_snirf(request.param[1], preload=True))


Test patch:
diff --git a/mne/gui/tests/test_core.py b/mne/gui/tests/test_core.py
--- a/mne/gui/tests/test_core.py
+++ b/mne/gui/tests/test_core.py
@@ -43,6 +43,7 @@ def fun(*args, **kwargs):
 
 
 @requires_nibabel()
+@testing.requires_testing_data
 def test_slice_browser_io(_slice_browser):
     """Test the input/output of the slice browser GUI."""
     import nibabel as nib
diff --git a/mne/viz/tests/test_3d.py b/mne/viz/tests/test_3d.py
--- a/mne/viz/tests/test_3d.py
+++ b/mne/viz/tests/test_3d.py
@@ -168,9 +168,10 @@ def _assert_n_actors(fig, renderer, n_actors):
 
 
 @pytest.mark.slowtest  # Slow on Azure
+@testing.requires_testing_data  # all use trans + head surf
 @pytest.mark.parametrize('system', [
     'Neuromag',
-    pytest.param('CTF', marks=testing._pytest_mark()),
+    'CTF',
     'BTi',
     'KIT',
 ])
Repository: mne-tools/mne-python

Issue:
Missing `verbose` argument in mne.channels.channels.combine_channels
### Description of the problem

The `combine_channels` function is missing the verbose argument. By default, it shows all logger information without the option to turn it off. 

### Steps to reproduce

```Python
from mne.io import concatenate_raws, read_raw_edf
from mne.datasets import eegbci
from mne.channels.channels import combine_channels

raw_fnames = eegbci.load_data(1, 1)
raws = [read_raw_edf(f, preload=True) for f in raw_fnames]
raw = concatenate_raws(raws)

res = combine_channels(raw, dict(Left=[1, 2, 3, 4], Right=[5, 6, 7, 8]))
res
```


### Link to data

_No response_

### Expected results

Expected to see only the information from a variable of type mne.io.array.array.RawArray.

### Actual results

Actual results include the snippet below and the information from a variable of type mne.io.array.array.RawArray

```
Creating RawArray with float64 data, n_channels=2, n_times=9760
    Range : 0 ... 9759 =      0.000 ...    60.994 secs
Ready.
```

### Additional information

Platform:         Windows-10-10.0.22000-SP0
Python:           3.10.6 | packaged by conda-forge | (main, Oct  7 2022, 20:14:50) [MSC v.1916 64 bit (AMD64)]
Executable:       C:\Users\dinara\miniconda3\envs\X\python.exe
CPU:              Intel64 Family 6 Model 158 Stepping 10, GenuineIntel: 12 cores
Memory:           7.8 GB

mne:              1.3.dev0
numpy:            1.23.4 {OpenBLAS 0.3.20 with 2 threads}
scipy:            1.9.2
matplotlib:       3.6.1 {backend=module://matplotlib_inline.backend_inline}

sklearn:          1.1.2
numba:            0.56.2
nibabel:          4.0.2
nilearn:          0.9.2
dipy:             1.5.0
openmeeg:         2.5.5
cupy:             Not found
pandas:           1.5.0
pyvista:          0.36.1 {OpenGL 4.5.0 NVIDIA 516.93 via NVIDIA GeForce GTX 1650 with Max-Q Design/PCIe/SSE2}
pyvistaqt:        0.9.0
ipyvtklink:       0.2.2
vtk:              9.2.2
qtpy:             2.2.1 {PySide6=6.3.2}
ipympl:           Not found
pyqtgraph:        0.13.1
pooch:            v1.6.0

mne_bids:         Not found
mne_nirs:         Not found
mne_features:     Not found
mne_qt_browser:   0.4.0
mne_connectivity: Not found
mne_icalabel:     Not found


Discussion:

Resolving patch:
diff --git a/mne/channels/channels.py b/mne/channels/channels.py
--- a/mne/channels/channels.py
+++ b/mne/channels/channels.py
@@ -1876,8 +1876,9 @@ def make_1020_channel_selections(info, midline="z"):
     return selections
 
 
+@verbose
 def combine_channels(inst, groups, method='mean', keep_stim=False,
-                     drop_bad=False):
+                     drop_bad=False, verbose=None):
     """Combine channels based on specified channel grouping.
 
     Parameters
@@ -1915,6 +1916,7 @@ def combine_channels(inst, groups, method='mean', keep_stim=False,
     drop_bad : bool
         If ``True``, drop channels marked as bad before combining. Defaults to
         ``False``.
+    %(verbose)s
 
     Returns
     -------


Test patch:
Repository: mne-tools/mne-python

Issue:
Check types tmin tmax for crop (TimeMixin)
### Describe the new feature or enhancement

Version: 1.2.1

Mistakenly passing a tuple of floats to `Epochs.crop` as `tmin` positional argument raised:

```
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
```
I missed that I was passing the complete time interval to `tmin`, and not `tmin` alone. Also, I'm familiar with the previous error arising from incompatible numpy versions, so I troubleshooted in that direction instead. Passing floats to `tmin` and `tmax` solved the issue, however, expected to raise:

```
TypeError: tmin must be an instance of int, float, None, got <class 'tuple'> instead.
```

### Describe your proposed implementation

Use `_validate_type` in `TimeMixin`. I'll follow with PR.

### Describe possible alternatives

Proposed solution should work, but maybe revise more robust checking of time intervals?

### Additional context

_No response_


Discussion:

Resolving patch:
diff --git a/mne/utils/mixin.py b/mne/utils/mixin.py
--- a/mne/utils/mixin.py
+++ b/mne/utils/mixin.py
@@ -495,6 +495,14 @@ def crop(self, tmin=None, tmax=None, include_tmax=True, verbose=None):
         -----
         %(notes_tmax_included_by_default)s
         """
+        t_vars = dict(tmin=tmin, tmax=tmax)
+        for name, t_var in t_vars.items():
+            _validate_type(
+                t_var,
+                types=("numeric", None),
+                item_name=name,
+            )
+
         if tmin is None:
             tmin = self.tmin
         elif tmin < self.tmin:


Test patch: