summaryrefslogtreecommitdiff
path: root/sphinx/ext
diff options
context:
space:
mode:
authorGeorg Brandl <georg@python.org>2014-03-01 08:18:23 +0100
committerGeorg Brandl <georg@python.org>2014-03-01 08:18:23 +0100
commit2f950d546d7304e7b9d6592d27b47f82b3a424ad (patch)
tree54b9419c78a5f725508241c48a1ca731ee9317fa /sphinx/ext
parent3c649bfde0126d72894989506c40bb8ae35d7d23 (diff)
parent4047fe8184c2984241b92754b6e6d6b639b8d09b (diff)
downloadsphinx-2f950d546d7304e7b9d6592d27b47f82b3a424ad.tar.gz
Update copyright year.
Diffstat (limited to 'sphinx/ext')
-rw-r--r--sphinx/ext/autodoc.py45
-rw-r--r--sphinx/ext/autosummary/__init__.py2
-rw-r--r--sphinx/ext/autosummary/generate.py11
-rw-r--r--sphinx/ext/coverage.py2
-rw-r--r--sphinx/ext/doctest.py6
-rw-r--r--sphinx/ext/graphviz.py10
-rw-r--r--sphinx/ext/ifconfig.py2
-rw-r--r--sphinx/ext/inheritance_diagram.py2
-rw-r--r--sphinx/ext/intersphinx.py4
-rw-r--r--sphinx/ext/napoleon/__init__.py375
-rw-r--r--sphinx/ext/napoleon/docstring.py714
-rw-r--r--sphinx/ext/napoleon/iterators.py244
-rw-r--r--sphinx/ext/oldcmarkup.py67
-rw-r--r--sphinx/ext/pngmath.py8
-rw-r--r--sphinx/ext/todo.py1
15 files changed, 1396 insertions, 97 deletions
diff --git a/sphinx/ext/autodoc.py b/sphinx/ext/autodoc.py
index 1b68c7b8..a79849ae 100644
--- a/sphinx/ext/autodoc.py
+++ b/sphinx/ext/autodoc.py
@@ -29,7 +29,7 @@ from sphinx.util.nodes import nested_parse_with_titles
from sphinx.util.compat import Directive
from sphinx.util.inspect import getargspec, isdescriptor, safe_getmembers, \
safe_getattr, safe_repr, is_builtin_class_method
-from sphinx.util.pycompat import base_exception, class_types
+from sphinx.util.pycompat import class_types
from sphinx.util.docstrings import prepare_docstring
@@ -70,6 +70,35 @@ class Options(dict):
return None
+class _MockModule(object):
+ """Used by autodoc_mock_imports."""
+ def __init__(self, *args, **kwargs):
+ pass
+
+ def __call__(self, *args, **kwargs):
+ return _MockModule()
+
+ @classmethod
+ def __getattr__(cls, name):
+ if name in ('__file__', '__path__'):
+ return '/dev/null'
+ elif name[0] == name[0].upper():
+ # Not very good, we assume Uppercase names are classes...
+ mocktype = type(name, (), {})
+ mocktype.__module__ = __name__
+ return mocktype
+ else:
+ return _MockModule()
+
+def mock_import(modname):
+ if '.' in modname:
+ pkg, _n, mods = modname.rpartition('.')
+ mock_import(pkg)
+ mod = _MockModule()
+ sys.modules[modname] = mod
+ return mod
+
+
ALL = object()
INSTANCEATTR = object()
@@ -332,6 +361,9 @@ class Documenter(object):
self.modname, '.'.join(self.objpath))
try:
dbg('[autodoc] import %s', self.modname)
+ for modname in self.env.config.autodoc_mock_imports:
+ dbg('[autodoc] adding a mock module %s!', self.modname)
+ mock_import(modname)
__import__(self.modname)
parent = None
obj = self.module = sys.modules[self.modname]
@@ -411,7 +443,7 @@ class Documenter(object):
# try to introspect the signature
try:
args = self.format_args()
- except Exception, err:
+ except Exception as err:
self.directive.warn('error while formatting arguments for '
'%s: %s' % (self.fullname, err))
args = None
@@ -731,7 +763,7 @@ class Documenter(object):
# parse right now, to get PycodeErrors on parsing (results will
# be cached anyway)
self.analyzer.find_attr_docs()
- except PycodeError, err:
+ except PycodeError as err:
self.env.app.debug('[autodoc] module analyzer failed: %s', err)
# no source file -- e.g. for builtin and C modules
self.analyzer = None
@@ -1131,7 +1163,7 @@ class ExceptionDocumenter(ClassDocumenter):
@classmethod
def can_document_member(cls, member, membername, isattr, parent):
return isinstance(member, class_types) and \
- issubclass(member, base_exception)
+ issubclass(member, BaseException)
class DataDocumenter(ModuleLevelDocumenter):
@@ -1198,7 +1230,7 @@ class MethodDocumenter(DocstringSignatureMixin, ClassLevelDocumenter):
ret = ClassLevelDocumenter.import_object(self)
if isinstance(self.object, classmethod) or \
(isinstance(self.object, MethodType) and
- self.object.im_self is not None):
+ self.object.__self__ is not None):
self.directivetype = 'classmethod'
# document class and static members before ordinary ones
self.member_order = self.member_order - 1
@@ -1390,7 +1422,7 @@ class AutoDirective(Directive):
try:
self.genopt = Options(assemble_option_dict(
self.options.items(), doc_class.option_spec))
- except (KeyError, ValueError, TypeError), err:
+ except (KeyError, ValueError, TypeError) as err:
# an option is either unknown or has a wrong type
msg = self.reporter.error('An option to %s is either unknown or '
'has an invalid value: %s' % (self.name, err),
@@ -1454,6 +1486,7 @@ def setup(app):
app.add_config_value('autodoc_member_order', 'alphabetic', True)
app.add_config_value('autodoc_default_flags', [], True)
app.add_config_value('autodoc_docstring_signature', True, True)
+ app.add_config_value('autodoc_mock_imports', [], True)
app.add_event('autodoc-process-docstring')
app.add_event('autodoc-process-signature')
app.add_event('autodoc-skip-member')
diff --git a/sphinx/ext/autosummary/__init__.py b/sphinx/ext/autosummary/__init__.py
index c8dcf2dc..6b9fed3d 100644
--- a/sphinx/ext/autosummary/__init__.py
+++ b/sphinx/ext/autosummary/__init__.py
@@ -465,7 +465,7 @@ def _import_by_name(name):
return obj, parent
else:
return sys.modules[modname], None
- except (ValueError, ImportError, AttributeError, KeyError), e:
+ except (ValueError, ImportError, AttributeError, KeyError) as e:
raise ImportError(*e.args)
diff --git a/sphinx/ext/autosummary/generate.py b/sphinx/ext/autosummary/generate.py
index c4f150a0..5daf95f5 100644
--- a/sphinx/ext/autosummary/generate.py
+++ b/sphinx/ext/autosummary/generate.py
@@ -17,6 +17,7 @@
:copyright: Copyright 2007-2014 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
+from __future__ import print_function
import os
import re
@@ -70,10 +71,10 @@ def main(argv=sys.argv):
template_dir=options.templates)
def _simple_info(msg):
- print msg
+ print(msg)
def _simple_warn(msg):
- print >> sys.stderr, 'WARNING: ' + msg
+ print('WARNING: ' + msg, file=sys.stderr)
# -- Generating output ---------------------------------------------------------
@@ -127,7 +128,7 @@ def generate_autosummary_docs(sources, output_dir=None, suffix='.rst',
try:
name, obj, parent = import_by_name(name)
- except ImportError, e:
+ except ImportError as e:
warn('[autosummary] failed to import %r: %s' % (name, e))
continue
@@ -240,8 +241,8 @@ def find_autosummary_in_docstring(name, module=None, filename=None):
return find_autosummary_in_lines(lines, module=name, filename=filename)
except AttributeError:
pass
- except ImportError, e:
- print "Failed to import '%s': %s" % (name, e)
+ except ImportError as e:
+ print("Failed to import '%s': %s" % (name, e))
return []
def find_autosummary_in_lines(lines, module=None, filename=None):
diff --git a/sphinx/ext/coverage.py b/sphinx/ext/coverage.py
index 52be1bdb..f1ac6cec 100644
--- a/sphinx/ext/coverage.py
+++ b/sphinx/ext/coverage.py
@@ -134,7 +134,7 @@ class CoverageBuilder(Builder):
try:
mod = __import__(mod_name, fromlist=['foo'])
- except ImportError, err:
+ except ImportError as err:
self.warn('module %s could not be imported: %s' %
(mod_name, err))
self.py_undoc[mod_name] = {'error': err}
diff --git a/sphinx/ext/doctest.py b/sphinx/ext/doctest.py
index 70beb9bf..ef27dc11 100644
--- a/sphinx/ext/doctest.py
+++ b/sphinx/ext/doctest.py
@@ -289,14 +289,14 @@ Doctest summary
if self.config.doctest_test_doctest_blocks:
def condition(node):
return (isinstance(node, (nodes.literal_block, nodes.comment))
- and node.has_key('testnodetype')) or \
+ and 'testnodetype' in node) or \
isinstance(node, nodes.doctest_block)
else:
def condition(node):
return isinstance(node, (nodes.literal_block, nodes.comment)) \
- and node.has_key('testnodetype')
+ and 'testnodetype' in node
for node in doctree.traverse(condition):
- source = node.has_key('test') and node['test'] or node.astext()
+ source = 'test' in node and node['test'] or node.astext()
if not source:
self.warn('no code/output in %s block at %s:%s' %
(node.get('testnodetype', 'doctest'),
diff --git a/sphinx/ext/graphviz.py b/sphinx/ext/graphviz.py
index 028560b1..abfb6ac6 100644
--- a/sphinx/ext/graphviz.py
+++ b/sphinx/ext/graphviz.py
@@ -156,7 +156,7 @@ def render_dot(self, code, options, format, prefix='graphviz'):
dot_args.extend(['-Tcmapx', '-o%s.map' % outfn])
try:
p = Popen(dot_args, stdout=PIPE, stdin=PIPE, stderr=PIPE)
- except OSError, err:
+ except OSError as err:
if err.errno != ENOENT: # No such file or directory
raise
self.builder.warn('dot command %r cannot be run (needed for graphviz '
@@ -168,7 +168,7 @@ def render_dot(self, code, options, format, prefix='graphviz'):
# Graphviz may close standard input when an error occurs,
# resulting in a broken pipe on communicate()
stdout, stderr = p.communicate(code)
- except (OSError, IOError), err:
+ except (OSError, IOError) as err:
if err.errno not in (EPIPE, EINVAL):
raise
# in this case, read the standard output and standard error streams
@@ -192,7 +192,7 @@ def render_dot_html(self, node, code, options, prefix='graphviz',
raise GraphvizError("graphviz_output_format must be one of 'png', "
"'svg', but is %r" % format)
fname, outfn = render_dot(self, code, options, format, prefix)
- except GraphvizError, exc:
+ except GraphvizError as exc:
self.builder.warn('dot code %r: ' % code + str(exc))
raise nodes.SkipNode
@@ -243,7 +243,7 @@ def html_visit_graphviz(self, node):
def render_dot_latex(self, node, code, options, prefix='graphviz'):
try:
fname, outfn = render_dot(self, code, options, 'pdf', prefix)
- except GraphvizError, exc:
+ except GraphvizError as exc:
self.builder.warn('dot code %r: ' % code + str(exc))
raise nodes.SkipNode
@@ -276,7 +276,7 @@ def latex_visit_graphviz(self, node):
def render_dot_texinfo(self, node, code, options, prefix='graphviz'):
try:
fname, outfn = render_dot(self, code, options, 'png', prefix)
- except GraphvizError, exc:
+ except GraphvizError as exc:
self.builder.warn('dot code %r: ' % code + str(exc))
raise nodes.SkipNode
if fname is not None:
diff --git a/sphinx/ext/ifconfig.py b/sphinx/ext/ifconfig.py
index 3362e56a..58ab9b40 100644
--- a/sphinx/ext/ifconfig.py
+++ b/sphinx/ext/ifconfig.py
@@ -53,7 +53,7 @@ def process_ifconfig_nodes(app, doctree, docname):
for node in doctree.traverse(ifconfig):
try:
res = eval(node['expr'], ns)
- except Exception, err:
+ except Exception as err:
# handle exceptions in a clean fashion
from traceback import format_exception_only
msg = ''.join(format_exception_only(err.__class__, err))
diff --git a/sphinx/ext/inheritance_diagram.py b/sphinx/ext/inheritance_diagram.py
index d10b89cc..6129da0c 100644
--- a/sphinx/ext/inheritance_diagram.py
+++ b/sphinx/ext/inheritance_diagram.py
@@ -306,7 +306,7 @@ class InheritanceDiagram(Directive):
class_names, env.temp_data.get('py:module'),
parts=node['parts'],
private_bases='private-bases' in self.options)
- except InheritanceException, err:
+ except InheritanceException as err:
return [node.document.reporter.warning(err.args[0],
line=self.lineno)]
diff --git a/sphinx/ext/intersphinx.py b/sphinx/ext/intersphinx.py
index c3adf563..e2c44b58 100644
--- a/sphinx/ext/intersphinx.py
+++ b/sphinx/ext/intersphinx.py
@@ -132,7 +132,7 @@ def fetch_inventory(app, uri, inv):
f = urllib2.urlopen(inv)
else:
f = open(path.join(app.srcdir, inv), 'rb')
- except Exception, err:
+ except Exception as err:
app.warn('intersphinx inventory %r not fetchable due to '
'%s: %s' % (inv, err.__class__, err))
return
@@ -149,7 +149,7 @@ def fetch_inventory(app, uri, inv):
except ValueError:
f.close()
raise ValueError('unknown or unsupported inventory version')
- except Exception, err:
+ except Exception as err:
app.warn('intersphinx inventory %r not readable due to '
'%s: %s' % (inv, err.__class__.__name__, err))
else:
diff --git a/sphinx/ext/napoleon/__init__.py b/sphinx/ext/napoleon/__init__.py
new file mode 100644
index 00000000..d3b6f754
--- /dev/null
+++ b/sphinx/ext/napoleon/__init__.py
@@ -0,0 +1,375 @@
+# -*- 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
+
+ Attributes
+ ----------
+ 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.
+
+ See Also
+ --------
+ :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.
+
+ See Also
+ --------
+ :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/sphinx/ext/oldcmarkup.py b/sphinx/ext/oldcmarkup.py
deleted file mode 100644
index aa10246b..00000000
--- a/sphinx/ext/oldcmarkup.py
+++ /dev/null
@@ -1,67 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
- sphinx.ext.oldcmarkup
- ~~~~~~~~~~~~~~~~~~~~~
-
- Extension for compatibility with old C markup (directives and roles).
-
- :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS.
- :license: BSD, see LICENSE for details.
-"""
-
-from docutils.parsers.rst import directives
-
-from sphinx.util.compat import Directive
-
-_warned_oldcmarkup = False
-WARNING_MSG = 'using old C markup; please migrate to new-style markup ' \
- '(e.g. c:function instead of cfunction), see ' \
- 'http://sphinx-doc.org/domains.html'
-
-
-class OldCDirective(Directive):
- has_content = True
- required_arguments = 1
- optional_arguments = 0
- final_argument_whitespace = True
- option_spec = {
- 'noindex': directives.flag,
- 'module': directives.unchanged,
- }
-
- def run(self):
- env = self.state.document.settings.env
- if not env.app._oldcmarkup_warned:
- self.state_machine.reporter.warning(WARNING_MSG, line=self.lineno)
- env.app._oldcmarkup_warned = True
- newname = 'c:' + self.name[1:]
- newdir = env.lookup_domain_element('directive', newname)[0]
- return newdir(newname, self.arguments, self.options,
- self.content, self.lineno, self.content_offset,
- self.block_text, self.state, self.state_machine).run()
-
-
-def old_crole(typ, rawtext, text, lineno, inliner, options={}, content=[]):
- env = inliner.document.settings.env
- if not typ:
- typ = env.config.default_role
- if not env.app._oldcmarkup_warned:
- inliner.reporter.warning(WARNING_MSG, line=lineno)
- env.app._oldcmarkup_warned = True
- newtyp = 'c:' + typ[1:]
- newrole = env.lookup_domain_element('role', newtyp)[0]
- return newrole(newtyp, rawtext, text, lineno, inliner, options, content)
-
-
-def setup(app):
- app._oldcmarkup_warned = False
- app.add_directive('cfunction', OldCDirective)
- app.add_directive('cmember', OldCDirective)
- app.add_directive('cmacro', OldCDirective)
- app.add_directive('ctype', OldCDirective)
- app.add_directive('cvar', OldCDirective)
- app.add_role('cdata', old_crole)
- app.add_role('cfunc', old_crole)
- app.add_role('cmacro', old_crole)
- app.add_role('ctype', old_crole)
- app.add_role('cmember', old_crole)
diff --git a/sphinx/ext/pngmath.py b/sphinx/ext/pngmath.py
index b6546301..6bfe644c 100644
--- a/sphinx/ext/pngmath.py
+++ b/sphinx/ext/pngmath.py
@@ -123,7 +123,7 @@ def render_math(self, math):
try:
try:
p = Popen(ltx_args, stdout=PIPE, stderr=PIPE)
- except OSError, err:
+ except OSError as err:
if err.errno != ENOENT: # No such file or directory
raise
self.builder.warn('LaTeX command %r cannot be run (needed for math '
@@ -150,7 +150,7 @@ def render_math(self, math):
dvipng_args.append(path.join(tempdir, 'math.dvi'))
try:
p = Popen(dvipng_args, stdout=PIPE, stderr=PIPE)
- except OSError, err:
+ except OSError as err:
if err.errno != ENOENT: # No such file or directory
raise
self.builder.warn('dvipng command %r cannot be run (needed for math '
@@ -190,7 +190,7 @@ def get_tooltip(self, node):
def html_visit_math(self, node):
try:
fname, depth = render_math(self, '$'+node['latex']+'$')
- except MathExtError, exc:
+ except MathExtError as exc:
msg = unicode(exc)
sm = nodes.system_message(msg, type='WARNING', level=2,
backrefs=[], source=node['latex'])
@@ -215,7 +215,7 @@ def html_visit_displaymath(self, node):
latex = wrap_displaymath(node['latex'], None)
try:
fname, depth = render_math(self, latex)
- except MathExtError, exc:
+ except MathExtError as exc:
sm = nodes.system_message(str(exc), type='WARNING', level=2,
backrefs=[], source=node['latex'])
sm.walkabout(self)
diff --git a/sphinx/ext/todo.py b/sphinx/ext/todo.py
index 9f521fb4..5422c89a 100644
--- a/sphinx/ext/todo.py
+++ b/sphinx/ext/todo.py
@@ -171,4 +171,3 @@ def setup(app):
app.connect('doctree-read', process_todos)
app.connect('doctree-resolved', process_todo_nodes)
app.connect('env-purge-doc', purge_todos)
-