mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
Merges napoleon extension into mainline sphinx
This commit is contained in:
parent
dbd544d12b
commit
a00a096069
1
AUTHORS
1
AUTHORS
@ -32,6 +32,7 @@ Other contributors, listed alphabetically, are:
|
||||
* Christopher Perkins -- autosummary integration
|
||||
* Benjamin Peterson -- unittests
|
||||
* T. Powers -- HTML output improvements
|
||||
* Rob Ruana -- napoleon extension
|
||||
* Stefan Seefeld -- toctree improvements
|
||||
* Shibukawa Yoshiki -- pluggable search API and Japanese search
|
||||
* Antonio Valentino -- qthelp builder
|
||||
|
@ -5,8 +5,10 @@
|
||||
import re
|
||||
import sphinx
|
||||
|
||||
|
||||
extensions = ['sphinx.ext.autodoc', 'sphinx.ext.doctest', 'sphinx.ext.todo',
|
||||
'sphinx.ext.autosummary', 'sphinx.ext.extlinks']
|
||||
'sphinx.ext.autosummary', 'sphinx.ext.extlinks',
|
||||
'sphinx.ext.napoleon']
|
||||
|
||||
master_doc = 'contents'
|
||||
templates_path = ['_templates']
|
||||
|
223
doc/ext/example_google.py
Normal file
223
doc/ext/example_google.py
Normal file
@ -0,0 +1,223 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""Example Google style docstrings.
|
||||
|
||||
This module demonstrates documentation as specified by the `Google Python
|
||||
Style Guide`_. Docstrings may extend over multiple lines. Sections are created
|
||||
with a section header and a colon followed by a block of indented text.
|
||||
|
||||
Example:
|
||||
Examples can be given using either the ``Example`` or ``Examples``
|
||||
sections. Sections support any reStructuredText formatting, including
|
||||
literal blocks::
|
||||
|
||||
$ python example_google.py
|
||||
|
||||
Section breaks are created by simply resuming unindented text. Section breaks
|
||||
are also implicitly created anytime a new section starts.
|
||||
|
||||
Attributes:
|
||||
module_level_variable (int): Module level variables may be documented in
|
||||
either the ``Attributes`` section of the module docstring, or in an
|
||||
inline docstring immediately following the variable.
|
||||
|
||||
Either form is acceptable, but the two should not be mixed. Choose
|
||||
one convention to document module level variables and be consistent
|
||||
with it.
|
||||
|
||||
.. _Google Python Style Guide:
|
||||
http://google-styleguide.googlecode.com/svn/trunk/pyguide.html
|
||||
|
||||
"""
|
||||
|
||||
module_level_variable = 12345
|
||||
|
||||
|
||||
def module_level_function(param1, param2=None, *args, **kwargs):
|
||||
"""This is an example of a module level function.
|
||||
|
||||
Function parameters should be documented in the ``Args`` section. The name
|
||||
of each parameter is required. The type and description of each parameter
|
||||
is optional, but should be included if not obvious.
|
||||
|
||||
If the parameter itself is optional, it should be noted by adding
|
||||
", optional" to the type. If \*args or \*\*kwargs are accepted, they
|
||||
should be listed as \*args and \*\*kwargs.
|
||||
|
||||
The format for a parameter is::
|
||||
|
||||
name (type): description
|
||||
The description may span multiple lines. Following
|
||||
lines should be indented.
|
||||
|
||||
Multiple paragraphs are supported in parameter
|
||||
descriptions.
|
||||
|
||||
Args:
|
||||
param1 (int): The first parameter.
|
||||
param2 (str, optional): The second parameter. Defaults to None.
|
||||
Second line of description should be indented.
|
||||
*args: Variable length argument list.
|
||||
**kwargs: Arbitrary keyword arguments.
|
||||
|
||||
Returns:
|
||||
bool: True if successful, False otherwise.
|
||||
|
||||
The return type is optional and may be specified at the beginning of
|
||||
the ``Returns`` section followed by a colon.
|
||||
|
||||
The ``Returns`` section may span multiple lines and paragraphs.
|
||||
Following lines should be indented to match the first line.
|
||||
|
||||
The ``Returns`` section supports any reStructuredText formatting,
|
||||
including literal blocks::
|
||||
|
||||
{
|
||||
'param1': param1,
|
||||
'param2': param2
|
||||
}
|
||||
|
||||
Raises:
|
||||
AttributeError: The ``Raises`` section is a list of all exceptions
|
||||
that are relevant to the interface.
|
||||
ValueError: If `param2` is equal to `param1`.
|
||||
|
||||
"""
|
||||
if param1 == param2:
|
||||
raise ValueError('param1 may not be equal to param2')
|
||||
return True
|
||||
|
||||
|
||||
def example_generator(n):
|
||||
"""Generators have a ``Yields`` section instead of a ``Returns`` section.
|
||||
|
||||
Args:
|
||||
n (int): The upper limit of the range to generate, from 0 to `n` - 1
|
||||
|
||||
Yields:
|
||||
int: The next number in the range of 0 to `n` - 1
|
||||
|
||||
Examples:
|
||||
Examples should be written in doctest format, and should illustrate how
|
||||
to use the function.
|
||||
|
||||
>>> print [i for i in example_generator(4)]
|
||||
[0, 1, 2, 3]
|
||||
|
||||
"""
|
||||
for i in range(n):
|
||||
yield i
|
||||
|
||||
|
||||
class ExampleError(Exception):
|
||||
"""Exceptions are documented in the same way as classes.
|
||||
|
||||
The __init__ method may be documented in either the class level
|
||||
docstring, or as a docstring on the __init__ method itself.
|
||||
|
||||
Either form is acceptable, but the two should not be mixed. Choose one
|
||||
convention to document the __init__ method and be consistent with it.
|
||||
|
||||
Note:
|
||||
Do not include the `self` parameter in the ``Args`` section.
|
||||
|
||||
Args:
|
||||
msg (str): Human readable string describing the exception.
|
||||
code (int, optional): Error code, defaults to 2.
|
||||
|
||||
Attributes:
|
||||
msg (str): Human readable string describing the exception.
|
||||
code (int): Exception error code.
|
||||
|
||||
"""
|
||||
def __init__(self, msg, code=2):
|
||||
self.msg = msg
|
||||
self.code = code
|
||||
|
||||
|
||||
class ExampleClass(object):
|
||||
"""The summary line for a class docstring should fit on one line.
|
||||
|
||||
If the class has public attributes, they should be documented here
|
||||
in an ``Attributes`` section and follow the same formatting as a
|
||||
function's ``Args`` section.
|
||||
|
||||
Attributes:
|
||||
attr1 (str): Description of `attr1`.
|
||||
attr2 (list of str): Description of `attr2`.
|
||||
attr3 (int): Description of `attr3`.
|
||||
|
||||
"""
|
||||
def __init__(self, param1, param2, param3=0):
|
||||
"""Example of docstring on the __init__ method.
|
||||
|
||||
The __init__ method may be documented in either the class level
|
||||
docstring, or as a docstring on the __init__ method itself.
|
||||
|
||||
Either form is acceptable, but the two should not be mixed. Choose one
|
||||
convention to document the __init__ method and be consistent with it.
|
||||
|
||||
Note:
|
||||
Do not include the `self` parameter in the ``Args`` section.
|
||||
|
||||
Args:
|
||||
param1 (str): Description of `param1`.
|
||||
param2 (list of str): Description of `param2`. Multiple
|
||||
lines are supported.
|
||||
param3 (int, optional): Description of `param3`, defaults to 0.
|
||||
|
||||
"""
|
||||
self.attr1 = param1
|
||||
self.attr2 = param2
|
||||
self.attr3 = param3
|
||||
|
||||
def example_method(self, param1, param2):
|
||||
"""Class methods are similar to regular functions.
|
||||
|
||||
Note:
|
||||
Do not include the `self` parameter in the ``Args`` section.
|
||||
|
||||
Args:
|
||||
param1: The first parameter.
|
||||
param2: The second parameter.
|
||||
|
||||
Returns:
|
||||
True if successful, False otherwise.
|
||||
|
||||
"""
|
||||
return True
|
||||
|
||||
def __special__(self):
|
||||
"""By default special members with docstrings are included.
|
||||
|
||||
Special members are any methods or attributes that start with and
|
||||
end with a double underscore. Any special member with a docstring
|
||||
will be included in the output.
|
||||
|
||||
This behavior can be disabled by changing the following setting in
|
||||
Sphinx's conf.py::
|
||||
|
||||
napoleon_include_special_with_doc = False
|
||||
|
||||
"""
|
||||
pass
|
||||
|
||||
def __special_without_docstring__(self):
|
||||
pass
|
||||
|
||||
def _private(self):
|
||||
"""By default private members are not included.
|
||||
|
||||
Private members are any methods or attributes that start with an
|
||||
underscore and are *not* special. By default they are not included
|
||||
in the output.
|
||||
|
||||
This behavior can be changed such that private members *are* included
|
||||
by changing the following setting in Sphinx's conf.py::
|
||||
|
||||
napoleon_include_private_with_doc = True
|
||||
|
||||
"""
|
||||
pass
|
||||
|
||||
def _private_without_docstring(self):
|
||||
pass
|
15
doc/ext/example_google.rst
Normal file
15
doc/ext/example_google.rst
Normal file
@ -0,0 +1,15 @@
|
||||
:orphan:
|
||||
|
||||
.. _example_google:
|
||||
|
||||
Example Google Style Python Docstrings
|
||||
======================================
|
||||
|
||||
.. seealso::
|
||||
|
||||
:ref:`example_numpy`
|
||||
|
||||
Download: :download:`example_google.py <example_google.py>`
|
||||
|
||||
.. literalinclude:: example_google.py
|
||||
:language: python
|
272
doc/ext/example_numpy.py
Normal file
272
doc/ext/example_numpy.py
Normal file
@ -0,0 +1,272 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""Example NumPy style docstrings.
|
||||
|
||||
This module demonstrates documentation as specified by the `NumPy
|
||||
Documentation HOWTO`_. Docstrings may extend over multiple lines. Sections
|
||||
are created with a section header followed by an underline of equal length.
|
||||
|
||||
Example
|
||||
-------
|
||||
Examples can be given using either the ``Example`` or ``Examples``
|
||||
sections. Sections support any reStructuredText formatting, including
|
||||
literal blocks::
|
||||
|
||||
$ python example_numpy.py
|
||||
|
||||
|
||||
Section breaks are created with two blank lines. Section breaks are also
|
||||
implicitly created anytime a new section starts. Section bodies *may* be
|
||||
indented:
|
||||
|
||||
Notes
|
||||
-----
|
||||
This is an example of an indented section. It's like any other section,
|
||||
but the body is indented to help it stand out from surrounding text.
|
||||
|
||||
If a section is indented, then a section break is created simply by
|
||||
resuming unindented text.
|
||||
|
||||
Attributes
|
||||
----------
|
||||
module_level_variable : int
|
||||
Module level variables may be documented in either the ``Attributes``
|
||||
section of the module docstring, or in an inline docstring immediately
|
||||
following the variable.
|
||||
|
||||
Either form is acceptable, but the two should not be mixed. Choose
|
||||
one convention to document module level variables and be consistent
|
||||
with it.
|
||||
|
||||
.. _NumPy Documentation HOWTO:
|
||||
https://github.com/numpy/numpy/blob/master/doc/HOWTO_DOCUMENT.rst.txt
|
||||
|
||||
"""
|
||||
|
||||
module_level_variable = 12345
|
||||
|
||||
|
||||
def module_level_function(param1, param2=None, *args, **kwargs):
|
||||
"""This is an example of a module level function.
|
||||
|
||||
Function parameters should be documented in the ``Parameters`` section.
|
||||
The name of each parameter is required. The type and description of each
|
||||
parameter is optional, but should be included if not obvious.
|
||||
|
||||
If the parameter itself is optional, it should be noted by adding
|
||||
", optional" to the type. If \*args or \*\*kwargs are accepted, they
|
||||
should be listed as \*args and \*\*kwargs.
|
||||
|
||||
The format for a parameter is::
|
||||
|
||||
name : type
|
||||
description
|
||||
|
||||
The description may span multiple lines. Following lines
|
||||
should be indented to match the first line of the description.
|
||||
|
||||
Multiple paragraphs are supported in parameter
|
||||
descriptions.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
param1 : int
|
||||
The first parameter.
|
||||
param2 : str, optional
|
||||
The second parameter, defaults to None.
|
||||
*args
|
||||
Variable length argument list.
|
||||
**kwargs
|
||||
Arbitrary keyword arguments.
|
||||
|
||||
Returns
|
||||
-------
|
||||
bool
|
||||
True if successful, False otherwise.
|
||||
|
||||
The return type is not optional. The ``Returns`` section may span
|
||||
multiple lines and paragraphs. Following lines should be indented to
|
||||
match the first line of the description.
|
||||
|
||||
The ``Returns`` section supports any reStructuredText formatting,
|
||||
including literal blocks::
|
||||
|
||||
{
|
||||
'param1': param1,
|
||||
'param2': param2
|
||||
}
|
||||
|
||||
Raises
|
||||
------
|
||||
AttributeError
|
||||
The ``Raises`` section is a list of all exceptions
|
||||
that are relevant to the interface.
|
||||
ValueError
|
||||
If `param2` is equal to `param1`.
|
||||
|
||||
"""
|
||||
if param1 == param2:
|
||||
raise ValueError('param1 may not be equal to param2')
|
||||
return True
|
||||
|
||||
|
||||
def example_generator(n):
|
||||
"""Generators have a ``Yields`` section instead of a ``Returns`` section.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
n : int
|
||||
The upper limit of the range to generate, from 0 to `n` - 1
|
||||
|
||||
Yields
|
||||
------
|
||||
int
|
||||
The next number in the range of 0 to `n` - 1
|
||||
|
||||
Examples
|
||||
--------
|
||||
Examples should be written in doctest format, and should illustrate how
|
||||
to use the function.
|
||||
|
||||
>>> print [i for i in example_generator(4)]
|
||||
[0, 1, 2, 3]
|
||||
|
||||
"""
|
||||
for i in range(n):
|
||||
yield i
|
||||
|
||||
|
||||
class ExampleError(Exception):
|
||||
"""Exceptions are documented in the same way as classes.
|
||||
|
||||
The __init__ method may be documented in either the class level
|
||||
docstring, or as a docstring on the __init__ method itself.
|
||||
|
||||
Either form is acceptable, but the two should not be mixed. Choose one
|
||||
convention to document the __init__ method and be consistent with it.
|
||||
|
||||
Note
|
||||
----
|
||||
Do not include the `self` parameter in the ``Parameters`` section.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
msg : str
|
||||
Human readable string describing the exception.
|
||||
code : int, optional
|
||||
Error code, defaults to 2.
|
||||
|
||||
Attributes
|
||||
----------
|
||||
msg : str
|
||||
Human readable string describing the exception.
|
||||
code : int
|
||||
Exception error code.
|
||||
|
||||
"""
|
||||
def __init__(self, msg, code=2):
|
||||
self.msg = msg
|
||||
self.code = code
|
||||
|
||||
|
||||
class ExampleClass(object):
|
||||
"""The summary line for a class docstring should fit on one line.
|
||||
|
||||
If the class has public attributes, they should be documented here
|
||||
in an ``Attributes`` section and follow the same formatting as a
|
||||
function's ``Parameters`` section.
|
||||
|
||||
Attributes
|
||||
----------
|
||||
attr1 : str
|
||||
Description of `attr1`.
|
||||
attr2 : list of str
|
||||
Description of `attr2`.
|
||||
attr3 : int
|
||||
Description of `attr3`.
|
||||
|
||||
"""
|
||||
def __init__(self, param1, param2, param3=0):
|
||||
"""Example of docstring on the __init__ method.
|
||||
|
||||
The __init__ method may be documented in either the class level
|
||||
docstring, or as a docstring on the __init__ method itself.
|
||||
|
||||
Either form is acceptable, but the two should not be mixed. Choose one
|
||||
convention to document the __init__ method and be consistent with it.
|
||||
|
||||
Note
|
||||
----
|
||||
Do not include the `self` parameter in the ``Parameters`` section.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
param1 : str
|
||||
Description of `param1`.
|
||||
param2 : list of str
|
||||
Description of `param2`. Multiple
|
||||
lines are supported.
|
||||
param3 : int, optional
|
||||
Description of `param3`, defaults to 0.
|
||||
|
||||
"""
|
||||
self.attr1 = param1
|
||||
self.attr2 = param2
|
||||
self.attr3 = param3
|
||||
|
||||
def example_method(self, param1, param2):
|
||||
"""Class methods are similar to regular functions.
|
||||
|
||||
Note
|
||||
----
|
||||
Do not include the `self` parameter in the ``Parameters`` section.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
param1
|
||||
The first parameter.
|
||||
param2
|
||||
The second parameter.
|
||||
|
||||
Returns
|
||||
-------
|
||||
bool
|
||||
True if successful, False otherwise.
|
||||
|
||||
"""
|
||||
return True
|
||||
|
||||
def __special__(self):
|
||||
"""By default special members with docstrings are included.
|
||||
|
||||
Special members are any methods or attributes that start with and
|
||||
end with a double underscore. Any special member with a docstring
|
||||
will be included in the output.
|
||||
|
||||
This behavior can be disabled by changing the following setting in
|
||||
Sphinx's conf.py::
|
||||
|
||||
napoleon_include_special_with_doc = False
|
||||
|
||||
"""
|
||||
pass
|
||||
|
||||
def __special_without_docstring__(self):
|
||||
pass
|
||||
|
||||
def _private(self):
|
||||
"""By default private members are not included.
|
||||
|
||||
Private members are any methods or attributes that start with an
|
||||
underscore and are *not* special. By default they are not included
|
||||
in the output.
|
||||
|
||||
This behavior can be changed such that private members *are* included
|
||||
by changing the following setting in Sphinx's conf.py::
|
||||
|
||||
napoleon_include_private_with_doc = True
|
||||
|
||||
"""
|
||||
pass
|
||||
|
||||
def _private_without_docstring(self):
|
||||
pass
|
15
doc/ext/example_numpy.rst
Normal file
15
doc/ext/example_numpy.rst
Normal file
@ -0,0 +1,15 @@
|
||||
:orphan:
|
||||
|
||||
.. _example_numpy:
|
||||
|
||||
Example NumPy Style Python Docstrings
|
||||
======================================
|
||||
|
||||
.. seealso::
|
||||
|
||||
:ref:`example_google`
|
||||
|
||||
Download: :download:`example_numpy.py <example_numpy.py>`
|
||||
|
||||
.. literalinclude:: example_numpy.py
|
||||
:language: python
|
365
doc/ext/napoleon.rst
Normal file
365
doc/ext/napoleon.rst
Normal file
@ -0,0 +1,365 @@
|
||||
:mod:`sphinx.ext.napoleon` -- Support for NumPy and Google style docstrings
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. module:: sphinx.ext.napoleon
|
||||
:synopsis: Support for NumPy and Google style docstrings
|
||||
|
||||
.. moduleauthor:: Rob Ruana
|
||||
|
||||
.. versionadded:: 1.3
|
||||
|
||||
Napoleon - *Marching toward legible docstrings*
|
||||
===============================================
|
||||
|
||||
Are you tired of writing docstrings that look like this::
|
||||
|
||||
:param path: The path of the file to wrap
|
||||
:type path: str
|
||||
:param field_storage: The :class:`FileStorage` instance to wrap
|
||||
:type field_storage: FileStorage
|
||||
:param temporary: Whether or not to delete the file when the File
|
||||
instance is destructed
|
||||
:type temporary: bool
|
||||
:returns: A buffered writable file descriptor
|
||||
:rtype: BufferedFileStorage
|
||||
|
||||
`ReStructuredText`_ is great, but it creates visually dense, hard to read
|
||||
`docstrings`_. Compare the jumble above to the same thing rewritten
|
||||
according to the `Google Python Style Guide`_::
|
||||
|
||||
Args:
|
||||
path (str): The path of the file to wrap
|
||||
field_storage (FileStorage): The :class:`FileStorage` instance to wrap
|
||||
temporary (bool): Whether or not to delete the file when the File
|
||||
instance is destructed
|
||||
|
||||
Returns:
|
||||
BufferedFileStorage: A buffered writable file descriptor
|
||||
|
||||
Much more legible, no?
|
||||
|
||||
Napoleon is a `Sphinx extension`_ that allows you to write readable API
|
||||
documentation in your source code. Napoleon understands both `NumPy`_ and
|
||||
`Google`_ style docstrings - the style recommended by `Khan Academy`_.
|
||||
|
||||
.. _ReStructuredText: http://docutils.sourceforge.net/rst.html
|
||||
.. _docstrings: http://www.python.org/dev/peps/pep-0287/
|
||||
.. _Google Python Style Guide:
|
||||
http://google-styleguide.googlecode.com/svn/trunk/pyguide.html
|
||||
.. _Sphinx extension: http://sphinx-doc.org/extensions.html
|
||||
.. _Google:
|
||||
http://google-styleguide.googlecode.com/svn/trunk/pyguide.html#Comments
|
||||
.. _NumPy:
|
||||
https://github.com/numpy/numpy/blob/master/doc/HOWTO_DOCUMENT.rst.txt
|
||||
.. _Khan Academy:
|
||||
https://sites.google.com/a/khanacademy.org/forge/for-developers/styleguide/python#TOC-Docstrings
|
||||
|
||||
Getting Started
|
||||
---------------
|
||||
|
||||
1. After `setting up Sphinx`_ to build your docs, enable napoleon in the
|
||||
Sphinx `conf.py` file::
|
||||
|
||||
# conf.py
|
||||
|
||||
# Add autodoc and napoleon to the extensions list
|
||||
extensions = ['sphinx.ext.autodoc', 'sphinx.ext.napoleon']
|
||||
|
||||
2. Use `sphinx-apidoc` to build your API documentation::
|
||||
|
||||
$ sphinx-apidoc -f -o docs/source projectdir
|
||||
|
||||
.. _setting up Sphinx: http://sphinx-doc.org/tutorial.html
|
||||
|
||||
Docstrings
|
||||
----------
|
||||
|
||||
Napoleon interprets every docstring that `Sphinx autodoc`_ can find,
|
||||
including docstrings on: ``modules``, ``classes``, ``attributes``,
|
||||
``methods``, ``functions``, and ``variables``. Inside each docstring,
|
||||
specially formatted `Sections`_ are parsed and converted to
|
||||
reStructuredText.
|
||||
|
||||
All standard reStructuredText formatting still works as expected.
|
||||
|
||||
.. _Sphinx autodoc: http://sphinx-doc.org/ext/autodoc.html
|
||||
|
||||
|
||||
.. _Sections:
|
||||
|
||||
Docstring Sections
|
||||
------------------
|
||||
|
||||
All of the following section headers are supported:
|
||||
|
||||
* ``Args`` *(alias of Parameters)*
|
||||
* ``Arguments`` *(alias of Parameters)*
|
||||
* ``Attributes``
|
||||
* ``Example``
|
||||
* ``Examples``
|
||||
* ``Keyword Args`` *(alias of Keyword Arguments)*
|
||||
* ``Keyword Arguments``
|
||||
* ``Methods``
|
||||
* ``Note``
|
||||
* ``Notes``
|
||||
* ``Other Parameters``
|
||||
* ``Parameters``
|
||||
* ``Return`` *(alias of Returns)*
|
||||
* ``Returns``
|
||||
* ``Raises``
|
||||
* ``References``
|
||||
* ``See Also``
|
||||
* ``Warning``
|
||||
* ``Warnings`` *(alias of Warning)*
|
||||
* ``Warns``
|
||||
* ``Yields``
|
||||
|
||||
Google vs NumPy
|
||||
---------------
|
||||
|
||||
Napoleon supports two styles of docstrings: `Google`_ and `NumPy`_. The
|
||||
main difference between the two styles is that Google uses indention to
|
||||
separate sections, whereas NumPy uses underlines.
|
||||
|
||||
Google style::
|
||||
|
||||
def func(arg1, arg2):
|
||||
"""Summary line.
|
||||
|
||||
Extended description of function.
|
||||
|
||||
Args:
|
||||
arg1 (int): Description of arg1
|
||||
arg2 (str): Description of arg2
|
||||
|
||||
Returns:
|
||||
bool: Description of return value
|
||||
|
||||
"""
|
||||
return True
|
||||
|
||||
NumPy style::
|
||||
|
||||
def func(arg1, arg2):
|
||||
"""Summary line.
|
||||
|
||||
Extended description of function.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
arg1 : int
|
||||
Description of arg1
|
||||
arg2 : str
|
||||
Description of arg2
|
||||
|
||||
Returns
|
||||
-------
|
||||
bool
|
||||
Description of return value
|
||||
|
||||
"""
|
||||
return True
|
||||
|
||||
NumPy style tends to require more vertical space, whereas Google style
|
||||
tends to use more horizontal space. Google style tends to be easier to
|
||||
read for short and simple docstrings, whereas NumPy style tends be easier
|
||||
to read for long and in-depth docstrings.
|
||||
|
||||
The `Khan Academy`_ recommends using Google style.
|
||||
|
||||
The choice between styles is largely aesthetic, but the two styles should
|
||||
not be mixed. Choose one style for your project and be consistent with it.
|
||||
|
||||
.. seealso::
|
||||
|
||||
For complete examples:
|
||||
|
||||
* :ref:`example_google`
|
||||
* :ref:`example_numpy`
|
||||
|
||||
|
||||
Configuration
|
||||
=============
|
||||
|
||||
Listed below are all the settings used by napoleon and their default
|
||||
values. These settings can be changed in the Sphinx `conf.py` file. Make
|
||||
sure that both "sphinx.ext.autodoc" and "sphinx.ext.napoleon" are
|
||||
enabled in `conf.py`::
|
||||
|
||||
# conf.py
|
||||
|
||||
# Add any Sphinx extension module names here, as strings
|
||||
extensions = ['sphinx.ext.autodoc', 'sphinx.ext.napoleon']
|
||||
|
||||
# Napoleon settings
|
||||
napoleon_google_docstring = True
|
||||
napoleon_numpy_docstring = True
|
||||
napoleon_include_private_with_doc = False
|
||||
napoleon_include_special_with_doc = True
|
||||
napoleon_use_admonition_for_examples = False
|
||||
napoleon_use_admonition_for_notes = False
|
||||
napoleon_use_admonition_for_references = False
|
||||
napoleon_use_ivar = False
|
||||
napoleon_use_param = False
|
||||
napoleon_use_rtype = False
|
||||
|
||||
.. _Google style:
|
||||
http://google-styleguide.googlecode.com/svn/trunk/pyguide.html
|
||||
.. _NumPy style:
|
||||
https://github.com/numpy/numpy/blob/master/doc/HOWTO_DOCUMENT.rst.txt
|
||||
|
||||
|
||||
|
||||
**napoleon_google_docstring** : bool, defaults to True
|
||||
True to parse `Google style`_ docstrings. False to disable support
|
||||
for Google style docstrings.
|
||||
|
||||
**napoleon_numpy_docstring** : bool, defaults to True
|
||||
True to parse `NumPy style`_ docstrings. False to disable support
|
||||
for NumPy style docstrings.
|
||||
|
||||
**napoleon_include_private_with_doc** : bool, defaults to False
|
||||
True to include private members (like ``_membername``) with docstrings
|
||||
in the documentation. False to fall back to Sphinx's default behavior.
|
||||
|
||||
**If True**::
|
||||
|
||||
def _included(self):
|
||||
"""
|
||||
This will be included in the docs because it has a docstring
|
||||
"""
|
||||
pass
|
||||
|
||||
def _skipped(self):
|
||||
# This will NOT be included in the docs
|
||||
pass
|
||||
|
||||
**napoleon_include_special_with_doc** : bool, defaults to True
|
||||
True to include special members (like ``__membername__``) with
|
||||
docstrings in the documentation. False to fall back to Sphinx's
|
||||
default behavior.
|
||||
|
||||
**If True**::
|
||||
|
||||
def __str__(self):
|
||||
"""
|
||||
This will be included in the docs because it has a docstring
|
||||
"""
|
||||
return unicode(self).encode('utf-8')
|
||||
|
||||
def __unicode__(self):
|
||||
# This will NOT be included in the docs
|
||||
return unicode(self.__class__.__name__)
|
||||
|
||||
**napoleon_use_admonition_for_examples** : bool, defaults to False
|
||||
True to use the ``.. admonition::`` directive for the **Example** and
|
||||
**Examples** sections. False to use the ``.. rubric::`` directive
|
||||
instead. One may look better than the other depending on what HTML
|
||||
theme is used.
|
||||
|
||||
This `NumPy style`_ snippet will be converted as follows::
|
||||
|
||||
Example
|
||||
-------
|
||||
This is just a quick example
|
||||
|
||||
**If True**::
|
||||
|
||||
.. admonition:: Example
|
||||
|
||||
This is just a quick example
|
||||
|
||||
**If False**::
|
||||
|
||||
.. rubric:: Example
|
||||
|
||||
This is just a quick example
|
||||
|
||||
**napoleon_use_admonition_for_notes** : bool, defaults to False
|
||||
True to use the ``.. admonition::`` directive for **Notes** sections.
|
||||
False to use the ``.. rubric::`` directive instead.
|
||||
|
||||
.. note:: The singular **Note** section will always be converted to a
|
||||
``.. note::`` directive.
|
||||
|
||||
.. seealso::
|
||||
|
||||
:attr:`napoleon_use_admonition_for_examples`
|
||||
|
||||
**napoleon_use_admonition_for_references** : bool, defaults to False
|
||||
True to use the ``.. admonition::`` directive for **References**
|
||||
sections. False to use the ``.. rubric::`` directive instead.
|
||||
|
||||
.. seealso::
|
||||
|
||||
:attr:`napoleon_use_admonition_for_examples`
|
||||
|
||||
**napoleon_use_ivar** : bool, defaults to False
|
||||
True to use the ``:ivar:`` role for instance variables. False to use
|
||||
the ``.. attribute::`` directive instead.
|
||||
|
||||
This `NumPy style`_ snippet will be converted as follows::
|
||||
|
||||
Attributes
|
||||
----------
|
||||
attr1 : int
|
||||
Description of `attr1`
|
||||
|
||||
**If True**::
|
||||
|
||||
:ivar attr1: Description of `attr1`
|
||||
:vartype attr1: int
|
||||
|
||||
**If False**::
|
||||
|
||||
.. attribute:: attr1
|
||||
:annotation: int
|
||||
|
||||
Description of `attr1`
|
||||
|
||||
**napoleon_use_param** : bool, defaults to False
|
||||
True to use a ``:param:`` role for each function parameter. False to
|
||||
use a single ``:parameters:`` role for all the parameters.
|
||||
|
||||
This `NumPy style`_ snippet will be converted as follows::
|
||||
|
||||
Parameters
|
||||
----------
|
||||
arg1 : str
|
||||
Description of `arg1`
|
||||
arg2 : int, optional
|
||||
Description of `arg2`, defaults to 0
|
||||
|
||||
**If True**::
|
||||
|
||||
:param arg1: Description of `arg1`
|
||||
:type arg1: str
|
||||
:param arg2: Description of `arg2`, defaults to 0
|
||||
:type arg2: int, optional
|
||||
|
||||
**If False**::
|
||||
|
||||
:parameters: * **arg1** (*str*) --
|
||||
Description of `arg1`
|
||||
* **arg2** (*int, optional*) --
|
||||
Description of `arg2`, defaults to 0
|
||||
|
||||
**napoleon_use_rtype** : bool, defaults to False
|
||||
True to use the ``:rtype:`` role for the return type. False to output
|
||||
the return type inline with the description.
|
||||
|
||||
This `NumPy style`_ snippet will be converted as follows::
|
||||
|
||||
Returns
|
||||
-------
|
||||
bool
|
||||
True if successful, False otherwise
|
||||
|
||||
**If True**::
|
||||
|
||||
:returns: True if successful, False otherwise
|
||||
:rtype: bool
|
||||
|
||||
**If False**::
|
||||
|
||||
:returns: *bool* -- True if successful, False otherwise
|
@ -54,6 +54,7 @@ These extensions are built in and can be activated by respective entries in the
|
||||
ext/viewcode
|
||||
ext/linkcode
|
||||
ext/oldcmarkup
|
||||
ext/napoleon
|
||||
|
||||
|
||||
Third-party extensions
|
||||
|
371
sphinx/ext/napoleon/__init__.py
Normal file
371
sphinx/ext/napoleon/__init__.py
Normal file
@ -0,0 +1,371 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
sphinx.ext.napoleon
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Support for NumPy and Google style docstrings.
|
||||
|
||||
:copyright: Copyright 2007-2014 by the Sphinx team, see AUTHORS.
|
||||
:license: BSD, see LICENSE for details.
|
||||
"""
|
||||
|
||||
import sys
|
||||
from sphinx.ext.napoleon.docstring import GoogleDocstring, NumpyDocstring
|
||||
|
||||
|
||||
class Config(object):
|
||||
"""Sphinx napoleon extension settings in `conf.py`.
|
||||
|
||||
Listed below are all the settings used by napoleon and their default
|
||||
values. These settings can be changed in the Sphinx `conf.py` file. Make
|
||||
sure that both "sphinx.ext.autodoc" and "sphinx.ext.napoleon" are
|
||||
enabled in `conf.py`::
|
||||
|
||||
# conf.py
|
||||
|
||||
# Add any Sphinx extension module names here, as strings
|
||||
extensions = ['sphinx.ext.autodoc', 'sphinx.ext.napoleon']
|
||||
|
||||
# Napoleon settings
|
||||
napoleon_google_docstring = True
|
||||
napoleon_numpy_docstring = True
|
||||
napoleon_include_private_with_doc = False
|
||||
napoleon_include_special_with_doc = True
|
||||
napoleon_use_admonition_for_examples = False
|
||||
napoleon_use_admonition_for_notes = False
|
||||
napoleon_use_admonition_for_references = False
|
||||
napoleon_use_ivar = False
|
||||
napoleon_use_param = False
|
||||
napoleon_use_rtype = False
|
||||
|
||||
.. _Google style:
|
||||
http://google-styleguide.googlecode.com/svn/trunk/pyguide.html
|
||||
.. _NumPy style:
|
||||
https://github.com/numpy/numpy/blob/master/doc/HOWTO_DOCUMENT.rst.txt
|
||||
|
||||
|
||||
|
||||
**napoleon_google_docstring** : bool, defaults to True
|
||||
True to parse `Google style`_ docstrings. False to disable support
|
||||
for Google style docstrings.
|
||||
|
||||
**napoleon_numpy_docstring** : bool, defaults to True
|
||||
True to parse `NumPy style`_ docstrings. False to disable support
|
||||
for NumPy style docstrings.
|
||||
|
||||
**napoleon_include_private_with_doc** : bool, defaults to False
|
||||
True to include private members (like ``_membername``) with docstrings
|
||||
in the documentation. False to fall back to Sphinx's default behavior.
|
||||
|
||||
**If True**::
|
||||
|
||||
def _included(self):
|
||||
\"\"\"
|
||||
This will be included in the docs because it has a docstring
|
||||
\"\"\"
|
||||
pass
|
||||
|
||||
def _skipped(self):
|
||||
# This will NOT be included in the docs
|
||||
pass
|
||||
|
||||
**napoleon_include_special_with_doc** : bool, defaults to True
|
||||
True to include special members (like ``__membername__``) with
|
||||
docstrings in the documentation. False to fall back to Sphinx's
|
||||
default behavior.
|
||||
|
||||
**If True**::
|
||||
|
||||
def __str__(self):
|
||||
\"\"\"
|
||||
This will be included in the docs because it has a docstring
|
||||
\"\"\"
|
||||
return unicode(self).encode('utf-8')
|
||||
|
||||
def __unicode__(self):
|
||||
# This will NOT be included in the docs
|
||||
return unicode(self.__class__.__name__)
|
||||
|
||||
**napoleon_use_admonition_for_examples** : bool, defaults to False
|
||||
True to use the ``.. admonition::`` directive for the **Example** and
|
||||
**Examples** sections. False to use the ``.. rubric::`` directive
|
||||
instead. One may look better than the other depending on what HTML
|
||||
theme is used.
|
||||
|
||||
This `NumPy style`_ snippet will be converted as follows::
|
||||
|
||||
Example
|
||||
-------
|
||||
This is just a quick example
|
||||
|
||||
**If True**::
|
||||
|
||||
.. admonition:: Example
|
||||
|
||||
This is just a quick example
|
||||
|
||||
**If False**::
|
||||
|
||||
.. rubric:: Example
|
||||
|
||||
This is just a quick example
|
||||
|
||||
**napoleon_use_admonition_for_notes** : bool, defaults to False
|
||||
True to use the ``.. admonition::`` directive for **Notes** sections.
|
||||
False to use the ``.. rubric::`` directive instead.
|
||||
|
||||
.. note:: The singular **Note** section will always be converted to a
|
||||
``.. note::`` directive.
|
||||
|
||||
.. seealso:: :attr:`napoleon_use_admonition_for_examples`
|
||||
|
||||
**napoleon_use_admonition_for_references** : bool, defaults to False
|
||||
True to use the ``.. admonition::`` directive for **References**
|
||||
sections. False to use the ``.. rubric::`` directive instead.
|
||||
|
||||
.. seealso:: :attr:`napoleon_use_admonition_for_examples`
|
||||
|
||||
**napoleon_use_ivar** : bool, defaults to False
|
||||
True to use the ``:ivar:`` role for instance variables. False to use
|
||||
the ``.. attribute::`` directive instead.
|
||||
|
||||
This `NumPy style`_ snippet will be converted as follows::
|
||||
|
||||
Attributes
|
||||
----------
|
||||
attr1 : int
|
||||
Description of `attr1`
|
||||
|
||||
**If True**::
|
||||
|
||||
:ivar attr1: Description of `attr1`
|
||||
:vartype attr1: int
|
||||
|
||||
**If False**::
|
||||
|
||||
.. attribute:: attr1
|
||||
:annotation: int
|
||||
|
||||
Description of `attr1`
|
||||
|
||||
**napoleon_use_param** : bool, defaults to False
|
||||
True to use a ``:param:`` role for each function parameter. False to
|
||||
use a single ``:parameters:`` role for all the parameters.
|
||||
|
||||
This `NumPy style`_ snippet will be converted as follows::
|
||||
|
||||
Parameters
|
||||
----------
|
||||
arg1 : str
|
||||
Description of `arg1`
|
||||
arg2 : int, optional
|
||||
Description of `arg2`, defaults to 0
|
||||
|
||||
**If True**::
|
||||
|
||||
:param arg1: Description of `arg1`
|
||||
:type arg1: str
|
||||
:param arg2: Description of `arg2`, defaults to 0
|
||||
:type arg2: int, optional
|
||||
|
||||
**If False**::
|
||||
|
||||
:parameters: * **arg1** (*str*) --
|
||||
Description of `arg1`
|
||||
* **arg2** (*int, optional*) --
|
||||
Description of `arg2`, defaults to 0
|
||||
|
||||
**napoleon_use_rtype** : bool, defaults to False
|
||||
True to use the ``:rtype:`` role for the return type. False to output
|
||||
the return type inline with the description.
|
||||
|
||||
This `NumPy style`_ snippet will be converted as follows::
|
||||
|
||||
Returns
|
||||
-------
|
||||
bool
|
||||
True if successful, False otherwise
|
||||
|
||||
**If True**::
|
||||
|
||||
:returns: True if successful, False otherwise
|
||||
:rtype: bool
|
||||
|
||||
**If False**::
|
||||
|
||||
:returns: *bool* -- True if successful, False otherwise
|
||||
|
||||
"""
|
||||
_config_values = {
|
||||
'napoleon_google_docstring': (True, 'env'),
|
||||
'napoleon_numpy_docstring': (True, 'env'),
|
||||
'napoleon_include_private_with_doc': (False, 'env'),
|
||||
'napoleon_include_special_with_doc': (True, 'env'),
|
||||
'napoleon_use_admonition_for_examples': (False, 'env'),
|
||||
'napoleon_use_admonition_for_notes': (False, 'env'),
|
||||
'napoleon_use_admonition_for_references': (False, 'env'),
|
||||
'napoleon_use_ivar': (False, 'env'),
|
||||
'napoleon_use_param': (False, 'env'),
|
||||
'napoleon_use_rtype': (False, 'env'),
|
||||
}
|
||||
|
||||
def __init__(self, **settings):
|
||||
for name, (default, rebuild) in self._config_values.iteritems():
|
||||
setattr(self, name, default)
|
||||
for name, value in settings.iteritems():
|
||||
setattr(self, name, value)
|
||||
|
||||
|
||||
def setup(app):
|
||||
"""Sphinx extension setup function.
|
||||
|
||||
When the extension is loaded, Sphinx imports this module and executes
|
||||
the ``setup()`` function, which in turn notifies Sphinx of everything
|
||||
the extension offers.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
app : sphinx.application.Sphinx
|
||||
Application object representing the Sphinx process
|
||||
|
||||
See Also
|
||||
--------
|
||||
The Sphinx documentation on `Extensions`_, the `Extension Tutorial`_, and
|
||||
the `Extension API`_.
|
||||
|
||||
.. _Extensions: http://sphinx-doc.org/extensions.html
|
||||
.. _Extension Tutorial: http://sphinx-doc.org/ext/tutorial.html
|
||||
.. _Extension API: http://sphinx-doc.org/ext/appapi.html
|
||||
|
||||
"""
|
||||
from sphinx.application import Sphinx
|
||||
if not isinstance(app, Sphinx):
|
||||
return # probably called by tests
|
||||
|
||||
app.connect('autodoc-process-docstring', _process_docstring)
|
||||
app.connect('autodoc-skip-member', _skip_member)
|
||||
|
||||
for name, (default, rebuild) in Config._config_values.iteritems():
|
||||
app.add_config_value(name, default, rebuild)
|
||||
|
||||
|
||||
def _process_docstring(app, what, name, obj, options, lines):
|
||||
"""Process the docstring for a given python object.
|
||||
|
||||
Called when autodoc has read and processed a docstring. `lines` is a list
|
||||
of docstring lines that `_process_docstring` modifies in place to change
|
||||
what Sphinx outputs.
|
||||
|
||||
The following settings in conf.py control what styles of docstrings will
|
||||
be parsed:
|
||||
|
||||
* ``napoleon_google_docstring`` -- parse Google style docstrings
|
||||
* ``napoleon_numpy_docstring`` -- parse NumPy style docstrings
|
||||
|
||||
Parameters
|
||||
----------
|
||||
app : sphinx.application.Sphinx
|
||||
Application object representing the Sphinx process.
|
||||
what : str
|
||||
A string specifying the type of the object to which the docstring
|
||||
belongs. Valid values: "module", "class", "exception", "function",
|
||||
"method", "attribute".
|
||||
name : str
|
||||
The fully qualified name of the object.
|
||||
obj : module, class, exception, function, method, or attribute
|
||||
The object to which the docstring belongs.
|
||||
options : sphinx.ext.autodoc.Options
|
||||
The options given to the directive: an object with attributes
|
||||
inherited_members, undoc_members, show_inheritance and noindex that
|
||||
are True if the flag option of same name was given to the auto
|
||||
directive.
|
||||
lines : list of str
|
||||
The lines of the docstring, see above.
|
||||
|
||||
.. note:: `lines` is modified *in place*
|
||||
|
||||
"""
|
||||
result_lines = lines
|
||||
if app.config.napoleon_numpy_docstring:
|
||||
docstring = NumpyDocstring(result_lines, app.config, app, what, name,
|
||||
obj, options)
|
||||
result_lines = docstring.lines()
|
||||
if app.config.napoleon_google_docstring:
|
||||
docstring = GoogleDocstring(result_lines, app.config, app, what, name,
|
||||
obj, options)
|
||||
result_lines = docstring.lines()
|
||||
lines[:] = result_lines[:]
|
||||
|
||||
|
||||
def _skip_member(app, what, name, obj, skip, options):
|
||||
"""Determine if private and special class members are included in docs.
|
||||
|
||||
The following settings in conf.py determine if private and special class
|
||||
members are included in the generated documentation:
|
||||
|
||||
* ``napoleon_include_private_with_doc`` --
|
||||
include private members if they have docstrings
|
||||
* ``napoleon_include_special_with_doc`` --
|
||||
include special members if they have docstrings
|
||||
|
||||
Parameters
|
||||
----------
|
||||
app : sphinx.application.Sphinx
|
||||
Application object representing the Sphinx process
|
||||
what : str
|
||||
A string specifying the type of the object to which the member
|
||||
belongs. Valid values: "module", "class", "exception", "function",
|
||||
"method", "attribute".
|
||||
name : str
|
||||
The name of the member.
|
||||
obj : module, class, exception, function, method, or attribute.
|
||||
For example, if the member is the __init__ method of class A, then
|
||||
`obj` will be `A.__init__`.
|
||||
skip : bool
|
||||
A boolean indicating if autodoc will skip this member if `_skip_member`
|
||||
does not override the decision
|
||||
options : sphinx.ext.autodoc.Options
|
||||
The options given to the directive: an object with attributes
|
||||
inherited_members, undoc_members, show_inheritance and noindex that
|
||||
are True if the flag option of same name was given to the auto
|
||||
directive.
|
||||
|
||||
Returns
|
||||
-------
|
||||
bool
|
||||
True if the member should be skipped during creation of the docs,
|
||||
False if it should be included in the docs.
|
||||
|
||||
"""
|
||||
has_doc = getattr(obj, '__doc__', False)
|
||||
is_member = (what == 'class' or what == 'exception' or what == 'module')
|
||||
if name != '__weakref__' and name != '__init__' and has_doc and is_member:
|
||||
if what == 'class' or what == 'exception':
|
||||
if sys.version_info[0] < 3:
|
||||
cls = getattr(obj, 'im_class', getattr(obj, '__objclass__',
|
||||
None))
|
||||
cls_is_owner = (cls and hasattr(cls, name) and
|
||||
name in cls.__dict__)
|
||||
elif sys.version_info[1] >= 3 and hasattr(obj, '__qualname__'):
|
||||
cls_path, _, _ = obj.__qualname__.rpartition('.')
|
||||
if cls_path:
|
||||
import importlib
|
||||
import functools
|
||||
|
||||
mod = importlib.import_module(obj.__module__)
|
||||
cls = functools.reduce(getattr, cls_path.split('.'), mod)
|
||||
cls_is_owner = (cls and hasattr(cls, name) and
|
||||
name in cls.__dict__)
|
||||
else:
|
||||
cls_is_owner = False
|
||||
else:
|
||||
cls_is_owner = True
|
||||
|
||||
if what == 'module' or cls_is_owner:
|
||||
is_special = name.startswith('__') and name.endswith('__')
|
||||
is_private = not is_special and name.startswith('_')
|
||||
inc_special = app.config.napoleon_include_special_with_doc
|
||||
inc_private = app.config.napoleon_include_private_with_doc
|
||||
if (is_special and inc_special) or (is_private and inc_private):
|
||||
return False
|
||||
return skip
|
714
sphinx/ext/napoleon/docstring.py
Normal file
714
sphinx/ext/napoleon/docstring.py
Normal file
@ -0,0 +1,714 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
sphinx.ext.napoleon.docstring
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
||||
Classes for docstring parsing and formatting.
|
||||
|
||||
|
||||
:copyright: Copyright 2007-2014 by the Sphinx team, see AUTHORS.
|
||||
:license: BSD, see LICENSE for details.
|
||||
"""
|
||||
|
||||
import re
|
||||
import sys
|
||||
from sphinx.ext.napoleon.iterators import modify_iter
|
||||
|
||||
|
||||
if sys.version_info[0] >= 3:
|
||||
basestring = str
|
||||
xrange = range
|
||||
|
||||
|
||||
_directive_regex = re.compile(r'\.\. \S+::')
|
||||
_field_parens_regex = re.compile(r'\s*(\w+)\s*\(\s*(.+?)\s*\)')
|
||||
|
||||
|
||||
class GoogleDocstring(object):
|
||||
"""Parse Google style docstrings.
|
||||
|
||||
Convert Google style docstrings to reStructuredText.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
docstring : str or list of str
|
||||
The docstring to parse, given either as a string or split into
|
||||
individual lines.
|
||||
config : sphinx.ext.napoleon.Config or sphinx.config.Config, optional
|
||||
The configuration settings to use. If not given, defaults to the
|
||||
config object on `app`; or if `app` is not given defaults to the
|
||||
a new `sphinx.ext.napoleon.Config` object.
|
||||
|
||||
See Also
|
||||
--------
|
||||
:class:`sphinx.ext.napoleon.Config`
|
||||
|
||||
Other Parameters
|
||||
----------------
|
||||
app : sphinx.application.Sphinx, optional
|
||||
Application object representing the Sphinx process.
|
||||
what : str, optional
|
||||
A string specifying the type of the object to which the docstring
|
||||
belongs. Valid values: "module", "class", "exception", "function",
|
||||
"method", "attribute".
|
||||
name : str, optional
|
||||
The fully qualified name of the object.
|
||||
obj : module, class, exception, function, method, or attribute
|
||||
The object to which the docstring belongs.
|
||||
options : sphinx.ext.autodoc.Options, optional
|
||||
The options given to the directive: an object with attributes
|
||||
inherited_members, undoc_members, show_inheritance and noindex that
|
||||
are True if the flag option of same name was given to the auto
|
||||
directive.
|
||||
|
||||
Example
|
||||
-------
|
||||
>>> from sphinx.ext.napoleon import Config
|
||||
>>> config = Config(napoleon_use_param=True, napoleon_use_rtype=True)
|
||||
>>> docstring = '''One line summary.
|
||||
...
|
||||
... Extended description.
|
||||
...
|
||||
... Args:
|
||||
... arg1(int): Description of `arg1`
|
||||
... arg2(str): Description of `arg2`
|
||||
... Returns:
|
||||
... str: Description of return value.
|
||||
... '''
|
||||
>>> print(GoogleDocstring(docstring, config))
|
||||
One line summary.
|
||||
<BLANKLINE>
|
||||
Extended description.
|
||||
<BLANKLINE>
|
||||
:param arg1: Description of `arg1`
|
||||
:type arg1: int
|
||||
:param arg2: Description of `arg2`
|
||||
:type arg2: str
|
||||
<BLANKLINE>
|
||||
:returns: Description of return value.
|
||||
:rtype: str
|
||||
|
||||
"""
|
||||
def __init__(self, docstring, config=None, app=None, what='', name='',
|
||||
obj=None, options=None):
|
||||
self._config = config
|
||||
self._app = app
|
||||
if not self._config:
|
||||
from sphinx.ext.napoleon import Config
|
||||
self._config = self._app and self._app.config or Config()
|
||||
self._what = what
|
||||
self._name = name
|
||||
self._obj = obj
|
||||
self._opt = options
|
||||
if isinstance(docstring, basestring):
|
||||
docstring = docstring.splitlines()
|
||||
self._lines = docstring
|
||||
self._line_iter = modify_iter(docstring, modifier=lambda s: s.rstrip())
|
||||
self._parsed_lines = []
|
||||
self._is_in_section = False
|
||||
self._section_indent = 0
|
||||
if not hasattr(self, '_directive_sections'):
|
||||
self._directive_sections = []
|
||||
if not hasattr(self, '_sections'):
|
||||
self._sections = {
|
||||
'args': self._parse_parameters_section,
|
||||
'arguments': self._parse_parameters_section,
|
||||
'attributes': self._parse_attributes_section,
|
||||
'example': self._parse_examples_section,
|
||||
'examples': self._parse_examples_section,
|
||||
'keyword args': self._parse_keyword_arguments_section,
|
||||
'keyword arguments': self._parse_keyword_arguments_section,
|
||||
'methods': self._parse_methods_section,
|
||||
'note': self._parse_note_section,
|
||||
'notes': self._parse_notes_section,
|
||||
'other parameters': self._parse_other_parameters_section,
|
||||
'parameters': self._parse_parameters_section,
|
||||
'return': self._parse_returns_section,
|
||||
'returns': self._parse_returns_section,
|
||||
'raises': self._parse_raises_section,
|
||||
'references': self._parse_references_section,
|
||||
'see also': self._parse_see_also_section,
|
||||
'warning': self._parse_warning_section,
|
||||
'warnings': self._parse_warning_section,
|
||||
'warns': self._parse_warns_section,
|
||||
'yields': self._parse_yields_section,
|
||||
}
|
||||
self._parse()
|
||||
|
||||
def __str__(self):
|
||||
"""Return the parsed docstring in reStructuredText format.
|
||||
|
||||
Returns
|
||||
-------
|
||||
str
|
||||
UTF-8 encoded version of the docstring.
|
||||
|
||||
"""
|
||||
if sys.version_info[0] >= 3:
|
||||
return self.__unicode__()
|
||||
else:
|
||||
return self.__unicode__().encode('utf8')
|
||||
|
||||
def __unicode__(self):
|
||||
"""Return the parsed docstring in reStructuredText format.
|
||||
|
||||
Returns
|
||||
-------
|
||||
unicode
|
||||
Unicode version of the docstring.
|
||||
|
||||
"""
|
||||
return u'\n'.join(self.lines())
|
||||
|
||||
def lines(self):
|
||||
"""Return the parsed lines of the docstring in reStructuredText format.
|
||||
|
||||
Returns
|
||||
-------
|
||||
list of str
|
||||
The lines of the docstring in a list.
|
||||
|
||||
"""
|
||||
return self._parsed_lines
|
||||
|
||||
def _consume_indented_block(self, indent=1):
|
||||
lines = []
|
||||
line = self._line_iter.peek()
|
||||
while(not self._is_section_break()
|
||||
and (not line or self._is_indented(line, indent))):
|
||||
lines.append(self._line_iter.next())
|
||||
line = self._line_iter.peek()
|
||||
return lines
|
||||
|
||||
def _consume_contiguous(self):
|
||||
lines = []
|
||||
while (self._line_iter.has_next()
|
||||
and self._line_iter.peek()
|
||||
and not self._is_section_header()):
|
||||
lines.append(self._line_iter.next())
|
||||
return lines
|
||||
|
||||
def _consume_empty(self):
|
||||
lines = []
|
||||
line = self._line_iter.peek()
|
||||
while self._line_iter.has_next() and not line:
|
||||
lines.append(self._line_iter.next())
|
||||
line = self._line_iter.peek()
|
||||
return lines
|
||||
|
||||
def _consume_field(self, parse_type=True, prefer_type=False):
|
||||
line = self._line_iter.next()
|
||||
_name, _, _desc = line.partition(':')
|
||||
_name, _type, _desc = _name.strip(), '', _desc.strip()
|
||||
match = _field_parens_regex.match(_name)
|
||||
if parse_type and match:
|
||||
_name = match.group(1)
|
||||
_type = match.group(2)
|
||||
if prefer_type and not _type:
|
||||
_type, _name = _name, _type
|
||||
indent = self._get_indent(line) + 1
|
||||
_desc = [_desc] + self._dedent(self._consume_indented_block(indent))
|
||||
_desc = self.__class__(_desc, self._config).lines()
|
||||
return _name, _type, _desc
|
||||
|
||||
def _consume_fields(self, parse_type=True, prefer_type=False):
|
||||
self._consume_empty()
|
||||
fields = []
|
||||
while not self._is_section_break():
|
||||
_name, _type, _desc = self._consume_field(parse_type, prefer_type)
|
||||
if _name or _type or _desc:
|
||||
fields.append((_name, _type, _desc,))
|
||||
return fields
|
||||
|
||||
def _consume_returns_section(self):
|
||||
lines = self._dedent(self._consume_to_next_section())
|
||||
if lines:
|
||||
if ':' in lines[0]:
|
||||
_type, _, _desc = lines[0].partition(':')
|
||||
_name, _type, _desc = '', _type.strip(), _desc.strip()
|
||||
match = _field_parens_regex.match(_type)
|
||||
if match:
|
||||
_name = match.group(1)
|
||||
_type = match.group(2)
|
||||
lines[0] = _desc
|
||||
_desc = lines
|
||||
else:
|
||||
_name, _type, _desc = '', '', lines
|
||||
_desc = self.__class__(_desc, self._config).lines()
|
||||
return [(_name, _type, _desc,)]
|
||||
else:
|
||||
return []
|
||||
|
||||
def _consume_section_header(self):
|
||||
section = self._line_iter.next()
|
||||
stripped_section = section.strip(':')
|
||||
if stripped_section.lower() in self._sections:
|
||||
section = stripped_section
|
||||
return section
|
||||
|
||||
def _consume_to_next_section(self):
|
||||
self._consume_empty()
|
||||
lines = []
|
||||
while not self._is_section_break():
|
||||
lines.append(self._line_iter.next())
|
||||
return lines + self._consume_empty()
|
||||
|
||||
def _dedent(self, lines, full=False):
|
||||
if full:
|
||||
return [line.lstrip() for line in lines]
|
||||
else:
|
||||
min_indent = self._get_min_indent(lines)
|
||||
return [line[min_indent:] for line in lines]
|
||||
|
||||
def _format_admonition(self, admonition, lines):
|
||||
lines = self._strip_empty(lines)
|
||||
if len(lines) == 1:
|
||||
return ['.. %s:: %s' % (admonition, lines[0].strip()), '']
|
||||
elif lines:
|
||||
lines = self._indent(self._dedent(lines), 3)
|
||||
return ['.. %s::' % admonition, ''] + lines + ['']
|
||||
else:
|
||||
return ['.. %s::' % admonition, '']
|
||||
|
||||
def _format_block(self, prefix, lines, padding=None):
|
||||
if lines:
|
||||
if padding is None:
|
||||
padding = ' ' * len(prefix)
|
||||
result_lines = []
|
||||
for i, line in enumerate(lines):
|
||||
if line:
|
||||
if i == 0:
|
||||
result_lines.append(prefix + line)
|
||||
else:
|
||||
result_lines.append(padding + line)
|
||||
else:
|
||||
result_lines.append('')
|
||||
return result_lines
|
||||
else:
|
||||
return [prefix]
|
||||
|
||||
def _format_field(self, _name, _type, _desc):
|
||||
separator = any([s for s in _desc]) and ' --' or ''
|
||||
if _name:
|
||||
if _type:
|
||||
field = ['**%s** (*%s*)%s' % (_name, _type, separator)]
|
||||
else:
|
||||
field = ['**%s**%s' % (_name, separator)]
|
||||
elif _type:
|
||||
field = ['*%s*%s' % (_type, separator)]
|
||||
else:
|
||||
field = []
|
||||
return field + _desc
|
||||
|
||||
def _format_fields(self, field_type, fields):
|
||||
field_type = ':%s:' % field_type.strip()
|
||||
padding = ' ' * len(field_type)
|
||||
multi = len(fields) > 1
|
||||
lines = []
|
||||
for _name, _type, _desc in fields:
|
||||
field = self._format_field(_name, _type, _desc)
|
||||
if multi:
|
||||
if lines:
|
||||
lines.extend(self._format_block(padding + ' * ', field))
|
||||
else:
|
||||
lines.extend(self._format_block(field_type + ' * ', field))
|
||||
else:
|
||||
lines.extend(self._format_block(field_type + ' ', field))
|
||||
return lines
|
||||
|
||||
def _get_current_indent(self, peek_ahead=0):
|
||||
line = self._line_iter.peek(peek_ahead + 1)[peek_ahead]
|
||||
while line != self._line_iter.sentinel:
|
||||
if line:
|
||||
return self._get_indent(line)
|
||||
peek_ahead += 1
|
||||
line = self._line_iter.peek(peek_ahead + 1)[peek_ahead]
|
||||
return 0
|
||||
|
||||
def _get_indent(self, line):
|
||||
for i, s in enumerate(line):
|
||||
if not s.isspace():
|
||||
return i
|
||||
return len(line)
|
||||
|
||||
def _get_min_indent(self, lines):
|
||||
min_indent = None
|
||||
for line in lines:
|
||||
if line:
|
||||
indent = self._get_indent(line)
|
||||
if min_indent is None:
|
||||
min_indent = indent
|
||||
elif indent < min_indent:
|
||||
min_indent = indent
|
||||
return min_indent or 0
|
||||
|
||||
def _indent(self, lines, n=4):
|
||||
return [(' ' * n) + line for line in lines]
|
||||
|
||||
def _is_indented(self, line, indent=1):
|
||||
for i, s in enumerate(line):
|
||||
if i >= indent:
|
||||
return True
|
||||
elif not s.isspace():
|
||||
return False
|
||||
return False
|
||||
|
||||
def _is_section_header(self):
|
||||
section = self._line_iter.peek().lower()
|
||||
if section.strip(':') in self._sections:
|
||||
header_indent = self._get_indent(section)
|
||||
section_indent = self._get_current_indent(peek_ahead=1)
|
||||
return section_indent > header_indent
|
||||
elif self._directive_sections:
|
||||
if _directive_regex.match(section):
|
||||
for directive_section in self._directive_sections:
|
||||
if section.startswith(directive_section):
|
||||
return True
|
||||
return False
|
||||
|
||||
def _is_section_break(self):
|
||||
line = self._line_iter.peek()
|
||||
return (not self._line_iter.has_next()
|
||||
or self._is_section_header()
|
||||
or (self._is_in_section
|
||||
and line
|
||||
and not self._is_indented(line, self._section_indent)))
|
||||
|
||||
def _parse(self):
|
||||
self._parsed_lines = self._consume_empty()
|
||||
while self._line_iter.has_next():
|
||||
if self._is_section_header():
|
||||
try:
|
||||
section = self._consume_section_header()
|
||||
self._is_in_section = True
|
||||
self._section_indent = self._get_current_indent()
|
||||
if _directive_regex.match(section):
|
||||
lines = [section] + self._consume_to_next_section()
|
||||
else:
|
||||
lines = self._sections[section.lower()](section)
|
||||
finally:
|
||||
self._is_in_section = False
|
||||
self._section_indent = 0
|
||||
else:
|
||||
if not self._parsed_lines:
|
||||
lines = self._consume_contiguous() + self._consume_empty()
|
||||
else:
|
||||
lines = self._consume_to_next_section()
|
||||
self._parsed_lines.extend(lines)
|
||||
|
||||
def _parse_attributes_section(self, section):
|
||||
lines = []
|
||||
for _name, _type, _desc in self._consume_fields():
|
||||
if self._config.napoleon_use_ivar:
|
||||
field = ':ivar %s: ' % _name
|
||||
lines.extend(self._format_block(field, _desc))
|
||||
if _type:
|
||||
lines.append(':vartype %s: %s' % (_name, _type))
|
||||
else:
|
||||
lines.append('.. attribute:: ' + _name)
|
||||
if _type:
|
||||
lines.append(' :annotation: ' + _type)
|
||||
if _desc:
|
||||
lines.extend([''] + self._indent(_desc, 3))
|
||||
lines.append('')
|
||||
if self._config.napoleon_use_ivar:
|
||||
lines.append('')
|
||||
return lines
|
||||
|
||||
def _parse_examples_section(self, section):
|
||||
use_admonition = self._config.napoleon_use_admonition_for_examples
|
||||
return self._parse_generic_section(section, use_admonition)
|
||||
|
||||
def _parse_generic_section(self, section, use_admonition):
|
||||
lines = self._strip_empty(self._consume_to_next_section())
|
||||
lines = self._dedent(lines)
|
||||
if use_admonition:
|
||||
header = '.. admonition:: %s' % section
|
||||
lines = self._indent(lines, 3)
|
||||
else:
|
||||
header = '.. rubric:: %s' % section
|
||||
if lines:
|
||||
return [header, ''] + lines + ['']
|
||||
else:
|
||||
return [header, '']
|
||||
|
||||
def _parse_keyword_arguments_section(self, section):
|
||||
return self._format_fields('Keyword Arguments', self._consume_fields())
|
||||
|
||||
def _parse_methods_section(self, section):
|
||||
lines = []
|
||||
for _name, _, _desc in self._consume_fields(parse_type=False):
|
||||
lines.append('.. method:: %s' % _name)
|
||||
if _desc:
|
||||
lines.extend([''] + self._indent(_desc, 3))
|
||||
lines.append('')
|
||||
return lines
|
||||
|
||||
def _parse_note_section(self, section):
|
||||
lines = self._consume_to_next_section()
|
||||
return self._format_admonition('note', lines)
|
||||
|
||||
def _parse_notes_section(self, section):
|
||||
use_admonition = self._config.napoleon_use_admonition_for_notes
|
||||
return self._parse_generic_section('Notes', use_admonition)
|
||||
|
||||
def _parse_other_parameters_section(self, section):
|
||||
return self._format_fields('Other Parameters', self._consume_fields())
|
||||
|
||||
def _parse_parameters_section(self, section):
|
||||
fields = self._consume_fields()
|
||||
if self._config.napoleon_use_param:
|
||||
lines = []
|
||||
for _name, _type, _desc in fields:
|
||||
field = ':param %s: ' % _name
|
||||
lines.extend(self._format_block(field, _desc))
|
||||
if _type:
|
||||
lines.append(':type %s: %s' % (_name, _type))
|
||||
return lines + ['']
|
||||
else:
|
||||
return self._format_fields('Parameters', fields)
|
||||
|
||||
def _parse_raises_section(self, section):
|
||||
fields = self._consume_fields()
|
||||
field_type = ':raises:'
|
||||
padding = ' ' * len(field_type)
|
||||
multi = len(fields) > 1
|
||||
lines = []
|
||||
for _name, _type, _desc in fields:
|
||||
sep = _desc and ' -- ' or ''
|
||||
if _name:
|
||||
if ' ' in _name:
|
||||
_name = '**%s**' % _name
|
||||
else:
|
||||
_name = ':exc:`%s`' % _name
|
||||
if _type:
|
||||
field = ['%s (*%s*)%s' % (_name, _type, sep)]
|
||||
else:
|
||||
field = ['%s%s' % (_name, sep)]
|
||||
elif _type:
|
||||
field = ['*%s*%s' % (_type, sep)]
|
||||
else:
|
||||
field = []
|
||||
field = field + _desc
|
||||
if multi:
|
||||
if lines:
|
||||
lines.extend(self._format_block(padding + ' * ', field))
|
||||
else:
|
||||
lines.extend(self._format_block(field_type + ' * ', field))
|
||||
else:
|
||||
lines.extend(self._format_block(field_type + ' ', field))
|
||||
return lines
|
||||
|
||||
def _parse_references_section(self, section):
|
||||
use_admonition = self._config.napoleon_use_admonition_for_references
|
||||
return self._parse_generic_section('References', use_admonition)
|
||||
|
||||
def _parse_returns_section(self, section):
|
||||
fields = self._consume_returns_section()
|
||||
multi = len(fields) > 1
|
||||
if multi:
|
||||
use_rtype = False
|
||||
else:
|
||||
use_rtype = self._config.napoleon_use_rtype
|
||||
|
||||
lines = []
|
||||
for _name, _type, _desc in fields:
|
||||
if use_rtype:
|
||||
field = self._format_field(_name, '', _desc)
|
||||
else:
|
||||
field = self._format_field(_name, _type, _desc)
|
||||
|
||||
if multi:
|
||||
if lines:
|
||||
lines.extend(self._format_block(' * ', field))
|
||||
else:
|
||||
lines.extend(self._format_block(':returns: * ', field))
|
||||
else:
|
||||
lines.extend(self._format_block(':returns: ', field))
|
||||
if _type and use_rtype:
|
||||
lines.append(':rtype: %s' % _type)
|
||||
return lines
|
||||
|
||||
def _parse_see_also_section(self, section):
|
||||
lines = self._consume_to_next_section()
|
||||
return self._format_admonition('seealso', lines)
|
||||
|
||||
def _parse_warning_section(self, section):
|
||||
lines = self._consume_to_next_section()
|
||||
return self._format_admonition('warning', lines)
|
||||
|
||||
def _parse_warns_section(self, section):
|
||||
return self._format_fields('Warns', self._consume_fields())
|
||||
|
||||
def _parse_yields_section(self, section):
|
||||
fields = self._consume_fields(prefer_type=True)
|
||||
return self._format_fields('Yields', fields)
|
||||
|
||||
def _strip_empty(self, lines):
|
||||
if lines:
|
||||
start = -1
|
||||
for i, line in enumerate(lines):
|
||||
if line:
|
||||
start = i
|
||||
break
|
||||
if start == -1:
|
||||
lines = []
|
||||
end = -1
|
||||
for i in reversed(xrange(len(lines))):
|
||||
line = lines[i]
|
||||
if line:
|
||||
end = i
|
||||
break
|
||||
if start > 0 or end + 1 < len(lines):
|
||||
lines = lines[start:end + 1]
|
||||
return lines
|
||||
|
||||
|
||||
class NumpyDocstring(GoogleDocstring):
|
||||
"""Parse NumPy style docstrings.
|
||||
|
||||
Convert NumPy style docstrings to reStructuredText.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
docstring : str or list of str
|
||||
The docstring to parse, given either as a string or split into
|
||||
individual lines.
|
||||
config : sphinx.ext.napoleon.Config or sphinx.config.Config, optional
|
||||
The configuration settings to use. If not given, defaults to the
|
||||
config object on `app`; or if `app` is not given defaults to the
|
||||
a new `sphinx.ext.napoleon.Config` object.
|
||||
|
||||
See Also
|
||||
--------
|
||||
:class:`sphinx.ext.napoleon.Config`
|
||||
|
||||
Other Parameters
|
||||
----------------
|
||||
app : sphinx.application.Sphinx, optional
|
||||
Application object representing the Sphinx process.
|
||||
what : str, optional
|
||||
A string specifying the type of the object to which the docstring
|
||||
belongs. Valid values: "module", "class", "exception", "function",
|
||||
"method", "attribute".
|
||||
name : str, optional
|
||||
The fully qualified name of the object.
|
||||
obj : module, class, exception, function, method, or attribute
|
||||
The object to which the docstring belongs.
|
||||
options : sphinx.ext.autodoc.Options, optional
|
||||
The options given to the directive: an object with attributes
|
||||
inherited_members, undoc_members, show_inheritance and noindex that
|
||||
are True if the flag option of same name was given to the auto
|
||||
directive.
|
||||
|
||||
Example
|
||||
-------
|
||||
>>> from sphinx.ext.napoleon import Config
|
||||
>>> config = Config(napoleon_use_param=True, napoleon_use_rtype=True)
|
||||
>>> docstring = '''One line summary.
|
||||
...
|
||||
... Extended description.
|
||||
...
|
||||
... Parameters
|
||||
... ----------
|
||||
... arg1 : int
|
||||
... Description of `arg1`
|
||||
... arg2 : str
|
||||
... Description of `arg2`
|
||||
... Returns
|
||||
... -------
|
||||
... str
|
||||
... Description of return value.
|
||||
... '''
|
||||
>>> print(NumpyDocstring(docstring, config))
|
||||
One line summary.
|
||||
<BLANKLINE>
|
||||
Extended description.
|
||||
<BLANKLINE>
|
||||
:param arg1: Description of `arg1`
|
||||
:type arg1: int
|
||||
:param arg2: Description of `arg2`
|
||||
:type arg2: str
|
||||
<BLANKLINE>
|
||||
:returns: Description of return value.
|
||||
:rtype: str
|
||||
|
||||
Methods
|
||||
-------
|
||||
__str__()
|
||||
Return the parsed docstring in reStructuredText format.
|
||||
|
||||
Returns
|
||||
-------
|
||||
str
|
||||
UTF-8 encoded version of the docstring.
|
||||
|
||||
__unicode__()
|
||||
Return the parsed docstring in reStructuredText format.
|
||||
|
||||
Returns
|
||||
-------
|
||||
unicode
|
||||
Unicode version of the docstring.
|
||||
|
||||
lines()
|
||||
Return the parsed lines of the docstring in reStructuredText format.
|
||||
|
||||
Returns
|
||||
-------
|
||||
list of str
|
||||
The lines of the docstring in a list.
|
||||
|
||||
"""
|
||||
def __init__(self, docstring, config=None, app=None, what='', name='',
|
||||
obj=None, options=None):
|
||||
self._directive_sections = ['.. index::']
|
||||
super(NumpyDocstring, self).__init__(docstring, config, app, what,
|
||||
name, obj, options)
|
||||
|
||||
def _consume_field(self, parse_type=True, prefer_type=False):
|
||||
line = self._line_iter.next()
|
||||
if parse_type:
|
||||
_name, _, _type = line.partition(':')
|
||||
else:
|
||||
_name, _type = line, ''
|
||||
_name, _type = _name.strip(), _type.strip()
|
||||
if prefer_type and not _type:
|
||||
_type, _name = _name, _type
|
||||
indent = self._get_indent(line)
|
||||
_desc = self._dedent(self._consume_indented_block(indent + 1))
|
||||
_desc = self.__class__(_desc, self._config).lines()
|
||||
return _name, _type, _desc
|
||||
|
||||
def _consume_returns_section(self):
|
||||
return self._consume_fields(prefer_type=True)
|
||||
|
||||
def _consume_section_header(self):
|
||||
section = self._line_iter.next()
|
||||
if not _directive_regex.match(section):
|
||||
# Consume the header underline
|
||||
self._line_iter.next()
|
||||
return section
|
||||
|
||||
def _is_section_break(self):
|
||||
line1, line2 = self._line_iter.peek(2)
|
||||
return (not self._line_iter.has_next()
|
||||
or self._is_section_header()
|
||||
or ['', ''] == [line1, line2]
|
||||
or (self._is_in_section
|
||||
and line1
|
||||
and not self._is_indented(line1, self._section_indent)))
|
||||
|
||||
def _is_section_header(self):
|
||||
section, underline = self._line_iter.peek(2)
|
||||
section = section.lower()
|
||||
if section in self._sections and isinstance(underline, basestring):
|
||||
pattern = r'[=\-`:\'"~^_*+#<>]{' + str(len(section)) + r'}$'
|
||||
return bool(re.match(pattern, underline))
|
||||
elif self._directive_sections:
|
||||
if _directive_regex.match(section):
|
||||
for directive_section in self._directive_sections:
|
||||
if section.startswith(directive_section):
|
||||
return True
|
||||
return False
|
244
sphinx/ext/napoleon/iterators.py
Normal file
244
sphinx/ext/napoleon/iterators.py
Normal file
@ -0,0 +1,244 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
sphinx.ext.napoleon.iterators
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
||||
A collection of helpful iterators.
|
||||
|
||||
|
||||
:copyright: Copyright 2007-2014 by the Sphinx team, see AUTHORS.
|
||||
:license: BSD, see LICENSE for details.
|
||||
"""
|
||||
|
||||
import collections
|
||||
import sys
|
||||
|
||||
|
||||
if sys.version_info[0] >= 3:
|
||||
callable = lambda o: hasattr(o, '__call__')
|
||||
|
||||
|
||||
class peek_iter(object):
|
||||
"""An iterator object that supports peeking ahead.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
o : iterable or callable
|
||||
`o` is interpreted very differently depending on the presence of
|
||||
`sentinel`.
|
||||
|
||||
If `sentinel` is not given, then `o` must be a collection object
|
||||
which supports either the iteration protocol or the sequence protocol.
|
||||
|
||||
If `sentinel` is given, then `o` must be a callable object.
|
||||
|
||||
sentinel : any value, optional
|
||||
If given, the iterator will call `o` with no arguments for each
|
||||
call to its `next` method; if the value returned is equal to
|
||||
`sentinel`, :exc:`StopIteration` will be raised, otherwise the
|
||||
value will be returned.
|
||||
|
||||
See Also
|
||||
--------
|
||||
`peek_iter` can operate as a drop in replacement for the built-in
|
||||
`iter <http://docs.python.org/2/library/functions.html#iter>`_ function.
|
||||
|
||||
Attributes
|
||||
----------
|
||||
sentinel
|
||||
The value used to indicate the iterator is exhausted. If `sentinel`
|
||||
was not given when the `peek_iter` was instantiated, then it will
|
||||
be set to a new object instance: ``object()``.
|
||||
|
||||
"""
|
||||
def __init__(self, *args):
|
||||
"""__init__(o, sentinel=None)"""
|
||||
self._iterable = iter(*args)
|
||||
self._cache = collections.deque()
|
||||
if len(args) == 2:
|
||||
self.sentinel = args[1]
|
||||
else:
|
||||
self.sentinel = object()
|
||||
|
||||
def __iter__(self):
|
||||
return self
|
||||
|
||||
def __next__(self, n=None):
|
||||
# note: prevent 2to3 to transform self.next() in next(self) which
|
||||
# causes an infinite loop !
|
||||
return getattr(self, 'next')(n)
|
||||
|
||||
def _fillcache(self, n):
|
||||
"""Cache `n` items. If `n` is 0 or None, then 1 item is cached."""
|
||||
if not n:
|
||||
n = 1
|
||||
try:
|
||||
while len(self._cache) < n:
|
||||
self._cache.append(self._iterable.next())
|
||||
except StopIteration:
|
||||
while len(self._cache) < n:
|
||||
self._cache.append(self.sentinel)
|
||||
|
||||
def has_next(self):
|
||||
"""Determine if iterator is exhausted.
|
||||
|
||||
Returns
|
||||
-------
|
||||
bool
|
||||
True if iterator has more items, False otherwise.
|
||||
|
||||
Note
|
||||
----
|
||||
Will never raise :exc:`StopIteration`.
|
||||
|
||||
"""
|
||||
return self.peek() != self.sentinel
|
||||
|
||||
def next(self, n=None):
|
||||
"""Get the next item or `n` items of the iterator.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
n : int or None
|
||||
The number of items to retrieve. Defaults to None.
|
||||
|
||||
Returns
|
||||
-------
|
||||
item or list of items
|
||||
The next item or `n` items of the iterator. If `n` is None, the
|
||||
item itself is returned. If `n` is an int, the items will be
|
||||
returned in a list. If `n` is 0, an empty list is returned.
|
||||
|
||||
Raises
|
||||
------
|
||||
StopIteration
|
||||
Raised if the iterator is exhausted, even if `n` is 0.
|
||||
|
||||
"""
|
||||
self._fillcache(n)
|
||||
if not n:
|
||||
if self._cache[0] == self.sentinel:
|
||||
raise StopIteration
|
||||
if n is None:
|
||||
result = self._cache.popleft()
|
||||
else:
|
||||
result = []
|
||||
else:
|
||||
if self._cache[n - 1] == self.sentinel:
|
||||
raise StopIteration
|
||||
result = [self._cache.popleft() for i in range(n)]
|
||||
return result
|
||||
|
||||
def peek(self, n=None):
|
||||
"""Preview the next item or `n` items of the iterator.
|
||||
|
||||
The iterator is not advanced when peek is called.
|
||||
|
||||
Returns
|
||||
-------
|
||||
item or list of items
|
||||
The next item or `n` items of the iterator. If `n` is None, the
|
||||
item itself is returned. If `n` is an int, the items will be
|
||||
returned in a list. If `n` is 0, an empty list is returned.
|
||||
|
||||
If the iterator is exhausted, `peek_iter.sentinel` is returned,
|
||||
or placed as the last item in the returned list.
|
||||
|
||||
Note
|
||||
----
|
||||
Will never raise :exc:`StopIteration`.
|
||||
|
||||
"""
|
||||
self._fillcache(n)
|
||||
if n is None:
|
||||
result = self._cache[0]
|
||||
else:
|
||||
result = [self._cache[i] for i in range(n)]
|
||||
return result
|
||||
|
||||
|
||||
class modify_iter(peek_iter):
|
||||
"""An iterator object that supports modifying items as they are returned.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
o : iterable or callable
|
||||
`o` is interpreted very differently depending on the presence of
|
||||
`sentinel`.
|
||||
|
||||
If `sentinel` is not given, then `o` must be a collection object
|
||||
which supports either the iteration protocol or the sequence protocol.
|
||||
|
||||
If `sentinel` is given, then `o` must be a callable object.
|
||||
|
||||
sentinel : any value, optional
|
||||
If given, the iterator will call `o` with no arguments for each
|
||||
call to its `next` method; if the value returned is equal to
|
||||
`sentinel`, :exc:`StopIteration` will be raised, otherwise the
|
||||
value will be returned.
|
||||
|
||||
modifier : callable, optional
|
||||
The function that will be used to modify each item returned by the
|
||||
iterator. `modifier` should take a single argument and return a
|
||||
single value. Defaults to ``lambda x: x``.
|
||||
|
||||
If `sentinel` is not given, `modifier` must be passed as a keyword
|
||||
argument.
|
||||
|
||||
Attributes
|
||||
----------
|
||||
modifier : callable
|
||||
`modifier` is called with each item in `o` as it is iterated. The
|
||||
return value of `modifier` is returned in lieu of the item.
|
||||
|
||||
Values returned by `peek` as well as `next` are affected by
|
||||
`modifier`. However, `modify_iter.sentinel` is never passed through
|
||||
`modifier`; it will always be returned from `peek` unmodified.
|
||||
|
||||
Example
|
||||
-------
|
||||
>>> a = [" A list ",
|
||||
... " of strings ",
|
||||
... " with ",
|
||||
... " extra ",
|
||||
... " whitespace. "]
|
||||
>>> modifier = lambda s: s.strip().replace('with', 'without')
|
||||
>>> for s in modify_iter(a, modifier=modifier):
|
||||
... print('"%s"' % s)
|
||||
"A list"
|
||||
"of strings"
|
||||
"without"
|
||||
"extra"
|
||||
"whitespace."
|
||||
|
||||
"""
|
||||
def __init__(self, *args, **kwargs):
|
||||
"""__init__(o, sentinel=None, modifier=lambda x: x)"""
|
||||
if 'modifier' in kwargs:
|
||||
self.modifier = kwargs['modifier']
|
||||
elif len(args) > 2:
|
||||
self.modifier = args[2]
|
||||
args = args[:2]
|
||||
else:
|
||||
self.modifier = lambda x: x
|
||||
if not callable(self.modifier):
|
||||
raise TypeError('modify_iter(o, modifier): '
|
||||
'modifier must be callable')
|
||||
super(modify_iter, self).__init__(*args)
|
||||
|
||||
def _fillcache(self, n):
|
||||
"""Cache `n` modified items. If `n` is 0 or None, 1 item is cached.
|
||||
|
||||
Each item returned by the iterator is passed through the
|
||||
`modify_iter.modified` function before being cached.
|
||||
|
||||
"""
|
||||
if not n:
|
||||
n = 1
|
||||
try:
|
||||
while len(self._cache) < n:
|
||||
self._cache.append(self.modifier(self._iterable.next()))
|
||||
except StopIteration:
|
||||
while len(self._cache) < n:
|
||||
self._cache.append(self.sentinel)
|
@ -8,7 +8,8 @@ sys.path.append(os.path.abspath('..'))
|
||||
extensions = ['sphinx.ext.autodoc', 'sphinx.ext.jsmath', 'sphinx.ext.todo',
|
||||
'sphinx.ext.coverage', 'sphinx.ext.autosummary',
|
||||
'sphinx.ext.doctest', 'sphinx.ext.extlinks',
|
||||
'sphinx.ext.viewcode', 'sphinx.ext.oldcmarkup', 'ext']
|
||||
'sphinx.ext.viewcode', 'sphinx.ext.oldcmarkup',
|
||||
'sphinx.ext.napoleon', 'ext']
|
||||
|
||||
jsmath_path = 'dummy.js'
|
||||
|
||||
|
190
tests/test_napoleon.py
Normal file
190
tests/test_napoleon.py
Normal file
@ -0,0 +1,190 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
test_napoleon
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
Tests for :mod:`sphinx.ext.napoleon.__init__` module.
|
||||
|
||||
|
||||
:copyright: Copyright 2007-2014 by the Sphinx team, see AUTHORS.
|
||||
:license: BSD, see LICENSE for details.
|
||||
"""
|
||||
|
||||
from mock import Mock
|
||||
from sphinx.application import Sphinx
|
||||
from sphinx.ext.napoleon import (_process_docstring, _skip_member, Config,
|
||||
setup)
|
||||
from unittest import TestCase
|
||||
|
||||
|
||||
def _private_doc():
|
||||
"""module._private_doc.DOCSTRING"""
|
||||
pass
|
||||
|
||||
|
||||
def _private_undoc():
|
||||
pass
|
||||
|
||||
|
||||
def __special_doc__():
|
||||
"""module.__special_doc__.DOCSTRING"""
|
||||
pass
|
||||
|
||||
|
||||
def __special_undoc__():
|
||||
pass
|
||||
|
||||
|
||||
class SampleClass(object):
|
||||
def _private_doc(self):
|
||||
"""SampleClass._private_doc.DOCSTRING"""
|
||||
pass
|
||||
|
||||
def _private_undoc(self):
|
||||
pass
|
||||
|
||||
def __special_doc__(self):
|
||||
"""SampleClass.__special_doc__.DOCSTRING"""
|
||||
pass
|
||||
|
||||
def __special_undoc__(self):
|
||||
pass
|
||||
|
||||
|
||||
class SampleError(Exception):
|
||||
def _private_doc(self):
|
||||
"""SampleError._private_doc.DOCSTRING"""
|
||||
pass
|
||||
|
||||
def _private_undoc(self):
|
||||
pass
|
||||
|
||||
def __special_doc__(self):
|
||||
"""SampleError.__special_doc__.DOCSTRING"""
|
||||
pass
|
||||
|
||||
def __special_undoc__(self):
|
||||
pass
|
||||
|
||||
|
||||
class ProcessDocstringTest(TestCase):
|
||||
def test_modify_in_place(self):
|
||||
lines = ['Summary line.',
|
||||
'',
|
||||
'Args:',
|
||||
' arg1: arg1 description']
|
||||
app = Mock()
|
||||
app.config = Config()
|
||||
_process_docstring(app, 'class', 'SampleClass', SampleClass, Mock(),
|
||||
lines)
|
||||
|
||||
expected = ['Summary line.',
|
||||
'',
|
||||
':Parameters: **arg1** --',
|
||||
' arg1 description']
|
||||
self.assertEqual(expected, lines)
|
||||
|
||||
|
||||
class SetupTest(TestCase):
|
||||
def test_unknown_app_type(self):
|
||||
setup(object())
|
||||
|
||||
def test_add_config_values(self):
|
||||
app = Mock(Sphinx)
|
||||
setup(app)
|
||||
for name, (default, rebuild) in Config._config_values.items():
|
||||
has_config = False
|
||||
for method_name, args, kwargs in app.method_calls:
|
||||
if(method_name == 'add_config_value' and
|
||||
args[0] == name):
|
||||
has_config = True
|
||||
if not has_config:
|
||||
self.fail('Config value was not added to app %s' % name)
|
||||
|
||||
has_process_docstring = False
|
||||
has_skip_member = False
|
||||
for method_name, args, kwargs in app.method_calls:
|
||||
if method_name == 'connect':
|
||||
if(args[0] == 'autodoc-process-docstring' and
|
||||
args[1] == _process_docstring):
|
||||
has_process_docstring = True
|
||||
elif(args[0] == 'autodoc-skip-member' and
|
||||
args[1] == _skip_member):
|
||||
has_skip_member = True
|
||||
if not has_process_docstring:
|
||||
self.fail('autodoc-process-docstring never connected')
|
||||
if not has_skip_member:
|
||||
self.fail('autodoc-skip-member never connected')
|
||||
|
||||
|
||||
class SkipMemberTest(TestCase):
|
||||
def assertSkip(self, what, member, obj, expect_skip, config_name):
|
||||
skip = 'default skip'
|
||||
app = Mock()
|
||||
app.config = Config()
|
||||
setattr(app.config, config_name, True)
|
||||
if expect_skip:
|
||||
self.assertEqual(skip, _skip_member(app, what, member, obj, skip,
|
||||
Mock()))
|
||||
else:
|
||||
self.assertFalse(_skip_member(app, what, member, obj, skip,
|
||||
Mock()))
|
||||
setattr(app.config, config_name, False)
|
||||
self.assertEqual(skip, _skip_member(app, what, member, obj, skip,
|
||||
Mock()))
|
||||
|
||||
def test_class_private_doc(self):
|
||||
self.assertSkip('class', '_private_doc',
|
||||
SampleClass._private_doc, False,
|
||||
'napoleon_include_private_with_doc')
|
||||
|
||||
def test_class_private_undoc(self):
|
||||
self.assertSkip('class', '_private_undoc',
|
||||
SampleClass._private_undoc, True,
|
||||
'napoleon_include_private_with_doc')
|
||||
|
||||
def test_class_special_doc(self):
|
||||
self.assertSkip('class', '__special_doc__',
|
||||
SampleClass.__special_doc__, False,
|
||||
'napoleon_include_special_with_doc')
|
||||
|
||||
def test_class_special_undoc(self):
|
||||
self.assertSkip('class', '__special_undoc__',
|
||||
SampleClass.__special_undoc__, True,
|
||||
'napoleon_include_special_with_doc')
|
||||
|
||||
def test_exception_private_doc(self):
|
||||
self.assertSkip('exception', '_private_doc',
|
||||
SampleError._private_doc, False,
|
||||
'napoleon_include_private_with_doc')
|
||||
|
||||
def test_exception_private_undoc(self):
|
||||
self.assertSkip('exception', '_private_undoc',
|
||||
SampleError._private_undoc, True,
|
||||
'napoleon_include_private_with_doc')
|
||||
|
||||
def test_exception_special_doc(self):
|
||||
self.assertSkip('exception', '__special_doc__',
|
||||
SampleError.__special_doc__, False,
|
||||
'napoleon_include_special_with_doc')
|
||||
|
||||
def test_exception_special_undoc(self):
|
||||
self.assertSkip('exception', '__special_undoc__',
|
||||
SampleError.__special_undoc__, True,
|
||||
'napoleon_include_special_with_doc')
|
||||
|
||||
def test_module_private_doc(self):
|
||||
self.assertSkip('module', '_private_doc', _private_doc, False,
|
||||
'napoleon_include_private_with_doc')
|
||||
|
||||
def test_module_private_undoc(self):
|
||||
self.assertSkip('module', '_private_undoc', _private_undoc, True,
|
||||
'napoleon_include_private_with_doc')
|
||||
|
||||
def test_module_special_doc(self):
|
||||
self.assertSkip('module', '__special_doc__', __special_doc__, False,
|
||||
'napoleon_include_special_with_doc')
|
||||
|
||||
def test_module_special_undoc(self):
|
||||
self.assertSkip('module', '__special_undoc__', __special_undoc__, True,
|
||||
'napoleon_include_special_with_doc')
|
259
tests/test_napoleon_docstring.py
Normal file
259
tests/test_napoleon_docstring.py
Normal file
@ -0,0 +1,259 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
test_napoleon_docstring
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Tests for :mod:`sphinx.ext.napoleon.docstring` module.
|
||||
|
||||
|
||||
:copyright: Copyright 2007-2014 by the Sphinx team, see AUTHORS.
|
||||
:license: BSD, see LICENSE for details.
|
||||
"""
|
||||
|
||||
import textwrap
|
||||
from sphinx.ext.napoleon.docstring import GoogleDocstring, NumpyDocstring
|
||||
from unittest import TestCase
|
||||
|
||||
|
||||
class BaseDocstringTest(TestCase):
|
||||
pass
|
||||
|
||||
|
||||
class GoogleDocstringTest(BaseDocstringTest):
|
||||
docstrings = [(
|
||||
"""Single line summary""",
|
||||
"""Single line summary"""
|
||||
), (
|
||||
"""
|
||||
Single line summary
|
||||
|
||||
Extended description
|
||||
|
||||
""",
|
||||
"""
|
||||
Single line summary
|
||||
|
||||
Extended description
|
||||
"""
|
||||
), (
|
||||
"""
|
||||
Single line summary
|
||||
|
||||
Args:
|
||||
arg1(str):Extended
|
||||
description of arg1
|
||||
""",
|
||||
"""
|
||||
Single line summary
|
||||
|
||||
:Parameters: **arg1** (*str*) --
|
||||
Extended
|
||||
description of arg1"""
|
||||
), (
|
||||
"""
|
||||
Single line summary
|
||||
|
||||
Args:
|
||||
arg1(str):Extended
|
||||
description of arg1
|
||||
arg2 ( int ) : Extended
|
||||
description of arg2
|
||||
|
||||
Keyword Args:
|
||||
kwarg1(str):Extended
|
||||
description of kwarg1
|
||||
kwarg2 ( int ) : Extended
|
||||
description of kwarg2""",
|
||||
"""
|
||||
Single line summary
|
||||
|
||||
:Parameters: * **arg1** (*str*) --
|
||||
Extended
|
||||
description of arg1
|
||||
* **arg2** (*int*) --
|
||||
Extended
|
||||
description of arg2
|
||||
|
||||
:Keyword Arguments: * **kwarg1** (*str*) --
|
||||
Extended
|
||||
description of kwarg1
|
||||
* **kwarg2** (*int*) --
|
||||
Extended
|
||||
description of kwarg2"""
|
||||
), (
|
||||
"""
|
||||
Single line summary
|
||||
|
||||
Arguments:
|
||||
arg1(str):Extended
|
||||
description of arg1
|
||||
arg2 ( int ) : Extended
|
||||
description of arg2
|
||||
|
||||
Keyword Arguments:
|
||||
kwarg1(str):Extended
|
||||
description of kwarg1
|
||||
kwarg2 ( int ) : Extended
|
||||
description of kwarg2""",
|
||||
"""
|
||||
Single line summary
|
||||
|
||||
:Parameters: * **arg1** (*str*) --
|
||||
Extended
|
||||
description of arg1
|
||||
* **arg2** (*int*) --
|
||||
Extended
|
||||
description of arg2
|
||||
|
||||
:Keyword Arguments: * **kwarg1** (*str*) --
|
||||
Extended
|
||||
description of kwarg1
|
||||
* **kwarg2** (*int*) --
|
||||
Extended
|
||||
description of kwarg2"""
|
||||
), (
|
||||
"""
|
||||
Single line summary
|
||||
|
||||
Return:
|
||||
str:Extended
|
||||
description of return value
|
||||
""",
|
||||
"""
|
||||
Single line summary
|
||||
|
||||
:returns: *str* --
|
||||
Extended
|
||||
description of return value"""
|
||||
), (
|
||||
"""
|
||||
Single line summary
|
||||
|
||||
Returns:
|
||||
str:Extended
|
||||
description of return value
|
||||
""",
|
||||
"""
|
||||
Single line summary
|
||||
|
||||
:returns: *str* --
|
||||
Extended
|
||||
description of return value"""
|
||||
)]
|
||||
|
||||
def test_docstrings(self):
|
||||
for docstring, expected in self.docstrings:
|
||||
actual = str(GoogleDocstring(textwrap.dedent(docstring)))
|
||||
expected = textwrap.dedent(expected)
|
||||
self.assertEqual(expected, actual)
|
||||
|
||||
|
||||
class NumpyDocstringTest(BaseDocstringTest):
|
||||
docstrings = [(
|
||||
"""Single line summary""",
|
||||
"""Single line summary"""
|
||||
), (
|
||||
"""
|
||||
Single line summary
|
||||
|
||||
Extended description
|
||||
|
||||
""",
|
||||
"""
|
||||
Single line summary
|
||||
|
||||
Extended description
|
||||
"""
|
||||
), (
|
||||
"""
|
||||
Single line summary
|
||||
|
||||
Parameters
|
||||
----------
|
||||
arg1:str
|
||||
Extended
|
||||
description of arg1
|
||||
""",
|
||||
"""
|
||||
Single line summary
|
||||
|
||||
:Parameters: **arg1** (*str*) --
|
||||
Extended
|
||||
description of arg1"""
|
||||
), (
|
||||
"""
|
||||
Single line summary
|
||||
|
||||
Parameters
|
||||
----------
|
||||
arg1:str
|
||||
Extended
|
||||
description of arg1
|
||||
arg2 : int
|
||||
Extended
|
||||
description of arg2
|
||||
|
||||
Keyword Arguments
|
||||
-----------------
|
||||
kwarg1:str
|
||||
Extended
|
||||
description of kwarg1
|
||||
kwarg2 : int
|
||||
Extended
|
||||
description of kwarg2
|
||||
""",
|
||||
"""
|
||||
Single line summary
|
||||
|
||||
:Parameters: * **arg1** (*str*) --
|
||||
Extended
|
||||
description of arg1
|
||||
* **arg2** (*int*) --
|
||||
Extended
|
||||
description of arg2
|
||||
|
||||
:Keyword Arguments: * **kwarg1** (*str*) --
|
||||
Extended
|
||||
description of kwarg1
|
||||
* **kwarg2** (*int*) --
|
||||
Extended
|
||||
description of kwarg2"""
|
||||
), (
|
||||
"""
|
||||
Single line summary
|
||||
|
||||
Return
|
||||
------
|
||||
str
|
||||
Extended
|
||||
description of return value
|
||||
""",
|
||||
"""
|
||||
Single line summary
|
||||
|
||||
:returns: *str* --
|
||||
Extended
|
||||
description of return value"""
|
||||
), (
|
||||
"""
|
||||
Single line summary
|
||||
|
||||
Returns
|
||||
-------
|
||||
str
|
||||
Extended
|
||||
description of return value
|
||||
""",
|
||||
"""
|
||||
Single line summary
|
||||
|
||||
:returns: *str* --
|
||||
Extended
|
||||
description of return value"""
|
||||
)]
|
||||
|
||||
def test_docstrings(self):
|
||||
for docstring, expected in self.docstrings:
|
||||
actual = str(NumpyDocstring(textwrap.dedent(docstring)))
|
||||
expected = textwrap.dedent(expected)
|
||||
self.assertEqual(expected, actual)
|
346
tests/test_napoleon_iterators.py
Normal file
346
tests/test_napoleon_iterators.py
Normal file
@ -0,0 +1,346 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
test_napoleon_iterators
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Tests for :mod:`sphinx.ext.napoleon.iterators` module.
|
||||
|
||||
|
||||
:copyright: Copyright 2007-2014 by the Sphinx team, see AUTHORS.
|
||||
:license: BSD, see LICENSE for details.
|
||||
"""
|
||||
|
||||
from sphinx.ext.napoleon.iterators import peek_iter, modify_iter
|
||||
from unittest import TestCase
|
||||
|
||||
|
||||
class BaseIteratorsTest(TestCase):
|
||||
def assertEqualTwice(self, expected, func, *args):
|
||||
self.assertEqual(expected, func(*args))
|
||||
self.assertEqual(expected, func(*args))
|
||||
|
||||
def assertFalseTwice(self, func, *args):
|
||||
self.assertFalse(func(*args))
|
||||
self.assertFalse(func(*args))
|
||||
|
||||
def assertNext(self, it, expected, is_last):
|
||||
self.assertTrueTwice(it.has_next)
|
||||
self.assertEqualTwice(expected, it.peek)
|
||||
self.assertTrueTwice(it.has_next)
|
||||
self.assertEqualTwice(expected, it.peek)
|
||||
self.assertTrueTwice(it.has_next)
|
||||
self.assertEqual(expected, it.next())
|
||||
if is_last:
|
||||
self.assertFalseTwice(it.has_next)
|
||||
self.assertRaisesTwice(StopIteration, it.next)
|
||||
else:
|
||||
self.assertTrueTwice(it.has_next)
|
||||
|
||||
def assertRaisesTwice(self, exc, func, *args):
|
||||
self.assertRaises(exc, func, *args)
|
||||
self.assertRaises(exc, func, *args)
|
||||
|
||||
def assertTrueTwice(self, func, *args):
|
||||
self.assertTrue(func(*args))
|
||||
self.assertTrue(func(*args))
|
||||
|
||||
|
||||
class PeekIterTest(BaseIteratorsTest):
|
||||
def test_init_with_sentinel(self):
|
||||
a = iter(['1', '2', 'DONE'])
|
||||
sentinel = 'DONE'
|
||||
self.assertRaises(TypeError, peek_iter, a, sentinel)
|
||||
|
||||
def get_next():
|
||||
return next(a)
|
||||
it = peek_iter(get_next, sentinel)
|
||||
self.assertEqual(it.sentinel, sentinel)
|
||||
self.assertNext(it, '1', is_last=False)
|
||||
self.assertNext(it, '2', is_last=True)
|
||||
|
||||
def test_iter(self):
|
||||
a = ['1', '2', '3']
|
||||
it = peek_iter(a)
|
||||
self.assertTrue(it is it.__iter__())
|
||||
|
||||
a = []
|
||||
b = [i for i in peek_iter(a)]
|
||||
self.assertEqual([], b)
|
||||
|
||||
a = ['1']
|
||||
b = [i for i in peek_iter(a)]
|
||||
self.assertEqual(['1'], b)
|
||||
|
||||
a = ['1', '2']
|
||||
b = [i for i in peek_iter(a)]
|
||||
self.assertEqual(['1', '2'], b)
|
||||
|
||||
a = ['1', '2', '3']
|
||||
b = [i for i in peek_iter(a)]
|
||||
self.assertEqual(['1', '2', '3'], b)
|
||||
|
||||
def test_next_with_multi(self):
|
||||
a = []
|
||||
it = peek_iter(a)
|
||||
self.assertFalseTwice(it.has_next)
|
||||
self.assertRaisesTwice(StopIteration, it.next, 2)
|
||||
|
||||
a = ['1']
|
||||
it = peek_iter(a)
|
||||
self.assertTrueTwice(it.has_next)
|
||||
self.assertRaisesTwice(StopIteration, it.next, 2)
|
||||
self.assertTrueTwice(it.has_next)
|
||||
|
||||
a = ['1', '2']
|
||||
it = peek_iter(a)
|
||||
self.assertTrueTwice(it.has_next)
|
||||
self.assertEqual(['1', '2'], it.next(2))
|
||||
self.assertFalseTwice(it.has_next)
|
||||
|
||||
a = ['1', '2', '3']
|
||||
it = peek_iter(a)
|
||||
self.assertTrueTwice(it.has_next)
|
||||
self.assertEqual(['1', '2'], it.next(2))
|
||||
self.assertTrueTwice(it.has_next)
|
||||
self.assertRaisesTwice(StopIteration, it.next, 2)
|
||||
self.assertTrueTwice(it.has_next)
|
||||
|
||||
a = ['1', '2', '3', '4']
|
||||
it = peek_iter(a)
|
||||
self.assertTrueTwice(it.has_next)
|
||||
self.assertEqual(['1', '2'], it.next(2))
|
||||
self.assertTrueTwice(it.has_next)
|
||||
self.assertEqual(['3', '4'], it.next(2))
|
||||
self.assertFalseTwice(it.has_next)
|
||||
self.assertRaisesTwice(StopIteration, it.next, 2)
|
||||
self.assertFalseTwice(it.has_next)
|
||||
|
||||
def test_next_with_none(self):
|
||||
a = []
|
||||
it = peek_iter(a)
|
||||
self.assertFalseTwice(it.has_next)
|
||||
self.assertRaisesTwice(StopIteration, it.next)
|
||||
self.assertFalseTwice(it.has_next)
|
||||
|
||||
a = ['1']
|
||||
it = peek_iter(a)
|
||||
self.assertEqual('1', it.__next__())
|
||||
|
||||
a = ['1']
|
||||
it = peek_iter(a)
|
||||
self.assertNext(it, '1', is_last=True)
|
||||
|
||||
a = ['1', '2']
|
||||
it = peek_iter(a)
|
||||
self.assertNext(it, '1', is_last=False)
|
||||
self.assertNext(it, '2', is_last=True)
|
||||
|
||||
a = ['1', '2', '3']
|
||||
it = peek_iter(a)
|
||||
self.assertNext(it, '1', is_last=False)
|
||||
self.assertNext(it, '2', is_last=False)
|
||||
self.assertNext(it, '3', is_last=True)
|
||||
|
||||
def test_next_with_one(self):
|
||||
a = []
|
||||
it = peek_iter(a)
|
||||
self.assertFalseTwice(it.has_next)
|
||||
self.assertRaisesTwice(StopIteration, it.next, 1)
|
||||
|
||||
a = ['1']
|
||||
it = peek_iter(a)
|
||||
self.assertTrueTwice(it.has_next)
|
||||
self.assertEqual(['1'], it.next(1))
|
||||
self.assertFalseTwice(it.has_next)
|
||||
self.assertRaisesTwice(StopIteration, it.next, 1)
|
||||
|
||||
a = ['1', '2']
|
||||
it = peek_iter(a)
|
||||
self.assertTrueTwice(it.has_next)
|
||||
self.assertEqual(['1'], it.next(1))
|
||||
self.assertTrueTwice(it.has_next)
|
||||
self.assertEqual(['2'], it.next(1))
|
||||
self.assertFalseTwice(it.has_next)
|
||||
self.assertRaisesTwice(StopIteration, it.next, 1)
|
||||
|
||||
def test_next_with_zero(self):
|
||||
a = []
|
||||
it = peek_iter(a)
|
||||
self.assertFalseTwice(it.has_next)
|
||||
self.assertRaisesTwice(StopIteration, it.next, 0)
|
||||
|
||||
a = ['1']
|
||||
it = peek_iter(a)
|
||||
self.assertTrueTwice(it.has_next)
|
||||
self.assertEqualTwice([], it.next, 0)
|
||||
self.assertTrueTwice(it.has_next)
|
||||
self.assertEqualTwice([], it.next, 0)
|
||||
|
||||
a = ['1', '2']
|
||||
it = peek_iter(a)
|
||||
self.assertTrueTwice(it.has_next)
|
||||
self.assertEqualTwice([], it.next, 0)
|
||||
self.assertTrueTwice(it.has_next)
|
||||
self.assertEqualTwice([], it.next, 0)
|
||||
|
||||
def test_peek_with_multi(self):
|
||||
a = []
|
||||
it = peek_iter(a)
|
||||
self.assertFalseTwice(it.has_next)
|
||||
self.assertEqualTwice([it.sentinel, it.sentinel], it.peek, 2)
|
||||
self.assertFalseTwice(it.has_next)
|
||||
|
||||
a = ['1']
|
||||
it = peek_iter(a)
|
||||
self.assertTrueTwice(it.has_next)
|
||||
self.assertEqualTwice(['1', it.sentinel], it.peek, 2)
|
||||
self.assertTrueTwice(it.has_next)
|
||||
self.assertEqualTwice(['1', it.sentinel, it.sentinel], it.peek, 3)
|
||||
self.assertTrueTwice(it.has_next)
|
||||
|
||||
a = ['1', '2']
|
||||
it = peek_iter(a)
|
||||
self.assertTrueTwice(it.has_next)
|
||||
self.assertEqualTwice(['1', '2'], it.peek, 2)
|
||||
self.assertTrueTwice(it.has_next)
|
||||
self.assertEqualTwice(['1', '2', it.sentinel], it.peek, 3)
|
||||
self.assertTrueTwice(it.has_next)
|
||||
self.assertEqualTwice(['1', '2', it.sentinel, it.sentinel], it.peek, 4)
|
||||
self.assertTrueTwice(it.has_next)
|
||||
|
||||
a = ['1', '2', '3']
|
||||
it = peek_iter(a)
|
||||
self.assertTrueTwice(it.has_next)
|
||||
self.assertEqualTwice(['1', '2'], it.peek, 2)
|
||||
self.assertTrueTwice(it.has_next)
|
||||
self.assertEqualTwice(['1', '2', '3'], it.peek, 3)
|
||||
self.assertTrueTwice(it.has_next)
|
||||
self.assertEqualTwice(['1', '2', '3', it.sentinel], it.peek, 4)
|
||||
self.assertTrueTwice(it.has_next)
|
||||
self.assertEqual('1', it.next())
|
||||
self.assertTrueTwice(it.has_next)
|
||||
self.assertEqualTwice(['2', '3'], it.peek, 2)
|
||||
self.assertTrueTwice(it.has_next)
|
||||
self.assertEqualTwice(['2', '3', it.sentinel], it.peek, 3)
|
||||
self.assertTrueTwice(it.has_next)
|
||||
self.assertEqualTwice(['2', '3', it.sentinel, it.sentinel], it.peek, 4)
|
||||
self.assertTrueTwice(it.has_next)
|
||||
|
||||
def test_peek_with_none(self):
|
||||
a = []
|
||||
it = peek_iter(a)
|
||||
self.assertFalseTwice(it.has_next)
|
||||
self.assertEqualTwice(it.sentinel, it.peek)
|
||||
self.assertFalseTwice(it.has_next)
|
||||
|
||||
a = ['1']
|
||||
it = peek_iter(a)
|
||||
self.assertTrueTwice(it.has_next)
|
||||
self.assertEqualTwice('1', it.peek)
|
||||
self.assertEqual('1', it.next())
|
||||
self.assertFalseTwice(it.has_next)
|
||||
self.assertEqualTwice(it.sentinel, it.peek)
|
||||
self.assertFalseTwice(it.has_next)
|
||||
|
||||
a = ['1', '2']
|
||||
it = peek_iter(a)
|
||||
self.assertTrueTwice(it.has_next)
|
||||
self.assertEqualTwice('1', it.peek)
|
||||
self.assertEqual('1', it.next())
|
||||
self.assertTrueTwice(it.has_next)
|
||||
self.assertEqualTwice('2', it.peek)
|
||||
self.assertEqual('2', it.next())
|
||||
self.assertFalseTwice(it.has_next)
|
||||
self.assertEqualTwice(it.sentinel, it.peek)
|
||||
self.assertFalseTwice(it.has_next)
|
||||
|
||||
def test_peek_with_one(self):
|
||||
a = []
|
||||
it = peek_iter(a)
|
||||
self.assertFalseTwice(it.has_next)
|
||||
self.assertEqualTwice([it.sentinel], it.peek, 1)
|
||||
self.assertFalseTwice(it.has_next)
|
||||
|
||||
a = ['1']
|
||||
it = peek_iter(a)
|
||||
self.assertTrueTwice(it.has_next)
|
||||
self.assertEqualTwice(['1'], it.peek, 1)
|
||||
self.assertEqual('1', it.next())
|
||||
self.assertFalseTwice(it.has_next)
|
||||
self.assertEqualTwice([it.sentinel], it.peek, 1)
|
||||
self.assertFalseTwice(it.has_next)
|
||||
|
||||
a = ['1', '2']
|
||||
it = peek_iter(a)
|
||||
self.assertTrueTwice(it.has_next)
|
||||
self.assertEqualTwice(['1'], it.peek, 1)
|
||||
self.assertEqual('1', it.next())
|
||||
self.assertTrueTwice(it.has_next)
|
||||
self.assertEqualTwice(['2'], it.peek, 1)
|
||||
self.assertEqual('2', it.next())
|
||||
self.assertFalseTwice(it.has_next)
|
||||
self.assertEqualTwice([it.sentinel], it.peek, 1)
|
||||
self.assertFalseTwice(it.has_next)
|
||||
|
||||
def test_peek_with_zero(self):
|
||||
a = []
|
||||
it = peek_iter(a)
|
||||
self.assertFalseTwice(it.has_next)
|
||||
self.assertEqualTwice([], it.peek, 0)
|
||||
|
||||
a = ['1']
|
||||
it = peek_iter(a)
|
||||
self.assertTrueTwice(it.has_next)
|
||||
self.assertEqualTwice([], it.peek, 0)
|
||||
self.assertTrueTwice(it.has_next)
|
||||
self.assertEqualTwice([], it.peek, 0)
|
||||
|
||||
a = ['1', '2']
|
||||
it = peek_iter(a)
|
||||
self.assertTrueTwice(it.has_next)
|
||||
self.assertEqualTwice([], it.peek, 0)
|
||||
self.assertTrueTwice(it.has_next)
|
||||
self.assertEqualTwice([], it.peek, 0)
|
||||
|
||||
|
||||
class ModifyIterTest(BaseIteratorsTest):
|
||||
def test_init_with_sentinel_args(self):
|
||||
a = iter(['1', '2', '3', 'DONE'])
|
||||
sentinel = 'DONE'
|
||||
|
||||
def get_next():
|
||||
return next(a)
|
||||
it = modify_iter(get_next, sentinel, int)
|
||||
expected = [1, 2, 3]
|
||||
self.assertEqual(expected, [i for i in it])
|
||||
|
||||
def test_init_with_sentinel_kwargs(self):
|
||||
a = iter([1, 2, 3, 4])
|
||||
sentinel = 4
|
||||
|
||||
def get_next():
|
||||
return next(a)
|
||||
it = modify_iter(get_next, sentinel, modifier=str)
|
||||
expected = ['1', '2', '3']
|
||||
self.assertEqual(expected, [i for i in it])
|
||||
|
||||
def test_modifier_default(self):
|
||||
a = ['', ' ', ' a ', 'b ', ' c', ' ', '']
|
||||
it = modify_iter(a)
|
||||
expected = ['', ' ', ' a ', 'b ', ' c', ' ', '']
|
||||
self.assertEqual(expected, [i for i in it])
|
||||
|
||||
def test_modifier_not_callable(self):
|
||||
self.assertRaises(TypeError, modify_iter, [1], modifier='not_callable')
|
||||
|
||||
def test_modifier_rstrip(self):
|
||||
a = ['', ' ', ' a ', 'b ', ' c', ' ', '']
|
||||
it = modify_iter(a, modifier=lambda s: s.rstrip())
|
||||
expected = ['', '', ' a', 'b', ' c', '', '']
|
||||
self.assertEqual(expected, [i for i in it])
|
||||
|
||||
def test_modifier_rstrip_unicode(self):
|
||||
a = [u'', u' ', u' a ', u'b ', u' c', u' ', u'']
|
||||
it = modify_iter(a, modifier=lambda s: s.rstrip())
|
||||
expected = [u'', u'', u' a', u'b', u' c', u'', u'']
|
||||
self.assertEqual(expected, [i for i in it])
|
Loading…
Reference in New Issue
Block a user