1"""
2    sphinx.ext.napoleon
3    ~~~~~~~~~~~~~~~~~~~
4
5    Support for NumPy and Google style docstrings.
6
7    :copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
8    :license: BSD, see LICENSE for details.
9"""
10
11from typing import Any, Dict, List
12
13from sphinx import __display_version__ as __version__
14from sphinx.application import Sphinx
15from sphinx.ext.napoleon.docstring import GoogleDocstring, NumpyDocstring
16from sphinx.util import inspect
17
18
19class Config:
20    """Sphinx napoleon extension settings in `conf.py`.
21
22    Listed below are all the settings used by napoleon and their default
23    values. These settings can be changed in the Sphinx `conf.py` file. Make
24    sure that "sphinx.ext.napoleon" is enabled in `conf.py`::
25
26        # conf.py
27
28        # Add any Sphinx extension module names here, as strings
29        extensions = ['sphinx.ext.napoleon']
30
31        # Napoleon settings
32        napoleon_google_docstring = True
33        napoleon_numpy_docstring = True
34        napoleon_include_init_with_doc = False
35        napoleon_include_private_with_doc = False
36        napoleon_include_special_with_doc = False
37        napoleon_use_admonition_for_examples = False
38        napoleon_use_admonition_for_notes = False
39        napoleon_use_admonition_for_references = False
40        napoleon_use_ivar = False
41        napoleon_use_param = True
42        napoleon_use_rtype = True
43        napoleon_use_keyword = True
44        napoleon_preprocess_types = False
45        napoleon_type_aliases = None
46        napoleon_custom_sections = None
47        napoleon_attr_annotations = True
48
49    .. _Google style:
50       https://google.github.io/styleguide/pyguide.html
51    .. _NumPy style:
52       https://github.com/numpy/numpy/blob/master/doc/HOWTO_DOCUMENT.rst.txt
53
54    Attributes
55    ----------
56    napoleon_google_docstring : :obj:`bool` (Defaults to True)
57        True to parse `Google style`_ docstrings. False to disable support
58        for Google style docstrings.
59    napoleon_numpy_docstring : :obj:`bool` (Defaults to True)
60        True to parse `NumPy style`_ docstrings. False to disable support
61        for NumPy style docstrings.
62    napoleon_include_init_with_doc : :obj:`bool` (Defaults to False)
63        True to list ``__init___`` docstrings separately from the class
64        docstring. False to fall back to Sphinx's default behavior, which
65        considers the ``__init___`` docstring as part of the class
66        documentation.
67
68        **If True**::
69
70            def __init__(self):
71                \"\"\"
72                This will be included in the docs because it has a docstring
73                \"\"\"
74
75            def __init__(self):
76                # This will NOT be included in the docs
77
78    napoleon_include_private_with_doc : :obj:`bool` (Defaults to False)
79        True to include private members (like ``_membername``) with docstrings
80        in the documentation. False to fall back to Sphinx's default behavior.
81
82        **If True**::
83
84            def _included(self):
85                \"\"\"
86                This will be included in the docs because it has a docstring
87                \"\"\"
88                pass
89
90            def _skipped(self):
91                # This will NOT be included in the docs
92                pass
93
94    napoleon_include_special_with_doc : :obj:`bool` (Defaults to False)
95        True to include special members (like ``__membername__``) with
96        docstrings in the documentation. False to fall back to Sphinx's
97        default behavior.
98
99        **If True**::
100
101            def __str__(self):
102                \"\"\"
103                This will be included in the docs because it has a docstring
104                \"\"\"
105                return unicode(self).encode('utf-8')
106
107            def __unicode__(self):
108                # This will NOT be included in the docs
109                return unicode(self.__class__.__name__)
110
111    napoleon_use_admonition_for_examples : :obj:`bool` (Defaults to False)
112        True to use the ``.. admonition::`` directive for the **Example** and
113        **Examples** sections. False to use the ``.. rubric::`` directive
114        instead. One may look better than the other depending on what HTML
115        theme is used.
116
117        This `NumPy style`_ snippet will be converted as follows::
118
119            Example
120            -------
121            This is just a quick example
122
123        **If True**::
124
125            .. admonition:: Example
126
127               This is just a quick example
128
129        **If False**::
130
131            .. rubric:: Example
132
133            This is just a quick example
134
135    napoleon_use_admonition_for_notes : :obj:`bool` (Defaults to False)
136        True to use the ``.. admonition::`` directive for **Notes** sections.
137        False to use the ``.. rubric::`` directive instead.
138
139        Note
140        ----
141        The singular **Note** section will always be converted to a
142        ``.. note::`` directive.
143
144        See Also
145        --------
146        :attr:`napoleon_use_admonition_for_examples`
147
148    napoleon_use_admonition_for_references : :obj:`bool` (Defaults to False)
149        True to use the ``.. admonition::`` directive for **References**
150        sections. False to use the ``.. rubric::`` directive instead.
151
152        See Also
153        --------
154        :attr:`napoleon_use_admonition_for_examples`
155
156    napoleon_use_ivar : :obj:`bool` (Defaults to False)
157        True to use the ``:ivar:`` role for instance variables. False to use
158        the ``.. attribute::`` directive instead.
159
160        This `NumPy style`_ snippet will be converted as follows::
161
162            Attributes
163            ----------
164            attr1 : int
165                Description of `attr1`
166
167        **If True**::
168
169            :ivar attr1: Description of `attr1`
170            :vartype attr1: int
171
172        **If False**::
173
174            .. attribute:: attr1
175
176               Description of `attr1`
177
178               :type: int
179
180    napoleon_use_param : :obj:`bool` (Defaults to True)
181        True to use a ``:param:`` role for each function parameter. False to
182        use a single ``:parameters:`` role for all the parameters.
183
184        This `NumPy style`_ snippet will be converted as follows::
185
186            Parameters
187            ----------
188            arg1 : str
189                Description of `arg1`
190            arg2 : int, optional
191                Description of `arg2`, defaults to 0
192
193        **If True**::
194
195            :param arg1: Description of `arg1`
196            :type arg1: str
197            :param arg2: Description of `arg2`, defaults to 0
198            :type arg2: int, optional
199
200        **If False**::
201
202            :parameters: * **arg1** (*str*) --
203                           Description of `arg1`
204                         * **arg2** (*int, optional*) --
205                           Description of `arg2`, defaults to 0
206
207    napoleon_use_keyword : :obj:`bool` (Defaults to True)
208        True to use a ``:keyword:`` role for each function keyword argument.
209        False to use a single ``:keyword arguments:`` role for all the
210        keywords.
211
212        This behaves similarly to  :attr:`napoleon_use_param`. Note unlike
213        docutils, ``:keyword:`` and ``:param:`` will not be treated the same
214        way - there will be a separate "Keyword Arguments" section, rendered
215        in the same fashion as "Parameters" section (type links created if
216        possible)
217
218        See Also
219        --------
220        :attr:`napoleon_use_param`
221
222    napoleon_use_rtype : :obj:`bool` (Defaults to True)
223        True to use the ``:rtype:`` role for the return type. False to output
224        the return type inline with the description.
225
226        This `NumPy style`_ snippet will be converted as follows::
227
228            Returns
229            -------
230            bool
231                True if successful, False otherwise
232
233        **If True**::
234
235            :returns: True if successful, False otherwise
236            :rtype: bool
237
238        **If False**::
239
240            :returns: *bool* -- True if successful, False otherwise
241
242    napoleon_preprocess_types : :obj:`bool` (Defaults to False)
243        Enable the type preprocessor for numpy style docstrings.
244
245    napoleon_type_aliases : :obj:`dict` (Defaults to None)
246        Add a mapping of strings to string, translating types in numpy
247        style docstrings. Only works if ``napoleon_preprocess_types = True``.
248
249    napoleon_custom_sections : :obj:`list` (Defaults to None)
250        Add a list of custom sections to include, expanding the list of parsed sections.
251
252        The entries can either be strings or tuples, depending on the intention:
253          * To create a custom "generic" section, just pass a string.
254          * To create an alias for an existing section, pass a tuple containing the
255            alias name and the original, in that order.
256          * To create a custom section that displays like the parameters or returns
257            section, pass a tuple containing the custom section name and a string
258            value, "params_style" or "returns_style".
259
260        If an entry is just a string, it is interpreted as a header for a generic
261        section. If the entry is a tuple/list/indexed container, the first entry
262        is the name of the section, the second is the section key to emulate. If the
263        second entry value is "params_style" or "returns_style", the custom section
264        will be displayed like the parameters section or returns section.
265
266    napoleon_attr_annotations : :obj:`bool` (Defaults to True)
267        Use the type annotations of class attributes that are documented in the docstring
268        but do not have a type in the docstring.
269
270    """
271    _config_values = {
272        'napoleon_google_docstring': (True, 'env'),
273        'napoleon_numpy_docstring': (True, 'env'),
274        'napoleon_include_init_with_doc': (False, 'env'),
275        'napoleon_include_private_with_doc': (False, 'env'),
276        'napoleon_include_special_with_doc': (False, 'env'),
277        'napoleon_use_admonition_for_examples': (False, 'env'),
278        'napoleon_use_admonition_for_notes': (False, 'env'),
279        'napoleon_use_admonition_for_references': (False, 'env'),
280        'napoleon_use_ivar': (False, 'env'),
281        'napoleon_use_param': (True, 'env'),
282        'napoleon_use_rtype': (True, 'env'),
283        'napoleon_use_keyword': (True, 'env'),
284        'napoleon_preprocess_types': (False, 'env'),
285        'napoleon_type_aliases': (None, 'env'),
286        'napoleon_custom_sections': (None, 'env'),
287        'napoleon_attr_annotations': (True, 'env'),
288    }
289
290    def __init__(self, **settings: Any) -> None:
291        for name, (default, rebuild) in self._config_values.items():
292            setattr(self, name, default)
293        for name, value in settings.items():
294            setattr(self, name, value)
295
296
297def setup(app: Sphinx) -> Dict[str, Any]:
298    """Sphinx extension setup function.
299
300    When the extension is loaded, Sphinx imports this module and executes
301    the ``setup()`` function, which in turn notifies Sphinx of everything
302    the extension offers.
303
304    Parameters
305    ----------
306    app : sphinx.application.Sphinx
307        Application object representing the Sphinx process
308
309    See Also
310    --------
311    `The Sphinx documentation on Extensions
312    <http://sphinx-doc.org/extensions.html>`_
313
314    `The Extension Tutorial <http://sphinx-doc.org/extdev/tutorial.html>`_
315
316    `The Extension API <http://sphinx-doc.org/extdev/appapi.html>`_
317
318    """
319    if not isinstance(app, Sphinx):
320        # probably called by tests
321        return {'version': __version__, 'parallel_read_safe': True}
322
323    _patch_python_domain()
324
325    app.setup_extension('sphinx.ext.autodoc')
326    app.connect('autodoc-process-docstring', _process_docstring)
327    app.connect('autodoc-skip-member', _skip_member)
328
329    for name, (default, rebuild) in Config._config_values.items():
330        app.add_config_value(name, default, rebuild)
331    return {'version': __version__, 'parallel_read_safe': True}
332
333
334def _patch_python_domain() -> None:
335    try:
336        from sphinx.domains.python import PyTypedField
337    except ImportError:
338        pass
339    else:
340        import sphinx.domains.python
341        from sphinx.locale import _
342        for doc_field in sphinx.domains.python.PyObject.doc_field_types:
343            if doc_field.name == 'parameter':
344                doc_field.names = ('param', 'parameter', 'arg', 'argument')
345                break
346        sphinx.domains.python.PyObject.doc_field_types.append(
347            PyTypedField('keyword', label=_('Keyword Arguments'),
348                         names=('keyword', 'kwarg', 'kwparam'),
349                         typerolename='obj', typenames=('paramtype', 'kwtype'),
350                         can_collapse=True))
351
352
353def _process_docstring(app: Sphinx, what: str, name: str, obj: Any,
354                       options: Any, lines: List[str]) -> None:
355    """Process the docstring for a given python object.
356
357    Called when autodoc has read and processed a docstring. `lines` is a list
358    of docstring lines that `_process_docstring` modifies in place to change
359    what Sphinx outputs.
360
361    The following settings in conf.py control what styles of docstrings will
362    be parsed:
363
364    * ``napoleon_google_docstring`` -- parse Google style docstrings
365    * ``napoleon_numpy_docstring`` -- parse NumPy style docstrings
366
367    Parameters
368    ----------
369    app : sphinx.application.Sphinx
370        Application object representing the Sphinx process.
371    what : str
372        A string specifying the type of the object to which the docstring
373        belongs. Valid values: "module", "class", "exception", "function",
374        "method", "attribute".
375    name : str
376        The fully qualified name of the object.
377    obj : module, class, exception, function, method, or attribute
378        The object to which the docstring belongs.
379    options : sphinx.ext.autodoc.Options
380        The options given to the directive: an object with attributes
381        inherited_members, undoc_members, show_inheritance and noindex that
382        are True if the flag option of same name was given to the auto
383        directive.
384    lines : list of str
385        The lines of the docstring, see above.
386
387        .. note:: `lines` is modified *in place*
388
389    """
390    result_lines = lines
391    docstring = None  # type: GoogleDocstring
392    if app.config.napoleon_numpy_docstring:
393        docstring = NumpyDocstring(result_lines, app.config, app, what, name,
394                                   obj, options)
395        result_lines = docstring.lines()
396    if app.config.napoleon_google_docstring:
397        docstring = GoogleDocstring(result_lines, app.config, app, what, name,
398                                    obj, options)
399        result_lines = docstring.lines()
400    lines[:] = result_lines[:]
401
402
403def _skip_member(app: Sphinx, what: str, name: str, obj: Any,
404                 skip: bool, options: Any) -> bool:
405    """Determine if private and special class members are included in docs.
406
407    The following settings in conf.py determine if private and special class
408    members or init methods are included in the generated documentation:
409
410    * ``napoleon_include_init_with_doc`` --
411      include init methods if they have docstrings
412    * ``napoleon_include_private_with_doc`` --
413      include private members if they have docstrings
414    * ``napoleon_include_special_with_doc`` --
415      include special members if they have docstrings
416
417    Parameters
418    ----------
419    app : sphinx.application.Sphinx
420        Application object representing the Sphinx process
421    what : str
422        A string specifying the type of the object to which the member
423        belongs. Valid values: "module", "class", "exception", "function",
424        "method", "attribute".
425    name : str
426        The name of the member.
427    obj : module, class, exception, function, method, or attribute.
428        For example, if the member is the __init__ method of class A, then
429        `obj` will be `A.__init__`.
430    skip : bool
431        A boolean indicating if autodoc will skip this member if `_skip_member`
432        does not override the decision
433    options : sphinx.ext.autodoc.Options
434        The options given to the directive: an object with attributes
435        inherited_members, undoc_members, show_inheritance and noindex that
436        are True if the flag option of same name was given to the auto
437        directive.
438
439    Returns
440    -------
441    bool
442        True if the member should be skipped during creation of the docs,
443        False if it should be included in the docs.
444
445    """
446    has_doc = getattr(obj, '__doc__', False)
447    is_member = (what == 'class' or what == 'exception' or what == 'module')
448    if name != '__weakref__' and has_doc and is_member:
449        cls_is_owner = False
450        if what == 'class' or what == 'exception':
451            qualname = getattr(obj, '__qualname__', '')
452            cls_path, _, _ = qualname.rpartition('.')
453            if cls_path:
454                try:
455                    if '.' in cls_path:
456                        import functools
457                        import importlib
458
459                        mod = importlib.import_module(obj.__module__)
460                        mod_path = cls_path.split('.')
461                        cls = functools.reduce(getattr, mod_path, mod)
462                    else:
463                        cls = inspect.unwrap(obj).__globals__[cls_path]
464                except Exception:
465                    cls_is_owner = False
466                else:
467                    cls_is_owner = (cls and hasattr(cls, name) and  # type: ignore
468                                    name in cls.__dict__)
469            else:
470                cls_is_owner = False
471
472        if what == 'module' or cls_is_owner:
473            is_init = (name == '__init__')
474            is_special = (not is_init and name.startswith('__') and
475                          name.endswith('__'))
476            is_private = (not is_init and not is_special and
477                          name.startswith('_'))
478            inc_init = app.config.napoleon_include_init_with_doc
479            inc_special = app.config.napoleon_include_special_with_doc
480            inc_private = app.config.napoleon_include_private_with_doc
481            if ((is_special and inc_special) or
482                    (is_private and inc_private) or
483                    (is_init and inc_init)):
484                return False
485    return None
486