From 58dd09a63b4c3813f404473b30c53bfa01906bd2 Mon Sep 17 00:00:00 2001 From: "Thomas S. Binns" Date: Tue, 18 Aug 2026 23:02:48 +0100 Subject: [PATCH 1/4] Update directive checks --- doc/sphinxext/directive_formatting.py | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/doc/sphinxext/directive_formatting.py b/doc/sphinxext/directive_formatting.py index ffe0724c5c3..f89ec409525 100644 --- a/doc/sphinxext/directive_formatting.py +++ b/doc/sphinxext/directive_formatting.py @@ -19,7 +19,7 @@ def setup_module(): def check_directive_formatting(*args): - """Check that directives are not missing a space. + """Check that directives are not malformed. For args, see Sphinx events 'source-read' and 'autodoc-process-docstring'. """ @@ -37,8 +37,8 @@ def check_directive_formatting(*args): else: raise RuntimeError("Unexpected number of arguments from Sphinx event") - # Check if any directives are present - if re.search(r"\.\.\s*[a-zA-Z]+::", source_concat) is None: + # Check if text resembling directives are present + if re.search(r"\.\.\s*[a-zA-Z]+\s*:", source_concat) is None: return # Separate content into lines (docstrings already are) @@ -48,7 +48,7 @@ def check_directive_formatting(*args): # Check for bad formatting for idx, line in enumerate(source): # Check for missing space after '..' - missing = re.search(r"\.\.[a-zA-Z]+::", line) + missing = re.search(r"\.\.[a-zA-Z]+\s*:", line) if missing is not None: sphinx_logger.warning( f"{source_type} '{name}' is missing a space after '..' in the " @@ -56,12 +56,21 @@ def check_directive_formatting(*args): ) # Extra spaces after '..' don't affect formatting + # Check for bad number of final colons (should be exactly 2) + bad_colons = re.search(r"\.\.\s*[a-zA-Z]+\s*(? Date: Thu, 20 Aug 2026 14:32:48 +0200 Subject: [PATCH 2/4] [skip actions][skip azp] From f9c5cc79700765c9fb5c860cc4a60962d844ad95 Mon Sep 17 00:00:00 2001 From: "Thomas S. Binns" Date: Thu, 20 Aug 2026 14:53:38 +0200 Subject: [PATCH 3/4] Fix malformed directives --- mne/decoding/ems.py | 8 ++++---- mne/filter.py | 10 +++++----- mne/morph.py | 8 +++----- mne/source_estimate.py | 8 ++++---- tutorials/intro/70_report.py | 5 +++-- 5 files changed, 19 insertions(+), 20 deletions(-) diff --git a/mne/decoding/ems.py b/mne/decoding/ems.py index 21b02ebc697..785b2b89964 100644 --- a/mne/decoding/ems.py +++ b/mne/decoding/ems.py @@ -60,7 +60,7 @@ def __repr__(self): # noqa: D105 def fit(self, X, y): """Fit the spatial filters. - .. note : EMS is fitted on data normalized by channel type before the + .. note:: EMS is fitted on data normalized by channel type before the fitting of the spatial filters. Parameters @@ -116,15 +116,15 @@ def compute_ems( gives the similarity between the filter at each time point and the data vector (sensors) at that time point. - .. note : EMS only works for binary classification. + .. note:: EMS only works for binary classification. - .. note : The present function applies a leave-one-out cross-validation, + .. note:: The present function applies a leave-one-out cross-validation, following Schurger et al's paper. However, we recommend using a stratified k-fold cross-validation. Indeed, leave-one-out tends to overfit and cannot be used to estimate the variance of the prediction within a given fold. - .. note : Because of the leave-one-out, this function needs an equal + .. note:: Because of the leave-one-out, this function needs an equal number of epochs in each of the two conditions. Parameters diff --git a/mne/filter.py b/mne/filter.py index 78701236e32..4f8e7a2b1a2 100644 --- a/mne/filter.py +++ b/mne/filter.py @@ -2744,11 +2744,11 @@ def apply_hilbert( channels/vertices defined in ``picks`` is computed, resulting in the envelope signal. - .. warning: Do not use ``envelope=True`` if you intend to compute - an inverse solution from the raw data. If you want to - compute the envelope in source space, use - ``envelope=False`` and compute the envelope after the - inverse solution has been obtained. + .. warning:: + Do not use ``envelope=True`` if you intend to compute an inverse solution + from the raw data. If you want to compute the envelope in source space, use + ``envelope=False`` and compute the envelope after the inverse solution has + been obtained. If ``envelope=False``, more memory is required since the original raw data as well as the analytic signal have temporarily to be stored in memory. diff --git a/mne/morph.py b/mne/morph.py index c489568ff42..b02d0611960 100644 --- a/mne/morph.py +++ b/mne/morph.py @@ -37,9 +37,7 @@ verbose, warn, ) -from .utils import ( - warn as warn_, -) +from .utils import warn as warn_ @verbose @@ -523,8 +521,8 @@ def apply( mri_resolution : bool | tuple | int | float If True the image is saved in MRI resolution. Default False. - .. warning: If you have many time points the file produced can be - huge. The default is ``mri_resolution=False``. + .. warning:: If you have many time points the file produced can be + huge. The default is ``mri_resolution=False``. mri_space : bool | None Whether the image to world registration should be in mri space. The default (None) is mri_space=mri_resolution. diff --git a/mne/source_estimate.py b/mne/source_estimate.py index ee11b2bfa62..2f90953c191 100644 --- a/mne/source_estimate.py +++ b/mne/source_estimate.py @@ -2546,8 +2546,8 @@ def save_as_volume( mri_resolution : bool It True the image is saved in MRI resolution. - .. warning: If you have many time points the file produced can be - huge. The default is ``mri_resolution=False``. + .. warning:: If you have many time points the file produced can be + huge. The default is ``mri_resolution=False``. format : str Either ``'nifti1'`` (default) or ``'nifti2'``. @@ -2596,8 +2596,8 @@ def as_volume( mri_resolution : bool It True the image is saved in MRI resolution. - .. warning: If you have many time points the file produced can be - huge. The default is ``mri_resolution=False``. + .. warning:: If you have many time points the file produced can be + huge. The default is ``mri_resolution=False``. format : str Either 'nifti1' (default) or 'nifti2'. diff --git a/tutorials/intro/70_report.py b/tutorials/intro/70_report.py index 918634825bd..f76a6a3cfbc 100644 --- a/tutorials/intro/70_report.py +++ b/tutorials/intro/70_report.py @@ -46,12 +46,13 @@ # You may also use the :meth:`~mne.Report.parse_folder` method to select # particular files to include in the report. But more on that later. # -# .. sidebar: Viewing the report +# .. sidebar:: Viewing the report # # On successful creation of the report, the :meth:`~mne.Report.save` method # will open the HTML in a new tab in your browser. To disable this, use the # ``open_browser=False`` parameter of :meth:`~mne.Report.save`. -# + +# %% # Adding `~mne.io.Raw` data # ^^^^^^^^^^^^^^^^^^^^^^^^^ # From 7012924f10dd8e0e3be2e115a65b3c1a578e3859 Mon Sep 17 00:00:00 2001 From: "Thomas S. Binns" Date: Thu, 20 Aug 2026 15:47:02 +0200 Subject: [PATCH 4/4] Update logic --- doc/sphinxext/directive_formatting.py | 70 ++++++++++++++++++++++++--- 1 file changed, 63 insertions(+), 7 deletions(-) diff --git a/doc/sphinxext/directive_formatting.py b/doc/sphinxext/directive_formatting.py index f89ec409525..910f62a7114 100644 --- a/doc/sphinxext/directive_formatting.py +++ b/doc/sphinxext/directive_formatting.py @@ -6,6 +6,57 @@ from mne_doc_utils import sphinx_logger +DIRECTIVE_NAMES = [ + # Table of contents + "toctree", + # Admonitions, messages, warnings + "attention", + "caution", + "danger", + "error", + "hint", + "important", + "note", + "tip", + "warning", + "admonition", + "seealso", + # Changes between versions + "version-added", + "versionadded", + "versionchanged", + "version-changed", + "version-deprecated", + "deprecated", + "version-removed", + "versionremoved", + # Presentational + "rubric", + "centered", + "hlist", + # Code examples + "highlight", + "code-block", + "sourcecode", + "code", + "literalinclude", + # Glossary + "glossary", + # Meta-information + "sectionauthor", + "codeauthor", + # Index-generating markup + "index", + # Including content + "only", + # Tables + "tabularcolumns", + # Math + "math", + # Grammar production + "productionlist", +] + def setup(app): app.connect("source-read", check_directive_formatting) @@ -38,7 +89,7 @@ def check_directive_formatting(*args): raise RuntimeError("Unexpected number of arguments from Sphinx event") # Check if text resembling directives are present - if re.search(r"\.\.\s*[a-zA-Z]+\s*:", source_concat) is None: + if re.search(r"\.\.\s*[a-zA-Z\-]+\s*:", source_concat) is None: return # Separate content into lines (docstrings already are) @@ -48,7 +99,7 @@ def check_directive_formatting(*args): # Check for bad formatting for idx, line in enumerate(source): # Check for missing space after '..' - missing = re.search(r"\.\.[a-zA-Z]+\s*:", line) + missing = re.search(r"\.\.[a-zA-Z\-]+\s*:", line) if missing is not None: sphinx_logger.warning( f"{source_type} '{name}' is missing a space after '..' in the " @@ -57,12 +108,17 @@ def check_directive_formatting(*args): # Extra spaces after '..' don't affect formatting # Check for bad number of final colons (should be exactly 2) - bad_colons = re.search(r"\.\.\s*[a-zA-Z]+\s*(?