diff --git a/CHANGES b/CHANGES index d800bda4d..c1a325bb5 100644 --- a/CHANGES +++ b/CHANGES @@ -174,6 +174,8 @@ Features added * #4271: sphinx-build supports an option called ``-j auto`` to adjust numbers of processes automatically. +* Napoleon: added option to specify custom section tags. + Features removed ---------------- diff --git a/sphinx/ext/napoleon/__init__.py b/sphinx/ext/napoleon/__init__.py index dbe542635..8198a8bf5 100644 --- a/sphinx/ext/napoleon/__init__.py +++ b/sphinx/ext/napoleon/__init__.py @@ -47,6 +47,7 @@ class Config(object): napoleon_use_param = True napoleon_use_rtype = True napoleon_use_keyword = True + napoleon_custom_sections = None .. _Google style: https://google.github.io/styleguide/pyguide.html @@ -241,6 +242,19 @@ class Config(object): :returns: *bool* -- True if successful, False otherwise + napoleon_custom_sections : :obj:`list` (Defaults to None) + Add a list of custom sections to include, expanding the list of parsed sections. + + The entries can either be strings or tuples, depending on the intention: + * To create a custom "generic" section, just pass a string. + * To create an alias for an existing section, pass a tuple containing the + alias name and the original, in that order. + + If an entry is just a string, it is interpreted as a header for a generic + section. If the entry is a tuple/list/indexed container, the first entry + is the name of the section, the second is the section key to emulate. + + """ _config_values = { 'napoleon_google_docstring': (True, 'env'), @@ -254,7 +268,8 @@ class Config(object): 'napoleon_use_ivar': (False, 'env'), 'napoleon_use_param': (True, 'env'), 'napoleon_use_rtype': (True, 'env'), - 'napoleon_use_keyword': (True, 'env') + 'napoleon_use_keyword': (True, 'env'), + 'napoleon_custom_sections': (None, 'env') } def __init__(self, **settings): diff --git a/sphinx/ext/napoleon/docstring.py b/sphinx/ext/napoleon/docstring.py index 6e3aaeaf6..530cec203 100644 --- a/sphinx/ext/napoleon/docstring.py +++ b/sphinx/ext/napoleon/docstring.py @@ -170,6 +170,9 @@ class GoogleDocstring(UnicodeMixin): 'yield': self._parse_yields_section, 'yields': self._parse_yields_section, } # type: Dict[unicode, Callable] + + self._load_custom_sections() + self._parse() def __unicode__(self): @@ -530,6 +533,23 @@ class GoogleDocstring(UnicodeMixin): line and not self._is_indented(line, self._section_indent))) + def _load_custom_sections(self): + # type: () -> None + + if self._config.napoleon_custom_sections is not None: + for entry in self._config.napoleon_custom_sections: + if isinstance(entry, string_types): + # if entry is just a label, add to sections list, + # using generic section logic. + self._sections[entry.lower()] = self._parse_custom_generic_section + else: + # otherwise, assume entry is container; + # [0] is new section, [1] is the section to alias. + # in the case of key mismatch, just handle as generic section. + self._sections[entry[0].lower()] = \ + self._sections.get(entry[1].lower(), + self._parse_custom_generic_section) + def _parse(self): # type: () -> None self._parsed_lines = self._consume_empty() @@ -597,6 +617,10 @@ class GoogleDocstring(UnicodeMixin): use_admonition = self._config.napoleon_use_admonition_for_examples return self._parse_generic_section(section, use_admonition) + def _parse_custom_generic_section(self, section): + # for now, no admonition for simple custom sections + return self._parse_generic_section(section, False) + def _parse_usage_section(self, section): # type: (unicode) -> List[unicode] header = ['.. rubric:: Usage:', ''] # type: List[unicode] diff --git a/tests/test_ext_napoleon_docstring.py b/tests/test_ext_napoleon_docstring.py index 3b921ea27..73fec9eb8 100644 --- a/tests/test_ext_napoleon_docstring.py +++ b/tests/test_ext_napoleon_docstring.py @@ -966,6 +966,28 @@ Parameters: actual = str(GoogleDocstring(docstring, config)) self.assertEqual(expected, actual) + def test_custom_generic_sections(self): + + docstrings=("""\ +Really Important Details: + You should listen to me! +""", + """.. rubric:: Really Important Details + +You should listen to me! +"""),\ + ("""\ +Sooper Warning: + Stop hitting yourself! +""",""":Warns: **Stop hitting yourself!** +""") + + testConfig=Config(napoleon_custom_sections=['Really Important Details',('Sooper Warning','warns')]) + + for docstring, expected in docstrings: + actual = str(GoogleDocstring(docstring, testConfig)) + self.assertEqual(expected, actual) + class NumpyDocstringTest(BaseDocstringTest): docstrings = [(