python-sphinxdocstringsectionsautodocsphinx-napoleon

How to fix "Unexpected section title"?


This docstring:

"""
Returns
-------
out: int
    Output.


Bad title
---------
Text.
"""

produces:

C:\module.py:docstring of my_pkg.module.func:5: CRITICAL: Unexpected section title.

Bad title
---------

Can I make it respect Bad title, or at least suppress the warning? I'm not using numpydoc or Napoleon, only

extensions = ['sphinx.ext.autodoc']

The two related questions with shared "Unexpected etc" don't help.


MRE

Built with sphinx-build -a -E . build. For convenience, here's sphinx-dummy-min.zip.

sphinx-dummy-min/docs/conf.py

import sys, pathlib

sys.path.insert(0, str(pathlib.Path(__file__).parent.parent))

project = 'dummy-module'
release = version = '0.1.0'
extensions = ['sphinx.ext.autodoc']
exclude_patterns = ['build']

sphinx-dummy-min/docs/dummy_module.rst

sphinx-dummy-min package
========================

Submodules
----------

dummy_module.dumdum module
--------------------------

.. automodule:: dummy_module.dumdum
   :members:
   :undoc-members:
   :show-inheritance:


Module contents
---------------

.. automodule:: dummy_module
   :members:
   :undoc-members:
   :show-inheritance:

sphinx-dummy-min/docs/index.rst

Welcome to dummy-module-min's documentation!
============================================

.. toctree::
   :maxdepth: 2
   :caption: Contents:

.. toctree::
   :maxdepth: 2
   :caption: Galleries:

   API reference <dummy_module>


Indices and tables
==================

* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`

sphinx-dummy-min/dummy_module/__init__.py


sphinx-dummy-min/dummy_module/dumdum.py

def bark():
    """
    Returns
    -------
    out: str
        "Bark".


    Bad title
    ---------
    Body.
    """


Solution

  • Arbitrary titles can be supported via 'sphinx.ext.napoleon''s napoleon_custom_sections in conf.py:

    napoleon_custom_sections = ['Bad title']
    

    This can be automated by iterating all .py in the package and finding ---- in docstrings - my approach below.

    Note, nested titles will also generate the warning - e.g.

    Parameters
    ----------
    param: str
        This is a param.
    
        Extended description
        --------------------
        This is a parameter.
    

    but that's fair enough.


    Auto custom sections

    from pathlib import Path
    
    def scrape_titles_in_folder(_dir):
        def scrape_titles_in_file(p):
            with open(p, 'r') as f:
                txt_lines = f.readlines()
            titles = []
            inside_of_docstring = False
            for i, line in enumerate(txt_lines):
                if '"""' in line:
                    inside_of_docstring = not inside_of_docstring
                # '---' or shorter is prone to false positives
                subsection_symbols = ('-', '^')  # can add more
    
                if (inside_of_docstring and
                    # `3` or less is prone to false positives (e.g. '-')
                        any(s * 4 in line for s in subsection_symbols)):
                    contender = txt_lines[i - 1].strip(' \n')
                    if contender != '':  # can appear for some reason
                        titles.append(contender)
    
                # e.g. """Docstring."""
                if line.count('"""') == 2:
                    inside_of_docstring = not inside_of_docstring
            return titles
    
        all_titles = []
        for p in _dir.iterdir():
            if p.suffix == '.py':
                all_titles.extend(scrape_titles_in_file(p))
            elif p.is_dir():
                all_titles.extend(scrape_titles_in_folder(p))
        return list(set(all_titles))  # unique only
    
    module_dir = Path(Path(__file__).parent.parent, 'dummy_module')
    section_titles = scrape_titles_in_folder(module_dir)
    
    napoleon_custom_sections = section_titles