summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRob Ruana <rob@relentlessidiot.com>2014-01-19 13:40:06 -0500
committerRob Ruana <rob@relentlessidiot.com>2014-01-19 13:40:06 -0500
commit7f3ff63caae51ddca2a6247295c885a0fda7cc8d (patch)
tree1d269fb42c680f8664e6ba66696ca9e5db508285
parentcd7658dde7f32714f4e6013cd221a9ea6e88bb82 (diff)
parenta8b06aa17015396b9bd5accb5cca4644f69f307d (diff)
downloadsphinx-7f3ff63caae51ddca2a6247295c885a0fda7cc8d.tar.gz
Merged birkenfeld/sphinx into default
-rw-r--r--AUTHORS1
-rw-r--r--doc/conf.py4
-rw-r--r--doc/ext/example_google.py223
-rw-r--r--doc/ext/example_google.rst15
-rw-r--r--doc/ext/example_numpy.py272
-rw-r--r--doc/ext/example_numpy.rst15
-rw-r--r--doc/ext/napoleon.rst365
-rw-r--r--doc/extensions.rst1
-rw-r--r--sphinx/ext/napoleon/__init__.py371
-rw-r--r--sphinx/ext/napoleon/docstring.py714
-rw-r--r--sphinx/ext/napoleon/iterators.py244
-rw-r--r--tests/root/conf.py2
-rw-r--r--tests/test_napoleon.py190
-rw-r--r--tests/test_napoleon_docstring.py259
-rw-r--r--tests/test_napoleon_iterators.py346
-rw-r--r--tox.ini1
16 files changed, 3021 insertions, 2 deletions
diff --git a/AUTHORS b/AUTHORS
index 2a9dbbac..24b9be74 100644
--- a/AUTHORS
+++ b/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
diff --git a/doc/conf.py b/doc/conf.py
index 08149fba..b6515a38 100644
--- a/doc/conf.py
+++ b/doc/conf.py
@@ -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']
diff --git a/doc/ext/example_google.py b/doc/ext/example_google.py
new file mode 100644
index 00000000..c94dcdf1
--- /dev/null
+++ b/doc/ext/example_google.py
@@ -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
diff --git a/doc/ext/example_google.rst b/doc/ext/example_google.rst
new file mode 100644
index 00000000..06508082
--- /dev/null
+++ b/doc/ext/example_google.rst
@@ -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
diff --git a/doc/ext/example_numpy.py b/doc/ext/example_numpy.py
new file mode 100644
index 00000000..df1d20e6
--- /dev/null
+++ b/doc/ext/example_numpy.py
@@ -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
diff --git a/doc/ext/example_numpy.rst b/doc/ext/example_numpy.rst
new file mode 100644
index 00000000..a3b41613
--- /dev/null
+++ b/doc/ext/example_numpy.rst
@@ -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
diff --git a/doc/ext/napoleon.rst b/doc/ext/napoleon.rst
new file mode 100644
index 00000000..fde23c7b
--- /dev/null
+++ b/doc/ext/napoleon.rst
@@ -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
diff --git a/doc/extensions.rst b/doc/extensions.rst
index 7597f281..5534a125 100644
--- a/doc/extensions.rst
+++ b/doc/extensions.rst
@@ -53,6 +53,7 @@ These extensions are built in and can be activated by respective entries in the
ext/extlinks
ext/viewcode
ext/linkcode
+ ext/napoleon
Third-party extensions
diff --git a/sphinx/ext/napoleon/__init__.py b/sphinx/ext/napoleon/__init__.py
new file mode 100644
index 00000000..168ac574
--- /dev/null
+++ b/sphinx/ext/napoleon/__init__.py
@@ -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
diff --git a/sphinx/ext/napoleon/docstring.py b/sphinx/ext/napoleon/docstring.py
new file mode 100644
index 00000000..d6c73db8
--- /dev/null
+++ b/sphinx/ext/napoleon/docstring.py
@@ -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
diff --git a/sphinx/ext/napoleon/iterators.py b/sphinx/ext/napoleon/iterators.py
new file mode 100644
index 00000000..2f1904da
--- /dev/null
+++ b/sphinx/ext/napoleon/iterators.py
@@ -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)
diff --git a/tests/root/conf.py b/tests/root/conf.py
index f0d40148..af984e5e 100644
--- a/tests/root/conf.py
+++ b/tests/root/conf.py
@@ -8,7 +8,7 @@ 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', 'ext']
+ 'sphinx.ext.viewcode', 'sphinx.ext.napoleon', 'ext']
jsmath_path = 'dummy.js'
diff --git a/tests/test_napoleon.py b/tests/test_napoleon.py
new file mode 100644
index 00000000..d8c71960
--- /dev/null
+++ b/tests/test_napoleon.py
@@ -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')
diff --git a/tests/test_napoleon_docstring.py b/tests/test_napoleon_docstring.py
new file mode 100644
index 00000000..aa52aebe
--- /dev/null
+++ b/tests/test_napoleon_docstring.py
@@ -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)
diff --git a/tests/test_napoleon_iterators.py b/tests/test_napoleon_iterators.py
new file mode 100644
index 00000000..db0be32c
--- /dev/null
+++ b/tests/test_napoleon_iterators.py
@@ -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])
diff --git a/tox.ini b/tox.ini
index 006819ef..ee8ce791 100644
--- a/tox.ini
+++ b/tox.ini
@@ -3,6 +3,7 @@ envlist=py26,py27,py32,py33,pypy,du11,du10,du09,du08,du07
[testenv]
deps=
+ mock
nose
sqlalchemy
whoosh