From dc9872c0276e63efa1c47aa748167812aa680e29 Mon Sep 17 00:00:00 2001 From: Eric Larson Date: Mon, 4 Dec 2023 13:39:45 -0500 Subject: [PATCH 1/4] MAINT: Post-release deprecations --- mne/_fiff/__init__.py | 19 -- mne/gui/_gui.py | 52 +---- mne/io/constants.py | 9 - mne/io/meas_info.py | 11 -- mne/io/pick.py | 9 - mne/io/proj.py | 11 -- mne/io/reference.py | 11 -- mne/io/tag.py | 11 -- mne/io/utils.py | 11 -- mne/io/write.py | 11 -- mne/preprocessing/__init__.pyi | 2 - mne/preprocessing/maxfilter.py | 230 ---------------------- mne/source_space/_source_space.py | 3 - mne/tests/test_bem.py | 2 +- mne/time_frequency/spectrum.py | 5 +- mne/time_frequency/tests/test_spectrum.py | 3 +- mne/viz/_brain/_brain.py | 10 - mne/viz/backends/_abstract.py | 9 - mne/viz/backends/_pyvista.py | 21 -- mne/viz/backends/renderer.py | 5 - mne/viz/topomap.py | 3 +- mne/viz/utils.py | 10 +- 22 files changed, 13 insertions(+), 445 deletions(-) delete mode 100644 mne/io/meas_info.py delete mode 100644 mne/io/proj.py delete mode 100644 mne/io/reference.py delete mode 100644 mne/io/tag.py delete mode 100644 mne/io/utils.py delete mode 100644 mne/io/write.py delete mode 100644 mne/preprocessing/maxfilter.py diff --git a/mne/_fiff/__init__.py b/mne/_fiff/__init__.py index 6402d78b325..877068fe54d 100644 --- a/mne/_fiff/__init__.py +++ b/mne/_fiff/__init__.py @@ -7,22 +7,3 @@ # All imports should be done directly to submodules, so we don't import # anything here or use lazy_loader. - -# This warn import (made private as _warn) is just for the temporary -# _io_dep_getattr and can be removed in 1.6 along with _dep_msg and _io_dep_getattr. -from ..utils import warn as _warn - - -_dep_msg = ( - "is deprecated will be removed in 1.6, use documented public API instead. " - "If no appropriate public API exists, please open an issue on GitHub." -) - - -def _io_dep_getattr(name, mod): - import importlib - - fiff_mod = importlib.import_module(f"mne._fiff.{mod}") - obj = getattr(fiff_mod, name) - _warn(f"mne.io.{mod}.{name} {_dep_msg}", FutureWarning) - return obj diff --git a/mne/gui/_gui.py b/mne/gui/_gui.py index 3c437f9d266..e0f2556afd0 100644 --- a/mne/gui/_gui.py +++ b/mne/gui/_gui.py @@ -3,31 +3,24 @@ # License: BSD-3-Clause # Copyright the MNE-Python contributors. -from ..utils import get_config, verbose, warn +from ..utils import get_config, verbose @verbose def coregistration( *, - tabbed=None, - split=None, width=None, + height=None, inst=None, subject=None, subjects_dir=None, - guess_mri_subject=None, - height=None, head_opacity=None, head_high_res=None, trans=None, - scrollable=None, orient_to_surface=None, scale_by_distance=None, mark_inside=None, interaction=None, - scale=None, - advanced_rendering=None, - head_inside=None, fullscreen=None, show=True, block=False, @@ -45,29 +38,20 @@ def coregistration( Parameters ---------- - tabbed : bool - Combine the data source panel and the coregistration panel into a - single panel with tabs. - split : bool - Split the main panels with a movable splitter (good for QT4 but - unnecessary for wx backend). width : int | None Specify the width for window (in logical pixels). Default is None, which uses ``MNE_COREG_WINDOW_WIDTH`` config value (which defaults to 800). + height : int | None + Specify a height for window (in logical pixels). + Default is None, which uses ``MNE_COREG_WINDOW_WIDTH`` config value + (which defaults to 400). inst : None | str Path to an instance file containing the digitizer data. Compatible for Raw, Epochs, and Evoked files. subject : None | str Name of the mri subject. %(subjects_dir)s - guess_mri_subject : bool - When selecting a new head shape file, guess the subject's name based - on the filename and change the MRI subject accordingly (default True). - height : int | None - Specify a height for window (in logical pixels). - Default is None, which uses ``MNE_COREG_WINDOW_WIDTH`` config value - (which defaults to 400). head_opacity : float | None The opacity of the head surface in the range [0., 1.]. Default is None, which uses ``MNE_COREG_HEAD_OPACITY`` config value @@ -78,8 +62,6 @@ def coregistration( (which defaults to True). trans : path-like | None The transform file to use. - scrollable : bool - Make the coregistration panel vertically scrollable (default True). orient_to_surface : bool | None If True (default), orient EEG electrode and head shape points to the head surface. @@ -143,28 +125,6 @@ def coregistration( .. youtube:: ALV5qqMHLlQ """ - unsupported_params = { - "tabbed": tabbed, - "split": split, - "scrollable": scrollable, - "head_inside": head_inside, - "guess_mri_subject": guess_mri_subject, - "scale": scale, - "advanced_rendering": advanced_rendering, - } - for key, val in unsupported_params.items(): - if isinstance(val, tuple): - to_raise = val[0] != val[1] - else: - to_raise = val is not None - if to_raise: - warn( - f"The parameter {key} is deprecated and will be removed in 1.7, do " - "not pass a value for it", - FutureWarning, - ) - del tabbed, split, scrollable, head_inside, guess_mri_subject, scale - del advanced_rendering config = get_config() if head_high_res is None: head_high_res = config.get("MNE_COREG_HEAD_HIGH_RES", "true") == "true" diff --git a/mne/io/constants.py b/mne/io/constants.py index 61db99600f2..fdac1a856c2 100644 --- a/mne/io/constants.py +++ b/mne/io/constants.py @@ -3,15 +3,6 @@ # License: BSD-3-Clause # Copyright the MNE-Python contributors. -from .._fiff import _io_dep_getattr from .._fiff.constants import FIFF __all__ = ["FIFF"] - - -def __getattr__(name): - try: - return globals()[name] - except KeyError: - pass - return _io_dep_getattr(name, "constants") diff --git a/mne/io/meas_info.py b/mne/io/meas_info.py deleted file mode 100644 index f971fff18d4..00000000000 --- a/mne/io/meas_info.py +++ /dev/null @@ -1,11 +0,0 @@ -# Author: Eric Larson -# -# License: BSD-3-Clause -# Copyright the MNE-Python contributors. - - -from .._fiff import _io_dep_getattr - - -def __getattr__(name): - return _io_dep_getattr(name, "meas_info") diff --git a/mne/io/pick.py b/mne/io/pick.py index f7c77b1af14..4ae1d25b3c5 100644 --- a/mne/io/pick.py +++ b/mne/io/pick.py @@ -4,7 +4,6 @@ # Copyright the MNE-Python contributors. -from .._fiff import _io_dep_getattr from .._fiff.pick import ( _DATA_CH_TYPES_ORDER_DEFAULT, _DATA_CH_TYPES_SPLIT, @@ -18,11 +17,3 @@ "_DATA_CH_TYPES_ORDER_DEFAULT", "_DATA_CH_TYPES_SPLIT", ] - - -def __getattr__(name): - try: - return globals()[name] - except KeyError: - pass - return _io_dep_getattr(name, "pick") diff --git a/mne/io/proj.py b/mne/io/proj.py deleted file mode 100644 index 98445f1ce7e..00000000000 --- a/mne/io/proj.py +++ /dev/null @@ -1,11 +0,0 @@ -# Author: Eric Larson -# -# License: BSD-3-Clause -# Copyright the MNE-Python contributors. - - -from .._fiff import _io_dep_getattr - - -def __getattr__(name): - return _io_dep_getattr(name, "proj") diff --git a/mne/io/reference.py b/mne/io/reference.py deleted file mode 100644 index 850d6bd7294..00000000000 --- a/mne/io/reference.py +++ /dev/null @@ -1,11 +0,0 @@ -# Author: Eric Larson -# -# License: BSD-3-Clause -# Copyright the MNE-Python contributors. - - -from .._fiff import _io_dep_getattr - - -def __getattr__(name): - return _io_dep_getattr(name, "reference") diff --git a/mne/io/tag.py b/mne/io/tag.py deleted file mode 100644 index 41dc15fd40d..00000000000 --- a/mne/io/tag.py +++ /dev/null @@ -1,11 +0,0 @@ -# Author: Eric Larson -# -# License: BSD-3-Clause -# Copyright the MNE-Python contributors. - - -from .._fiff import _io_dep_getattr - - -def __getattr__(name): - return _io_dep_getattr(name, "tag") diff --git a/mne/io/utils.py b/mne/io/utils.py deleted file mode 100644 index 9460ceed55e..00000000000 --- a/mne/io/utils.py +++ /dev/null @@ -1,11 +0,0 @@ -# Author: Eric Larson -# -# License: BSD-3-Clause -# Copyright the MNE-Python contributors. - - -from .._fiff import _io_dep_getattr - - -def __getattr__(name): - return _io_dep_getattr(name, "utils") diff --git a/mne/io/write.py b/mne/io/write.py deleted file mode 100644 index 12c0ae00ca0..00000000000 --- a/mne/io/write.py +++ /dev/null @@ -1,11 +0,0 @@ -# Author: Eric Larson -# -# License: BSD-3-Clause -# Copyright the MNE-Python contributors. - - -from .._fiff import _io_dep_getattr - - -def __getattr__(name): - return _io_dep_getattr(name, "write") diff --git a/mne/preprocessing/__init__.pyi b/mne/preprocessing/__init__.pyi index 7d0741ab30a..0ea66345687 100644 --- a/mne/preprocessing/__init__.pyi +++ b/mne/preprocessing/__init__.pyi @@ -7,7 +7,6 @@ __all__ = [ "annotate_movement", "annotate_muscle_zscore", "annotate_nan", - "apply_maxfilter", "compute_average_dev_head_t", "compute_bridged_electrodes", "compute_current_source_density", @@ -77,7 +76,6 @@ from .ica import ( ) from .infomax_ import infomax from .interpolate import equalize_bads, interpolate_bridged_electrodes -from .maxfilter import apply_maxfilter from .maxwell import ( compute_maxwell_basis, find_bad_channels_maxwell, diff --git a/mne/preprocessing/maxfilter.py b/mne/preprocessing/maxfilter.py deleted file mode 100644 index 64a48b68cf3..00000000000 --- a/mne/preprocessing/maxfilter.py +++ /dev/null @@ -1,230 +0,0 @@ -# Authors: Alexandre Gramfort -# Matti Hämäläinen -# Martin Luessi -# -# License: BSD-3-Clause -# Copyright the MNE-Python contributors. - -import os - -from ..bem import fit_sphere_to_headshape -from ..io import read_raw_fif -from ..utils import deprecated, logger, verbose, warn - - -def _mxwarn(msg): - """Warn about a bug.""" - warn( - "Possible MaxFilter bug: %s, more info: " - "http://imaging.mrc-cbu.cam.ac.uk/meg/maxbugs" % msg - ) - - -@deprecated( - "apply_maxfilter will be removed in 1.7, use mne.preprocessing.maxwell_filter or " - "the MEGIN command-line utility maxfilter and mne.bem.fit_sphere_to_headshape " - "instead." -) -@verbose -def apply_maxfilter( - in_fname, - out_fname, - origin=None, - frame="device", - bad=None, - autobad="off", - skip=None, - force=False, - st=False, - st_buflen=16.0, - st_corr=0.96, - mv_trans=None, - mv_comp=False, - mv_headpos=False, - mv_hp=None, - mv_hpistep=None, - mv_hpisubt=None, - mv_hpicons=True, - linefreq=None, - cal=None, - ctc=None, - mx_args="", - overwrite=True, - verbose=None, -): - """Apply NeuroMag MaxFilter to raw data. - - Needs Maxfilter license, maxfilter has to be in PATH. - - Parameters - ---------- - in_fname : path-like - Input file name. - out_fname : path-like - Output file name. - origin : array-like or str - Head origin in mm. If None it will be estimated from headshape points. - frame : ``'device'`` | ``'head'`` - Coordinate frame for head center. - bad : str, list (or None) - List of static bad channels. Can be a list with channel names, or a - string with channels (names or logical channel numbers). - autobad : str ('on', 'off', 'n') - Sets automated bad channel detection on or off. - skip : str or a list of float-tuples (or None) - Skips raw data sequences, time intervals pairs in s, - e.g.: 0 30 120 150. - force : bool - Ignore program warnings. - st : bool - Apply the time-domain MaxST extension. - st_buflen : float - MaxSt buffer length in s (disabled if st is False). - st_corr : float - MaxSt subspace correlation limit (disabled if st is False). - mv_trans : str (filename or 'default') (or None) - Transforms the data into the coil definitions of in_fname, or into the - default frame (None: don't use option). - mv_comp : bool (or 'inter') - Estimates and compensates head movements in continuous raw data. - mv_headpos : bool - Estimates and stores head position parameters, but does not compensate - movements (disabled if mv_comp is False). - mv_hp : str (or None) - Stores head position data in an ascii file - (disabled if mv_comp is False). - mv_hpistep : float (or None) - Sets head position update interval in ms (disabled if mv_comp is - False). - mv_hpisubt : str ('amp', 'base', 'off') (or None) - Subtracts hpi signals: sine amplitudes, amp + baseline, or switch off - (disabled if mv_comp is False). - mv_hpicons : bool - Check initial consistency isotrak vs hpifit - (disabled if mv_comp is False). - linefreq : int (50, 60) (or None) - Sets the basic line interference frequency (50 or 60 Hz) - (None: do not use line filter). - cal : str - Path to calibration file. - ctc : str - Path to Cross-talk compensation file. - mx_args : str - Additional command line arguments to pass to MaxFilter. - %(overwrite)s - %(verbose)s - - Returns - ------- - origin: str - Head origin in selected coordinate frame. - """ - # check for possible maxfilter bugs - if mv_trans is not None and mv_comp: - _mxwarn("Don't use '-trans' with head-movement compensation " "'-movecomp'") - - if autobad != "off" and (mv_headpos or mv_comp): - _mxwarn( - "Don't use '-autobad' with head-position estimation " - "'-headpos' or movement compensation '-movecomp'" - ) - - if st and autobad != "off": - _mxwarn("Don't use '-autobad' with '-st' option") - - # determine the head origin if necessary - if origin is None: - logger.info("Estimating head origin from headshape points..") - raw = read_raw_fif(in_fname) - r, o_head, o_dev = fit_sphere_to_headshape(raw.info, units="mm") - raw.close() - logger.info("[done]") - if frame == "head": - origin = o_head - elif frame == "device": - origin = o_dev - else: - raise RuntimeError("invalid frame for origin") - - if not isinstance(origin, str): - origin = "%0.1f %0.1f %0.1f" % (origin[0], origin[1], origin[2]) - - # format command - cmd = "maxfilter -f %s -o %s -frame %s -origin %s " % ( - in_fname, - out_fname, - frame, - origin, - ) - - if bad is not None: - # format the channels - if not isinstance(bad, list): - bad = bad.split() - bad = map(str, bad) - bad_logic = [ch[3:] if ch.startswith("MEG") else ch for ch in bad] - bad_str = " ".join(bad_logic) - - cmd += "-bad %s " % bad_str - - cmd += "-autobad %s " % autobad - - if skip is not None: - if isinstance(skip, list): - skip = " ".join(["%0.3f %0.3f" % (s[0], s[1]) for s in skip]) - cmd += "-skip %s " % skip - - if force: - cmd += "-force " - - if st: - cmd += "-st " - cmd += " %d " % st_buflen - cmd += "-corr %0.4f " % st_corr - - if mv_trans is not None: - cmd += "-trans %s " % mv_trans - - if mv_comp: - cmd += "-movecomp " - if mv_comp == "inter": - cmd += " inter " - - if mv_headpos: - cmd += "-headpos " - - if mv_hp is not None: - cmd += "-hp %s " % mv_hp - - if mv_hpisubt is not None: - cmd += "hpisubt %s " % mv_hpisubt - - if mv_hpicons: - cmd += "-hpicons " - - if linefreq is not None: - cmd += "-linefreq %d " % linefreq - - if cal is not None: - cmd += "-cal %s " % cal - - if ctc is not None: - cmd += "-ctc %s " % ctc - - cmd += mx_args - - if overwrite and os.path.exists(out_fname): - os.remove(out_fname) - - logger.info("Running MaxFilter: %s " % cmd) - if os.getenv("_MNE_MAXFILTER_TEST", "") != "true": # fake maxfilter - # OK to `nosec` because it's deprecated / will be removed - st = os.system(cmd) # nosec B605 - else: - print(cmd) # we can check the output - st = 0 - if st != 0: - raise RuntimeError("MaxFilter returned non-zero exit status %d" % st) - logger.info("[done]") - - return origin diff --git a/mne/source_space/_source_space.py b/mne/source_space/_source_space.py index 3cfedb9d7a1..e1d8611354c 100644 --- a/mne/source_space/_source_space.py +++ b/mne/source_space/_source_space.py @@ -35,13 +35,10 @@ write_int_matrix, write_string, ) - -# Remove get_mni_fiducials in 1.6 (deprecated) from .._freesurfer import ( _check_mri, _get_atlas_values, _get_mri_info_data, - get_mni_fiducials, # noqa: F401 get_volume_labels_from_aseg, read_freesurfer_lut, ) diff --git a/mne/tests/test_bem.py b/mne/tests/test_bem.py index 0dd682606f6..498e1985f8e 100644 --- a/mne/tests/test_bem.py +++ b/mne/tests/test_bem.py @@ -37,11 +37,11 @@ _ico_downsample, _order_surfaces, distance_to_bem, + fit_sphere_to_headshape, make_scalp_surfaces, ) from mne.datasets import testing from mne.io import read_info -from mne.preprocessing.maxfilter import fit_sphere_to_headshape from mne.surface import _get_ico_surface, read_surface from mne.transforms import translation from mne.utils import catch_logging, check_version diff --git a/mne/time_frequency/spectrum.py b/mne/time_frequency/spectrum.py index aa459124347..ef8817e4828 100644 --- a/mne/time_frequency/spectrum.py +++ b/mne/time_frequency/spectrum.py @@ -322,12 +322,11 @@ def __init__( # don't allow complex output psd_funcs = dict(welch=psd_array_welch, multitaper=psd_array_multitaper) if method_kw.get("output", "") == "complex": - warn( + raise RuntimeError( f"Complex output support in {type(self).__name__} objects is " - "deprecated and will be removed in version 1.7. If you need complex " + "not supported. If you need complex " f"output please use mne.time_frequency.{psd_funcs[method].__name__}() " "instead.", - FutureWarning, ) # triage method and kwargs. partial() doesn't check validity of kwargs, # so we do it manually to save compute time if any are invalid. diff --git a/mne/time_frequency/tests/test_spectrum.py b/mne/time_frequency/tests/test_spectrum.py index 58e3309bcc8..df339ca5fad 100644 --- a/mne/time_frequency/tests/test_spectrum.py +++ b/mne/time_frequency/tests/test_spectrum.py @@ -22,7 +22,8 @@ def test_compute_psd_errors(raw): raw.compute_psd(foo=None) with pytest.raises(TypeError, match="keyword arguments foo, bar for"): raw.compute_psd(foo=None, bar=None) - with pytest.warns(FutureWarning, match="Complex output support in.*deprecated"): + # TODO: More code to remove here? + with pytest.raises(RuntimeError, match="Complex output support in.*deprecated"): raw.compute_psd(output="complex") diff --git a/mne/viz/_brain/_brain.py b/mne/viz/_brain/_brain.py index 4725e8664aa..69f70786645 100644 --- a/mne/viz/_brain/_brain.py +++ b/mne/viz/_brain/_brain.py @@ -2183,8 +2183,6 @@ def add_label( borders=False, hemi=None, subdir=None, - *, - reset_camera=None, ): """Add an ROI label to the image. @@ -2216,8 +2214,6 @@ def add_label( label directory rather than in the label directory itself (e.g. for ``$SUBJECTS_DIR/$SUBJECT/label/aparc/lh.cuneus.label`` ``brain.add_label('cuneus', subdir='aparc')``). - reset_camera : bool - Deprecated. Use :meth:`show_view` instead. Notes ----- @@ -2324,12 +2320,6 @@ def add_label( keep_idx = np.unique(keep_idx) show[keep_idx] = 1 scalars *= show - if reset_camera is not None: - warn( - "reset_camera is deprecated and will be removed in 1.7, " - "use show_view instead", - FutureWarning, - ) for _, _, v in self._iter_views(hemi): mesh = self._layered_meshes[hemi] mesh.add_overlay( diff --git a/mne/viz/backends/_abstract.py b/mne/viz/backends/_abstract.py index a8e902aa33b..b28468ebc77 100644 --- a/mne/viz/backends/_abstract.py +++ b/mne/viz/backends/_abstract.py @@ -549,8 +549,6 @@ def set_camera( distance=None, focalpoint=None, roll=None, - *, - reset_camera=None, ): """Configure the camera of the scene. @@ -566,16 +564,9 @@ def set_camera( The focal point of the camera: (x, y, z). roll : float The rotation of the camera along its axis. - reset_camera : bool - Deprecated, used ``distance="auto"`` instead. """ pass - @abstractclassmethod - def reset_camera(self): - """Reset the camera properties.""" - pass - @abstractclassmethod def screenshot(self, mode="rgb", filename=None): """Take a screenshot of the scene. diff --git a/mne/viz/backends/_pyvista.py b/mne/viz/backends/_pyvista.py index b8fca4c995a..c1fb06eb8ff 100644 --- a/mne/viz/backends/_pyvista.py +++ b/mne/viz/backends/_pyvista.py @@ -26,8 +26,6 @@ _check_option, _require_version, _validate_type, - copy_base_doc_to_subclass_doc, - deprecated, warn, ) from ._abstract import Figure3D, _AbstractRenderer @@ -195,7 +193,6 @@ def visible(self, state): self.plotter.render() -@copy_base_doc_to_subclass_doc class _PyVistaRenderer(_AbstractRenderer): """Class managing rendering scene. @@ -843,7 +840,6 @@ def set_camera( *, rigid=None, update=True, - reset_camera=None, ): _set_3d_view( self.figure, @@ -852,18 +848,10 @@ def set_camera( distance=distance, focalpoint=focalpoint, roll=roll, - reset_camera=reset_camera, rigid=rigid, update=update, ) - @deprecated( - "reset_camera is deprecated and will be removed in 1.7, use " - "set_camera(distance='auto') instead" - ) - def reset_camera(self): - self.plotter.reset_camera() - def screenshot(self, mode="rgb", filename=None): return _take_3d_screenshot(figure=self.figure, mode=mode, filename=filename) @@ -1190,7 +1178,6 @@ def _set_3d_view( focalpoint=None, distance=None, roll=None, - reset_camera=None, rigid=None, update=True, ): @@ -1201,14 +1188,6 @@ def _set_3d_view( # camera slides along the vector defined from camera position to focal point until # all of the actors can be seen (quoting PyVista's docs) - if reset_camera is not None: - reset_camera = False - warn( - "reset_camera is deprecated and will be removed in 1.7, use " - "distance='auto' instead", - FutureWarning, - ) - # Figure out our current parameters in the transformed space _, phi, theta = _get_user_camera_direction(figure.plotter, rigid) diff --git a/mne/viz/backends/renderer.py b/mne/viz/backends/renderer.py index 1bb396d165c..2e2ab1e7333 100644 --- a/mne/viz/backends/renderer.py +++ b/mne/viz/backends/renderer.py @@ -264,8 +264,6 @@ def set_3d_view( focalpoint=None, distance=None, roll=None, - *, - reset_camera=None, ): """Configure the view of the given scene. @@ -278,8 +276,6 @@ def set_3d_view( %(focalpoint)s %(distance)s %(roll)s - reset_camera : bool - Deprecated, use ``distance="auto"`` instead. """ backend._set_3d_view( figure=figure, @@ -288,7 +284,6 @@ def set_3d_view( focalpoint=focalpoint, distance=distance, roll=roll, - reset_camera=reset_camera, ) diff --git a/mne/viz/topomap.py b/mne/viz/topomap.py index 0c2f6f273b0..f78d035e0ad 100644 --- a/mne/viz/topomap.py +++ b/mne/viz/topomap.py @@ -376,8 +376,7 @@ def plot_projs_topomap( %(info_not_none)s Must be associated with the channels in the projectors. .. versionchanged:: 0.20 - The positional argument ``layout`` was deprecated and replaced - by ``info``. + The positional argument ``layout`` was replaced by ``info``. %(sensors_topomap)s %(show_names_topomap)s diff --git a/mne/viz/utils.py b/mne/viz/utils.py index 4223bafad6c..15a43916dc4 100644 --- a/mne/viz/utils.py +++ b/mne/viz/utils.py @@ -1784,15 +1784,7 @@ def _get_color_list(annotations=False): from matplotlib import rcParams color_cycle = rcParams.get("axes.prop_cycle") - - if not color_cycle: - # Use deprecated color_cycle to avoid KeyErrors in environments - # with Python 2.7 and Matplotlib < 1.5 - # this will already be a list - colors = rcParams.get("axes.color_cycle") - else: - # we were able to use the prop_cycle. Now just convert to list - colors = color_cycle.by_key()["color"] + colors = color_cycle.by_key()["color"] # If we want annotations, red is reserved ... remove if present. This # checks for the reddish color in MPL dark background style, normal style, From 1cf5549d2db118b0a243273ac8f766cd73217d06 Mon Sep 17 00:00:00 2001 From: Eric Larson Date: Mon, 4 Dec 2023 14:15:37 -0500 Subject: [PATCH 2/4] FIX: Remove more test code --- mne/commands/tests/test_commands.py | 27 --------- mne/gui/_gui.py | 15 ----- mne/io/tests/test_deprecation.py | 30 ---------- mne/time_frequency/tests/test_spectrum.py | 67 +---------------------- 4 files changed, 3 insertions(+), 136 deletions(-) delete mode 100644 mne/io/tests/test_deprecation.py diff --git a/mne/commands/tests/test_commands.py b/mne/commands/tests/test_commands.py index ae5e84cbd58..ea87c717db0 100644 --- a/mne/commands/tests/test_commands.py +++ b/mne/commands/tests/test_commands.py @@ -31,7 +31,6 @@ mne_flash_bem, mne_kit2fiff, mne_make_scalp_surfaces, - mne_maxfilter, mne_prepare_bem_model, mne_report, mne_setup_forward_model, @@ -206,32 +205,6 @@ def test_make_scalp_surfaces(tmp_path, monkeypatch): assert "SUBJECTS_DIR" not in os.environ -def test_maxfilter(): - """Test mne maxfilter.""" - check_usage(mne_maxfilter) - with ArgvSetter( - ( - "-i", - raw_fname, - "--st", - "--movecomp", - "--linefreq", - "60", - "--trans", - raw_fname, - ) - ) as out: - with pytest.warns(RuntimeWarning, match="Don't use"): - os.environ["_MNE_MAXFILTER_TEST"] = "true" - try: - mne_maxfilter.run() - finally: - del os.environ["_MNE_MAXFILTER_TEST"] - out = out.stdout.getvalue() - for check in ("maxfilter", "-trans", "-movecomp"): - assert check in out, check - - @pytest.mark.slowtest @testing.requires_testing_data def test_report(tmp_path): diff --git a/mne/gui/_gui.py b/mne/gui/_gui.py index e0f2556afd0..e522986a60c 100644 --- a/mne/gui/_gui.py +++ b/mne/gui/_gui.py @@ -84,21 +84,6 @@ def coregistration( .. versionchanged:: 1.0 Default interaction mode if ``None`` and no config setting found changed from ``'trackball'`` to ``'terrain'``. - scale : float | None - The scaling for the scene. - - .. versionadded:: 0.16 - advanced_rendering : bool - Use advanced OpenGL rendering techniques (default True). - For some renderers (such as MESA software) this can cause rendering - bugs. - - .. versionadded:: 0.18 - head_inside : bool - If True (default), add opaque inner scalp head surface to help occlude - points behind the head. - - .. versionadded:: 0.23 %(fullscreen)s Default is None, which uses ``MNE_COREG_FULLSCREEN`` config value (which defaults to False). diff --git a/mne/io/tests/test_deprecation.py b/mne/io/tests/test_deprecation.py deleted file mode 100644 index fecf9a78091..00000000000 --- a/mne/io/tests/test_deprecation.py +++ /dev/null @@ -1,30 +0,0 @@ -"""Test deprecation of mne.io private attributes to mne._fiff.""" - -# Author: Eric Larson -# -# License: BSD-3-Clause -# Copyright the MNE-Python contributors. - -import pytest - - -def test_deprecation(): - """Test deprecation of mne.io FIFF stuff.""" - import mne.io - - # Shouldn't warn (backcompat) - mne.io.constants.FIFF - mne.io.pick._picks_to_idx - mne.io.get_channel_type_constants() - - # Should warn - with pytest.warns(FutureWarning, match=r"mne\.io\.pick\.pick_channels is dep"): - from mne.io.pick import pick_channels # noqa: F401 - with pytest.warns(FutureWarning, match=r"mne\.io\.pick\.pick_channels is dep"): - mne.io.pick.pick_channels - with pytest.warns(FutureWarning, match=r"mne\.io\.meas_info\.read_info is dep"): - from mne.io.meas_info import read_info # noqa: F401 - from mne.io import meas_info - - with pytest.warns(FutureWarning, match=r"mne\.io\.meas_info\.read_info is dep"): - meas_info.read_info diff --git a/mne/time_frequency/tests/test_spectrum.py b/mne/time_frequency/tests/test_spectrum.py index df339ca5fad..0de15b4248e 100644 --- a/mne/time_frequency/tests/test_spectrum.py +++ b/mne/time_frequency/tests/test_spectrum.py @@ -5,10 +5,9 @@ import numpy as np import pytest from matplotlib.colors import same_color -from numpy.testing import assert_allclose, assert_array_equal +from numpy.testing import assert_array_equal -from mne import Annotations, create_info, make_fixed_length_epochs -from mne.io import RawArray +from mne import Annotations from mne.time_frequency import read_spectrum from mne.time_frequency.multitaper import _psd_from_mt from mne.time_frequency.spectrum import EpochsSpectrumArray, SpectrumArray @@ -23,7 +22,7 @@ def test_compute_psd_errors(raw): with pytest.raises(TypeError, match="keyword arguments foo, bar for"): raw.compute_psd(foo=None, bar=None) # TODO: More code to remove here? - with pytest.raises(RuntimeError, match="Complex output support in.*deprecated"): + with pytest.raises(RuntimeError, match="Complex output support in.*not supported"): raw.compute_psd(output="complex") @@ -218,14 +217,11 @@ def _agg_helper(df, weights, group_cols): return Series(_df) -@pytest.mark.filterwarnings("ignore:Complex output support.*:FutureWarning") @pytest.mark.parametrize("long_format", (False, True)) @pytest.mark.parametrize( "method, output", [ - ("welch", "complex"), ("welch", "power"), - ("multitaper", "complex"), ], ) def test_unaggregated_spectrum_to_data_frame(raw, long_format, method, output): @@ -340,63 +336,6 @@ def test_spectrum_proj(inst, request): assert has_proj == no_proj -@pytest.mark.filterwarnings("ignore:Complex output support.*:FutureWarning") -@pytest.mark.parametrize( - "method, average", - [ - ("welch", False), - ("welch", "mean"), - ("multitaper", False), - ], -) -def test_spectrum_complex(method, average): - """Test output='complex' support.""" - sfreq = 100 - n = 10 * sfreq - freq = 3.0 - phase = np.pi / 4 # should be recoverable - data = np.cos(2 * np.pi * freq * np.arange(n) / sfreq + phase)[np.newaxis] - raw = RawArray(data, create_info(1, sfreq, "eeg")) - epochs = make_fixed_length_epochs(raw, duration=2.0, preload=True) - assert len(epochs) == 5 - assert len(epochs.times) == 2 * sfreq - kwargs = dict(output="complex", method=method) - if method == "welch": - kwargs["n_fft"] = sfreq - want_dims = ("epoch", "channel", "freq") - want_shape = (5, 1, sfreq // 2 + 1) - if not average: - want_dims = want_dims + ("segment",) - want_shape = want_shape + (2,) - kwargs["average"] = average - else: - assert method == "multitaper" - assert not average - want_dims = ("epoch", "channel", "taper", "freq") - want_shape = (5, 1, 7, sfreq + 1) - spectrum = epochs.compute_psd(**kwargs) - idx = np.argmin(np.abs(spectrum.freqs - freq)) - assert spectrum.freqs[idx] == freq - assert spectrum._dims == want_dims - assert spectrum.shape == want_shape - data = spectrum.get_data() - assert data.dtype == np.complex128 - coef = spectrum.get_data(fmin=freq, fmax=freq).mean(0) - if method == "multitaper": - coef = coef[..., 0, :] # first taper - elif not average: - coef = coef.mean(-1) # over segments - coef = coef.item() - assert_allclose(np.angle(coef), phase, rtol=1e-4) - # Now test that it warns appropriately - epochs._data[0, 0, :] = 0 # actually zero for one epoch and ch - with pytest.warns(UserWarning, match="Zero value.*channel 0"): - epochs.compute_psd(**kwargs) - # But not if we mark that channel as bad - epochs.info["bads"] = epochs.ch_names[:1] - epochs.compute_psd(**kwargs) - - def test_spectrum_kwarg_triaging(raw): """Test kwarg triaging in legacy plot_psd() method.""" import matplotlib.pyplot as plt From 3144a6d1142dced7d3d31024044e5b477021de5e Mon Sep 17 00:00:00 2001 From: Daniel McCloy Date: Sun, 10 Sep 2023 16:01:41 -0500 Subject: [PATCH 3/4] remove complex support --- mne/time_frequency/spectrum.py | 47 +++++------------------ mne/time_frequency/tests/test_spectrum.py | 29 +++----------- 2 files changed, 15 insertions(+), 61 deletions(-) diff --git a/mne/time_frequency/spectrum.py b/mne/time_frequency/spectrum.py index ef8817e4828..2b31ca46340 100644 --- a/mne/time_frequency/spectrum.py +++ b/mne/time_frequency/spectrum.py @@ -322,11 +322,9 @@ def __init__( # don't allow complex output psd_funcs = dict(welch=psd_array_welch, multitaper=psd_array_multitaper) if method_kw.get("output", "") == "complex": - raise RuntimeError( - f"Complex output support in {type(self).__name__} objects is " - "not supported. If you need complex " - f"output please use mne.time_frequency.{psd_funcs[method].__name__}() " - "instead.", + raise ValueError( + f"Complex output is not supported in {type(self).__name__} objects. " + f"Please use mne.time_frequency.{psd_funcs[method].__name__}() instead." ) # triage method and kwargs. partial() doesn't check validity of kwargs, # so we do it manually to save compute time if any are invalid. @@ -367,8 +365,6 @@ def __init__( ) if method_kw.get("average", "") in (None, False): self._dims += ("segment",) - if self._returns_complex_tapers(**method_kw): - self._dims = self._dims[:-1] + ("taper",) + self._dims[-1:] # record data type (for repr and html_repr) self._data_type = ( "Fourier Coefficients" if "taper" in self._dims else "Power Spectrum" @@ -410,8 +406,6 @@ def __setstate__(self, state): # instance type inst_types = dict(Raw=Raw, Epochs=Epochs, Evoked=Evoked, Array=np.ndarray) self._inst_type = inst_types[state["inst_type_str"]] - if "weights" in state and state["weights"] is not None: - self._mt_weights = state["weights"] def __repr__(self): """Build string representation of the Spectrum object.""" @@ -455,23 +449,14 @@ def _check_values(self): s = _pl(bad_value.sum()) warn(f'Zero value in spectrum for channel{s} {", ".join(chs)}', UserWarning) - def _returns_complex_tapers(self, **method_kw): - return method_kw.get("output", "") == "complex" and self.method == "multitaper" - def _compute_spectra(self, data, fmin, fmax, n_jobs, method_kw, verbose): # make the spectra result = self._psd_func( data, self.sfreq, fmin=fmin, fmax=fmax, n_jobs=n_jobs, verbose=verbose ) - # assign ._data (handling unaggregated multitaper output) - if self._returns_complex_tapers(**method_kw): - fourier_coefs, freqs, weights = result - self._data = fourier_coefs - self._mt_weights = weights - else: - psds, freqs = result - self._data = psds - # assign properties (._data already assigned above) + # assign ._data ._freqs, ._shape + psds, freqs = result + self._data = psds self._freqs = freqs # this is *expected* shape, it gets asserted later in _check_values() # (and then deleted afterwards) @@ -480,9 +465,6 @@ def _compute_spectra(self, data, fmin, fmax, n_jobs, method_kw, verbose): if method_kw.get("average", "") in (None, False): n_welch_segments = _compute_n_welch_segments(data.shape[-1], method_kw) self._shape += (n_welch_segments,) - # insert n_tapers - if self._returns_complex_tapers(**method_kw): - self._shape = self._shape[:-1] + (self._mt_weights.size,) + self._shape[-1:] # we don't need these anymore, and they make save/load harder del self._picks del self._psd_func @@ -661,7 +643,6 @@ def plot( # Must nest this _mpl_figure import because of the BACKEND global # stuff from ..viz._mpl_figure import _line_figure, _split_picks_by_type - from .multitaper import _psd_from_mt # arg checking ci = _check_ci(ci) @@ -682,12 +663,8 @@ def plot( (picks_list, units_list, scalings_list, titles_list) = _split_picks_by_type( self, picks, units, scalings, titles ) - # handle unaggregated multitaper - if hasattr(self, "_mt_weights"): - logger.info("Aggregating multitaper estimates before plotting...") - _f = partial(_psd_from_mt, weights=self._mt_weights) # handle unaggregated Welch - elif "segment" in self._dims: + if "segment" in self._dims: logger.info("Aggregating Welch estimates (median) before plotting...") seg_axis = self._dims.index("segment") _f = partial(np.nanmedian, axis=seg_axis) @@ -1058,9 +1035,8 @@ def units(self, latex=False): for that channel type. """ units = _handle_default("si_units", None) - power = not hasattr(self, "_mt_weights") return { - ch_type: _format_units_psd(units[ch_type], power=power, latex=latex) + ch_type: _format_units_psd(units[ch_type], power=True, latex=latex) for ch_type in sorted(self.get_channel_types(unique=True)) } @@ -1446,12 +1422,7 @@ def average(self, method="mean"): f"got a {type(method).__name__} ({method})." ) # averaging unaggregated spectral estimates are not supported - if hasattr(self, "_mt_weights"): - raise NotImplementedError( - "Averaging complex spectra is not supported. Consider " - "averaging the signals before computing the complex spectrum." - ) - elif "segment" in self._dims: + if "segment" in self._dims: raise NotImplementedError( "Averaging individual Welch segments across epochs is not " "supported. Consider averaging the signals before computing " diff --git a/mne/time_frequency/tests/test_spectrum.py b/mne/time_frequency/tests/test_spectrum.py index 0de15b4248e..2dbc68741ff 100644 --- a/mne/time_frequency/tests/test_spectrum.py +++ b/mne/time_frequency/tests/test_spectrum.py @@ -21,8 +21,7 @@ def test_compute_psd_errors(raw): raw.compute_psd(foo=None) with pytest.raises(TypeError, match="keyword arguments foo, bar for"): raw.compute_psd(foo=None, bar=None) - # TODO: More code to remove here? - with pytest.raises(RuntimeError, match="Complex output support in.*not supported"): + with pytest.raises(ValueError, match="Complex output is not supported in "): raw.compute_psd(output="complex") @@ -218,13 +217,8 @@ def _agg_helper(df, weights, group_cols): @pytest.mark.parametrize("long_format", (False, True)) -@pytest.mark.parametrize( - "method, output", - [ - ("welch", "power"), - ], -) -def test_unaggregated_spectrum_to_data_frame(raw, long_format, method, output): +@pytest.mark.parametrize("method", ("welch", "multitaper")) +def test_unaggregated_spectrum_to_data_frame(raw, long_format, method): """Test converting complex multitaper spectra to data frame.""" pytest.importorskip("pandas") from pandas.testing import assert_frame_equal @@ -238,10 +232,10 @@ def test_unaggregated_spectrum_to_data_frame(raw, long_format, method, output): kwargs = dict() if method == "welch": kwargs.update(average=False, verbose="error") - spectrum = raw.compute_psd(method=method, output=output, **kwargs) + spectrum = raw.compute_psd(method=method, **kwargs) df = spectrum.to_data_frame(long_format=long_format) grouping_cols = ["freq"] - drop_cols = ["segment"] if method == "welch" else ["taper"] + drop_cols = ["segment"] if method == "welch" else [] if long_format: grouping_cols.append("channel") drop_cols.append("ch_type") @@ -256,18 +250,7 @@ def test_unaggregated_spectrum_to_data_frame(raw, long_format, method, output): # aggregate df = df.drop(columns=drop_cols) gb = df.groupby(grouping_cols, as_index=False, observed=False) - if method == "welch": - if output == "complex": - - def _fun(x): - return np.nanmean(np.abs(x)) - - agg_df = gb.agg(_fun) - else: - agg_df = gb.mean() # excludes missing values itself - else: - gb = gb[df.columns] # https://github.com/pandas-dev/pandas/pull/52477 - agg_df = gb.apply(_agg_helper, spectrum._mt_weights, grouping_cols) + agg_df = gb.mean() # excludes NA automatically # even with check_categorical=False, we know that the *data* matches; # what may differ is the order of the "levels" in the *metadata* for the # channel name column From 009731c52fbba141f74747319096f76aa69548f9 Mon Sep 17 00:00:00 2001 From: Daniel McCloy Date: Mon, 4 Dec 2023 16:17:47 -0600 Subject: [PATCH 4/4] remove crufty tests --- mne/time_frequency/tests/test_spectrum.py | 54 ----------------------- 1 file changed, 54 deletions(-) diff --git a/mne/time_frequency/tests/test_spectrum.py b/mne/time_frequency/tests/test_spectrum.py index 2dbc68741ff..653f0ab1411 100644 --- a/mne/time_frequency/tests/test_spectrum.py +++ b/mne/time_frequency/tests/test_spectrum.py @@ -9,7 +9,6 @@ from mne import Annotations from mne.time_frequency import read_spectrum -from mne.time_frequency.multitaper import _psd_from_mt from mne.time_frequency.spectrum import EpochsSpectrumArray, SpectrumArray @@ -204,59 +203,6 @@ def test_epochs_spectrum_average(epochs_spectrum, method): assert avg_spect._dims == ("channel", "freq") # no 'epoch' -def _agg_helper(df, weights, group_cols): - """Aggregate complex multitaper spectrum after conversion to DataFrame.""" - from pandas import Series - - unagged_columns = df[group_cols].iloc[0].values.tolist() - x_mt = df.drop(columns=group_cols).values[np.newaxis].T - psd = _psd_from_mt(x_mt, weights) - psd = np.atleast_1d(np.squeeze(psd)).tolist() - _df = dict(zip(df.columns, unagged_columns + psd)) - return Series(_df) - - -@pytest.mark.parametrize("long_format", (False, True)) -@pytest.mark.parametrize("method", ("welch", "multitaper")) -def test_unaggregated_spectrum_to_data_frame(raw, long_format, method): - """Test converting complex multitaper spectra to data frame.""" - pytest.importorskip("pandas") - from pandas.testing import assert_frame_equal - - from mne.utils.dataframe import _inplace - - # aggregated spectrum → dataframe - orig_df = raw.compute_psd(method=method).to_data_frame(long_format=long_format) - # unaggregated welch or complex multitaper → - # aggregate w/ pandas (to make sure we did reshaping right) - kwargs = dict() - if method == "welch": - kwargs.update(average=False, verbose="error") - spectrum = raw.compute_psd(method=method, **kwargs) - df = spectrum.to_data_frame(long_format=long_format) - grouping_cols = ["freq"] - drop_cols = ["segment"] if method == "welch" else [] - if long_format: - grouping_cols.append("channel") - drop_cols.append("ch_type") - orig_df.drop(columns="ch_type", inplace=True) - # only do a couple freq bins, otherwise test takes forever for multitaper - subset = partial(np.isin, test_elements=spectrum.freqs[:2]) - df = df.loc[subset(df["freq"])] - orig_df = orig_df.loc[subset(orig_df["freq"])] - # sort orig_df, because at present we can't actually prevent pandas from - # sorting at the agg step *sigh* - _inplace(orig_df, "sort_values", by=grouping_cols, ignore_index=True) - # aggregate - df = df.drop(columns=drop_cols) - gb = df.groupby(grouping_cols, as_index=False, observed=False) - agg_df = gb.mean() # excludes NA automatically - # even with check_categorical=False, we know that the *data* matches; - # what may differ is the order of the "levels" in the *metadata* for the - # channel name column - assert_frame_equal(agg_df, orig_df, check_categorical=False) - - @pytest.mark.parametrize("inst", ("raw_spectrum", "epochs_spectrum", "evoked")) def test_spectrum_to_data_frame(inst, request, evoked): """Test the to_data_frame method for Spectrum."""