Repository: mne-tools/mne-python

Issue:
switch our DeprecationWarnings to FutureWarnings
Python silences DeprecationWarnings when running in user code. 

It is therefore recommended to use FutureWarnings if we want to ensure that users see the warnings by default. 

We could remove
`warnings.filterwarnings('always', category=DeprecationWarning, module='mne')` from `mne/utils/docs.py`  and change our Deprecation- to FutureWarnings

_Originally posted by @hoechenberger in https://github.com/mne-tools/mne-python/pull/11097#discussion_r956784748_


Discussion:
hands down best issue number ever! 🎈 

Resolving patch:
diff --git a/mne/utils/docs.py b/mne/utils/docs.py
--- a/mne/utils/docs.py
+++ b/mne/utils/docs.py
@@ -9,7 +9,6 @@
 import os.path as op
 import re
 import sys
-import warnings
 import webbrowser
 from copy import deepcopy
 
@@ -4163,15 +4162,11 @@ def open_docs(kind=None, version=None):
     webbrowser.open_new_tab('https://mne.tools/%s/%s' % (version, kind))
 
 
-# make DeprecationWarnings actually show up for users
-warnings.filterwarnings('always', category=DeprecationWarning, module='mne')
-
-
 class _decorator:
     """Inject code or modify the docstring of a class, method, or function."""
 
-    def __init__(self, extra, kind):  # noqa: D102
-        self.kind = kind
+    def __init__(self, extra):  # noqa: D102
+        self.kind = self.__class__.__name__
         self.extra = extra
         self.msg = f'{{}} is a {self.kind} {{}}. {self.extra}.'
 
@@ -4241,18 +4236,15 @@ class deprecated(_decorator):
     extra : str
         Extra information beyond just saying the class/function/method is
         deprecated. Should be a complete sentence (trailing period will be
-        added automatically). Will be included in DeprecationWarning messages
+        added automatically). Will be included in FutureWarning messages
         and in a sphinx warning box in the docstring.
     """
 
-    def __init__(self, extra=''):
-        super().__init__(extra=extra, kind='deprecated')
-
     def _make_fun(self, func, msg):
         body = f"""\
 def %(name)s(%(signature)s):\n
     import warnings
-    warnings.warn({repr(msg)}, category=DeprecationWarning)
+    warnings.warn({repr(msg)}, category=FutureWarning)
     return _function_(%(shortsignature)s)"""
         return super()._make_fun(func=func, body=body)
 
@@ -4292,7 +4284,7 @@ class legacy(_decorator):
     def __init__(self, alt, extra=''):  # noqa: D102
         period = '. ' if len(extra) else ''
         extra = f'New code should use {alt}{period}{extra}'
-        super().__init__(extra=extra, kind='legacy')
+        super().__init__(extra=extra)
 
     def _make_fun(self, func, msg):
         body = f"""\
diff --git a/mne/viz/_brain/_brain.py b/mne/viz/_brain/_brain.py
--- a/mne/viz/_brain/_brain.py
+++ b/mne/viz/_brain/_brain.py
@@ -249,12 +249,12 @@ def __init__(self, subject=None, hemi='both', surf='pial', title=None,
 
         if show_toolbar is not None:
             warn('show_toolbar is deprecated and will be removed in 1.3.',
-                 DeprecationWarning)
+                 FutureWarning)
         # This and the "if subject is None" conditional should be removed in
         # 1.3, and the default subject=None switched to subject (no default)
         if subject_id is not None:
             warn('subject_id is deprecated and will be removed in 1.3, use '
-                 'subject instead.', DeprecationWarning)
+                 'subject instead.', FutureWarning)
             subject = subject_id
         if subject is None:
             # raise the same error that we'd get if subject had no default


Test patch:
diff --git a/mne/simulation/tests/test_metrics.py b/mne/simulation/tests/test_metrics.py
--- a/mne/simulation/tests/test_metrics.py
+++ b/mne/simulation/tests/test_metrics.py
@@ -27,7 +27,7 @@ def test_metrics():
     rng = np.random.RandomState(42)
     stc1 = simulate_sparse_stc(src, n_dipoles=2, times=times, random_state=rng)
     stc2 = simulate_sparse_stc(src, n_dipoles=2, times=times, random_state=rng)
-    with pytest.deprecated_call(match="source_estimate_quantification"):
+    with pytest.warns(FutureWarning, match="source_estimate_quantification"):
         E1_rms = source_estimate_quantification(stc1, stc1, metric='rms')
         E2_rms = source_estimate_quantification(stc2, stc2, metric='rms')
         E1_cos = source_estimate_quantification(stc1, stc1, metric='cosine')
@@ -39,14 +39,14 @@ def test_metrics():
     assert_allclose(E2_cos, 0., atol=1e-08)
 
     stc_bad = stc2.copy().crop(0, 0.5)
-    with pytest.deprecated_call(match="source_estimate_quantification"):
+    with pytest.warns(FutureWarning, match="source_estimate_quantification"):
         with pytest.raises(ValueError, match='must have the same size'):
             source_estimate_quantification(stc1, stc_bad)
     stc_bad = stc2.copy()
     stc_bad.tmin -= 0.1
-    with pytest.deprecated_call(match="source_estimate_quantification"):
+    with pytest.warns(FutureWarning, match="source_estimate_quantification"):
         with pytest.raises(ValueError, match='Times.*must match'):
             source_estimate_quantification(stc1, stc_bad)
-    with pytest.deprecated_call(match="source_estimate_quantification"):
+    with pytest.warns(FutureWarning, match="source_estimate_quantification"):
         with pytest.raises(ValueError, match="Invalid value for the 'metric'"):
             source_estimate_quantification(stc1, stc2, metric='foo')
diff --git a/mne/utils/tests/test_docs.py b/mne/utils/tests/test_docs.py
--- a/mne/utils/tests/test_docs.py
+++ b/mne/utils/tests/test_docs.py
@@ -70,11 +70,11 @@ def bad(self):  # noqa D102
 def test_deprecated_and_legacy(msg, func, klass):
     """Test deprecated and legacy decorators."""
     if msg == 'deprecated':
-        with pytest.deprecated_call(match=f'{msg} class'):
+        with pytest.warns(FutureWarning, match=f'{msg} class'):
             _klass = klass()
-        with pytest.deprecated_call(match=f'{msg} method'):
+        with pytest.warns(FutureWarning, match=f'{msg} method'):
             _klass.bad()
-        with pytest.deprecated_call(match=f'{msg} func'):
+        with pytest.warns(FutureWarning, match=f'{msg} func'):
             func()
     else:
         with catch_logging(verbose='info') as log:
diff --git a/mne/viz/_brain/tests/test_brain.py b/mne/viz/_brain/tests/test_brain.py
--- a/mne/viz/_brain/tests/test_brain.py
+++ b/mne/viz/_brain/tests/test_brain.py
@@ -199,7 +199,7 @@ def __init__(self):
     with pytest.raises(ValueError, match='RGB argument'):
         Brain('sample', cortex='badcolor')
     # test no surfaces
-    with pytest.deprecated_call(match='show_toolbar'):  # and subject_id
+    with pytest.warns(FutureWarning, match='show_toolbar'):  # and subject_id
         Brain(subject_id=subject, hemi=None, surf=None, show_toolbar=True)
     with pytest.raises(TypeError, match='missing 1 required positional'):
         Brain()