diff --git a/doc/conf.py b/doc/conf.py index f3a50fd3518..0c8f65b7df7 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -128,6 +128,7 @@ "newcontrib_substitutions", "unit_role", "related_software", + "directive_formatting", ] # Add any paths that contain templates here, relative to this directory. diff --git a/doc/sphinxext/directive_formatting.py b/doc/sphinxext/directive_formatting.py new file mode 100644 index 00000000000..a3090ab4c90 --- /dev/null +++ b/doc/sphinxext/directive_formatting.py @@ -0,0 +1,88 @@ +# Authors: The MNE-Python contributors. +# License: BSD-3-Clause +# Copyright the MNE-Python contributors. + +import re + +from mne_doc_utils import sphinx_logger + + +def setup(app): + app.connect("source-read", check_directive_formatting) + app.connect("autodoc-process-docstring", check_directive_formatting) + + +def setup_module(): + # HACK: Stop nosetests running setup() above + pass + + +def check_directive_formatting(*args): + """Check that directives are not missing a space. + + For args, see Sphinx events 'source-read' and 'autodoc-process-docstring'. + """ + # Extract relevant info from args + if len(args) == 3: # from source-read + source_type = "File" + name = args[1] + source = args[2][0] + source_concat = source # content already a single string + elif len(args) == 6: # from autodoc-process-docstring + source_type = "Docstring" + name = args[2] + source = args[5] + source_concat = "\n".join(source) # combine lines into single string + 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: + return + + # Separate content into lines (docstrings already are) + if source_type == "File": + source = source.split("\n") + + # Check for bad formatting + for idx, line in enumerate(source): + # Check for missing space after '..' + missing = re.search(r"\.\.[a-zA-Z]+::", line) + if missing is not None: + sphinx_logger.warning( + f"{source_type} '{name}' is missing a space after '..' in the " + f"directive '{missing.group()}'" + ) + # Extra spaces after '..' don't affect formatting + + # Check for missing preceding blank line + # (exceptions are for directives at the start of files, after a header, or after + # another directive/another directive's content) + if idx == 0: + continue + dir_pattern = r"\.\. [a-zA-Z]+::" + head_pattern = r"^[-|=|\^]+$" + directive = re.search(dir_pattern, line) + if directive is not None: + line_prev = source[idx - 1].strip() + if ( # If previous line is... + line_prev != "" # not empty + and not re.search(head_pattern, line_prev) # not a header + and not re.search(dir_pattern, line_prev) # not a directive + ): + # Check if previous line is part of another directive + bad = True + for line_prev in reversed(source[: idx - 1]): + line_prev = line_prev.strip() + if line_prev == "" or re.search(head_pattern, line_prev): + # is a blank line or header, so not part of another directive + break # must be bad formatting + if re.search(dir_pattern, line_prev): + bad = False # is part of another directive, is good formatting + break + # or keep going until we reach the first line (so must be bad) + if bad: + sphinx_logger.warning( + f"{source_type} '{name}' is missing a blank line before the " + f"directive '{directive.group()}'" + ) diff --git a/examples/preprocessing/css.py b/examples/preprocessing/css.py index ba4e2385d0c..c6684b74273 100644 --- a/examples/preprocessing/css.py +++ b/examples/preprocessing/css.py @@ -99,6 +99,7 @@ def subcortical_waveform(times): ax.set(ylabel="EEG Power spectral density", xlabel="Frequency (Hz)") ax.legend() +############################################################################### # References # ^^^^^^^^^^ # diff --git a/examples/time_frequency/compute_source_psd_epochs.py b/examples/time_frequency/compute_source_psd_epochs.py index 0fa7558e9f4..21901e82eaa 100644 --- a/examples/time_frequency/compute_source_psd_epochs.py +++ b/examples/time_frequency/compute_source_psd_epochs.py @@ -72,7 +72,7 @@ # Compute source space PSD in label # --------------------------------- # -# ..note:: By using "return_generator=True" stcs will be a generator object +# .. note:: By using "return_generator=True" stcs will be a generator object # instead of a list. This allows us so to iterate without having to # keep everything in memory. diff --git a/mne/epochs.py b/mne/epochs.py index c042715e6ae..d6159f49583 100644 --- a/mne/epochs.py +++ b/mne/epochs.py @@ -2211,6 +2211,7 @@ def save( ------- fnames : List of path-like List of path-like objects containing the path to each file split. + .. versionadded:: 1.9 Notes diff --git a/mne/io/base.py b/mne/io/base.py index 29852d38863..eb2b5bc49f3 100644 --- a/mne/io/base.py +++ b/mne/io/base.py @@ -1755,6 +1755,7 @@ def save( ------- fnames : List of path-like List of path-like objects containing the path to each file split. + .. versionadded:: 1.9 Notes diff --git a/mne/io/egi/egi.py b/mne/io/egi/egi.py index 433758e19bf..73e93d39460 100644 --- a/mne/io/egi/egi.py +++ b/mne/io/egi/egi.py @@ -131,6 +131,7 @@ def read_raw_egi( Channel naming convention for the data channels. Defaults to ``'E%%d'`` (resulting in channel names ``'E1'``, ``'E2'``, ``'E3'``...). The effective default prior to 0.14.0 was ``'EEG %%03d'``. + .. versionadded:: 0.14.0 events_as_annotations : bool diff --git a/mne/io/nicolet/nicolet.py b/mne/io/nicolet/nicolet.py index f55cd77061d..05b8035bddd 100644 --- a/mne/io/nicolet/nicolet.py +++ b/mne/io/nicolet/nicolet.py @@ -21,7 +21,7 @@ def read_raw_nicolet( ) -> "RawNicolet": """Read Nicolet data as raw object. - ..note:: This reader takes data files with the extension ``.data`` as an + .. note:: This reader takes data files with the extension ``.data`` as an input. The header file with the same file name stem and an extension ``.head`` is expected to be found in the same directory. diff --git a/mne/label.py b/mne/label.py index 02bf9dc09c0..2d55de755c0 100644 --- a/mne/label.py +++ b/mne/label.py @@ -949,7 +949,7 @@ def compute_area( Notes ----- - ..versionadded:: 0.24 + .. versionadded:: 0.24 """ _, _, surf = self._load_surface( subject, subjects_dir, surface, return_dict=True