summaryrefslogtreecommitdiff
path: root/sphinx/ext
diff options
context:
space:
mode:
Diffstat (limited to 'sphinx/ext')
-rw-r--r--sphinx/ext/__init__.py2
-rw-r--r--sphinx/ext/apidoc.py14
-rw-r--r--sphinx/ext/autodoc/__init__.py339
-rw-r--r--sphinx/ext/autodoc/directive.py155
-rw-r--r--sphinx/ext/autodoc/importer.py91
-rw-r--r--sphinx/ext/autodoc/inspector.py2
-rw-r--r--sphinx/ext/autosectionlabel.py2
-rw-r--r--sphinx/ext/autosummary/__init__.py29
-rw-r--r--sphinx/ext/autosummary/generate.py54
-rw-r--r--sphinx/ext/coverage.py2
-rw-r--r--sphinx/ext/doctest.py2
-rw-r--r--sphinx/ext/extlinks.py2
-rw-r--r--sphinx/ext/githubpages.py2
-rw-r--r--sphinx/ext/graphviz.py2
-rw-r--r--sphinx/ext/ifconfig.py2
-rw-r--r--sphinx/ext/imgconverter.py2
-rw-r--r--sphinx/ext/imgmath.py6
-rw-r--r--sphinx/ext/inheritance_diagram.py2
-rw-r--r--sphinx/ext/intersphinx.py3
-rw-r--r--sphinx/ext/jsmath.py6
-rw-r--r--sphinx/ext/linkcode.py2
-rw-r--r--sphinx/ext/mathbase.py39
-rw-r--r--sphinx/ext/mathjax.py6
-rw-r--r--sphinx/ext/napoleon/__init__.py2
-rw-r--r--sphinx/ext/napoleon/docstring.py2
-rw-r--r--sphinx/ext/napoleon/iterators.py2
-rw-r--r--sphinx/ext/pngmath.py6
-rw-r--r--sphinx/ext/todo.py5
-rw-r--r--sphinx/ext/viewcode.py2
29 files changed, 472 insertions, 313 deletions
diff --git a/sphinx/ext/__init__.py b/sphinx/ext/__init__.py
index e529ee9e5..440c01a15 100644
--- a/sphinx/ext/__init__.py
+++ b/sphinx/ext/__init__.py
@@ -5,6 +5,6 @@
Contains Sphinx features not activated by default.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/ext/apidoc.py b/sphinx/ext/apidoc.py
index 0bdeb9865..cec9d8138 100644
--- a/sphinx/ext/apidoc.py
+++ b/sphinx/ext/apidoc.py
@@ -11,7 +11,7 @@
Copyright 2008 Société des arts technologiques (SAT),
http://www.sat.qc.ca/
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -27,7 +27,7 @@ from fnmatch import fnmatch
from sphinx import __display_version__
from sphinx.cmd.quickstart import EXTENSIONS
from sphinx.util import rst
-from sphinx.util.osutil import FileAvoidWrite, walk
+from sphinx.util.osutil import FileAvoidWrite, ensuredir, walk
if False:
# For type annotation
@@ -117,7 +117,11 @@ def create_package_file(root, master_package, subroot, py_files, opts, subs, is_
text += '\n'
# build a list of directories that are szvpackages (contain an INITPY file)
- subs = [sub for sub in subs if path.isfile(path.join(root, sub, INITPY))]
+ # and also checks the INITPY file is not empty, or there are other python
+ # source files in that folder.
+ # (depending on settings - but shall_skip() takes care of that)
+ subs = [sub for sub in subs if not
+ shall_skip(path.join(root, sub, INITPY), opts)]
# if there are some package directories, add a TOC for theses subpackages
if subs:
text += format_heading(2, 'Subpackages')
@@ -382,8 +386,8 @@ def main(argv=sys.argv[1:]):
if not path.isdir(rootpath):
print('%s is not a directory.' % rootpath, file=sys.stderr)
sys.exit(1)
- if not path.isdir(args.destdir) and not args.dryrun:
- os.makedirs(args.destdir)
+ if not args.dryrun:
+ ensuredir(args.destdir)
excludes = [path.abspath(exclude) for exclude in args.exclude_pattern]
modules = recurse_tree(rootpath, excludes, args)
diff --git a/sphinx/ext/autodoc/__init__.py b/sphinx/ext/autodoc/__init__.py
index 48afbcc91..d9e82813d 100644
--- a/sphinx/ext/autodoc/__init__.py
+++ b/sphinx/ext/autodoc/__init__.py
@@ -7,24 +7,22 @@
the doctree, thus avoiding duplication between docstrings and documentation
for those who like elaborate docstrings.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
import re
import sys
import inspect
-import traceback
+import warnings
-from six import PY2, iterkeys, iteritems, itervalues, text_type, class_types, string_types
+from six import iteritems, itervalues, text_type, class_types, string_types
-from docutils import nodes
-from docutils.utils import assemble_option_dict
-from docutils.parsers.rst import Directive
from docutils.statemachine import ViewList
import sphinx
-from sphinx.ext.autodoc.importer import mock, import_module
+from sphinx.deprecation import RemovedInSphinx20Warning
+from sphinx.ext.autodoc.importer import mock, import_object, get_object_members
from sphinx.ext.autodoc.importer import _MockImporter # to keep compatibility # NOQA
from sphinx.ext.autodoc.inspector import format_annotation, formatargspec # to keep compatibility # NOQA
from sphinx.util import rpartition, force_decode
@@ -32,21 +30,23 @@ from sphinx.locale import _
from sphinx.pycode import ModuleAnalyzer, PycodeError
from sphinx.application import ExtensionError
from sphinx.util import logging
-from sphinx.util.nodes import nested_parse_with_titles
from sphinx.util.inspect import Signature, isdescriptor, safe_getmembers, \
safe_getattr, object_description, is_builtin_class_method, \
- isenumclass, isenumattribute, getdoc
+ isenumattribute, getdoc
from sphinx.util.docstrings import prepare_docstring
if False:
# For type annotation
from types import ModuleType # NOQA
from typing import Any, Callable, Dict, Iterator, List, Sequence, Set, Tuple, Type, Union # NOQA
+ from docutils import nodes # NOQA
from docutils.utils import Reporter # NOQA
from sphinx.application import Sphinx # NOQA
+ from sphinx.ext.autodoc.directive import DocumenterBridge # NOQA
logger = logging.getLogger(__name__)
+
# This type isn't exposed directly in any modules, but can be found
# here in most Python versions
MethodDescriptorType = type(type.__subclasses__)
@@ -63,42 +63,11 @@ py_ext_sig_re = re.compile(
''', re.VERBOSE)
-class DefDict(dict):
- """A dict that returns a default on nonexisting keys."""
- def __init__(self, default):
- # type: (Any) -> None
- dict.__init__(self)
- self.default = default
-
- def __getitem__(self, key):
- # type: (Any) -> Any
- try:
- return dict.__getitem__(self, key)
- except KeyError:
- return self.default
-
- def __bool__(self):
- # type: () -> bool
- # docutils check "if option_spec"
- return True
- __nonzero__ = __bool__ # for python2 compatibility
-
-
def identity(x):
# type: (Any) -> Any
return x
-class Options(dict):
- """A dict/attribute hybrid that returns None on nonexisting keys."""
- def __getattr__(self, name):
- # type: (unicode) -> Any
- try:
- return self[name.replace('_', '-')]
- except KeyError:
- return None
-
-
ALL = object()
INSTANCEATTR = object()
@@ -146,6 +115,9 @@ class AutodocReporter(object):
"""
def __init__(self, viewlist, reporter):
# type: (ViewList, Reporter) -> None
+ warnings.warn('AutodocReporter is now deprecated. '
+ 'Use sphinx.util.docutils.switch_source_input() instead.',
+ RemovedInSphinx20Warning)
self.viewlist = viewlist
self.reporter = reporter
@@ -284,14 +256,10 @@ class Documenter(object):
option_spec = {'noindex': bool_option} # type: Dict[unicode, Callable]
- @staticmethod
- def get_attr(obj, name, *defargs):
+ def get_attr(self, obj, name, *defargs):
# type: (Any, unicode, Any) -> Any
"""getattr() override for types such as Zope interfaces."""
- for typ, func in iteritems(AutoDirective._special_attrgetters):
- if isinstance(obj, typ):
- return func(obj, name, *defargs)
- return safe_getattr(obj, name, *defargs)
+ return autodoc_attrgetter(self.env.app, obj, name, *defargs)
@classmethod
def can_document_member(cls, member, membername, isattr, parent):
@@ -300,7 +268,7 @@ class Documenter(object):
raise NotImplementedError('must be implemented in subclasses')
def __init__(self, directive, name, indent=u''):
- # type: (Directive, unicode, unicode) -> None
+ # type: (DocumenterBridge, unicode, unicode) -> None
self.directive = directive
self.env = directive.env
self.options = directive.genopt
@@ -324,6 +292,12 @@ class Documenter(object):
# the module analyzer to get at attribute docs, or None
self.analyzer = None # type: Any
+ @property
+ def documenters(self):
+ # type: () -> Dict[unicode, Type[Documenter]]
+ """Returns registered Documenter classes"""
+ return get_documenters(self.env.app)
+
def add_line(self, line, source, *lineno):
# type: (unicode, unicode, int) -> None
"""Append one line of generated reST to the output."""
@@ -383,55 +357,15 @@ class Documenter(object):
Returns True if successful, False if an error occurred.
"""
- if self.objpath:
- logger.debug('[autodoc] from %s import %s',
- self.modname, '.'.join(self.objpath))
- # always enable mock import hook
- # it will do nothing if autodoc_mock_imports is empty
with mock(self.env.config.autodoc_mock_imports):
try:
- logger.debug('[autodoc] import %s', self.modname)
- obj = import_module(self.modname, self.env.config.autodoc_warningiserror)
- parent = None
- self.module = obj
- logger.debug('[autodoc] => %r', obj)
- for part in self.objpath:
- parent = obj
- logger.debug('[autodoc] getattr(_, %r)', part)
- obj = self.get_attr(obj, part)
- logger.debug('[autodoc] => %r', obj)
- self.object_name = part
- self.parent = parent
- self.object = obj
+ ret = import_object(self.modname, self.objpath, self.objtype,
+ attrgetter=self.get_attr,
+ warningiserror=self.env.config.autodoc_warningiserror)
+ self.module, self.parent, self.object_name, self.object = ret
return True
- except (AttributeError, ImportError) as exc:
- if self.objpath:
- errmsg = 'autodoc: failed to import %s %r from module %r' % \
- (self.objtype, '.'.join(self.objpath), self.modname)
- else:
- errmsg = 'autodoc: failed to import %s %r' % \
- (self.objtype, self.fullname)
-
- if isinstance(exc, ImportError):
- # import_module() raises ImportError having real exception obj and
- # traceback
- real_exc, traceback_msg = exc.args
- if isinstance(real_exc, SystemExit):
- errmsg += ('; the module executes module level statement ' +
- 'and it might call sys.exit().')
- elif isinstance(real_exc, ImportError):
- errmsg += ('; the following exception was raised:\n%s' %
- real_exc.args[0])
- else:
- errmsg += ('; the following exception was raised:\n%s' %
- traceback_msg)
- else:
- errmsg += ('; the following exception was raised:\n%s' %
- traceback.format_exc())
-
- if PY2:
- errmsg = errmsg.decode('utf-8') # type: ignore
- logger.warning(errmsg)
+ except ImportError as exc:
+ logger.warning(exc.args[0])
self.env.note_reread()
return False
@@ -604,57 +538,24 @@ class Documenter(object):
If *want_all* is True, return all members. Else, only return those
members given by *self.options.members* (which may also be none).
"""
- analyzed_member_names = set()
- if self.analyzer:
- attr_docs = self.analyzer.find_attr_docs()
- namespace = '.'.join(self.objpath)
- for item in iteritems(attr_docs):
- if item[0][0] == namespace:
- analyzed_member_names.add(item[0][1])
+ members = get_object_members(self.object, self.objpath, self.get_attr, self.analyzer)
if not want_all:
if not self.options.members:
return False, []
# specific members given
- members = []
- for mname in self.options.members:
- try:
- members.append((mname, self.get_attr(self.object, mname)))
- except AttributeError:
- if mname not in analyzed_member_names:
- logger.warning('missing attribute %s in object %s' %
- (mname, self.fullname))
+ selected = []
+ for name in self.options.members:
+ if name in members:
+ selected.append((name, members[name].value))
+ else:
+ logger.warning('missing attribute %s in object %s' %
+ (name, self.fullname))
+ return False, sorted(selected)
elif self.options.inherited_members:
- # safe_getmembers() uses dir() which pulls in members from all
- # base classes
- members = safe_getmembers(self.object, attr_getter=self.get_attr)
+ return False, sorted((m.name, m.value) for m in itervalues(members))
else:
- # __dict__ contains only the members directly defined in
- # the class (but get them via getattr anyway, to e.g. get
- # unbound method objects instead of function objects);
- # using list(iterkeys()) because apparently there are objects for which
- # __dict__ changes while getting attributes
- try:
- obj_dict = self.get_attr(self.object, '__dict__')
- except AttributeError:
- members = []
- else:
- members = [(mname, self.get_attr(self.object, mname, None))
- for mname in list(iterkeys(obj_dict))]
-
- # Py34 doesn't have enum members in __dict__.
- if isenumclass(self.object):
- members.extend(
- item for item in self.object.__members__.items()
- if item not in members
- )
-
- membernames = set(m[0] for m in members)
- # add instance attributes from the analyzer
- for aname in analyzed_member_names:
- if aname not in membernames and \
- (want_all or aname in self.options.members):
- members.append((aname, INSTANCEATTR))
- return False, sorted(members)
+ return False, sorted((m.name, m.value) for m in itervalues(members)
+ if m.directly_defined)
def filter_members(self, members, want_all):
# type: (List[Tuple[unicode, Any]], bool) -> List[Tuple[unicode, Any, bool]]
@@ -713,8 +614,7 @@ class Documenter(object):
elif (namespace, membername) in attr_docs:
if want_all and membername.startswith('_'):
# ignore members whose name starts with _ by default
- keep = self.options.private_members and \
- (has_doc or self.options.undoc_members)
+ keep = self.options.private_members
else:
# keep documented attributes
keep = True
@@ -767,7 +667,7 @@ class Documenter(object):
# document non-skipped members
memberdocumenters = [] # type: List[Tuple[Documenter, bool]]
for (mname, member, isattr) in self.filter_members(members, want_all):
- classes = [cls for cls in itervalues(AutoDirective._registry)
+ classes = [cls for cls in itervalues(self.documenters)
if cls.can_document_member(member, mname, isattr, self)]
if not classes:
# don't know how to document this member
@@ -896,7 +796,7 @@ class ModuleDocumenter(Documenter):
'platform': identity, 'deprecated': bool_option,
'member-order': identity, 'exclude-members': members_set_option,
'private-members': bool_option, 'special-members': members_option,
- 'imported-members': bool_option,
+ 'imported-members': bool_option, 'ignore-module-all': bool_option
} # type: Dict[unicode, Callable]
@classmethod
@@ -938,7 +838,8 @@ class ModuleDocumenter(Documenter):
def get_object_members(self, want_all):
# type: (bool) -> Tuple[bool, List[Tuple[unicode, object]]]
if want_all:
- if not hasattr(self.object, '__all__'):
+ if (self.options.ignore_module_all or not
+ hasattr(self.object, '__all__')):
# for implicit module members, check __module__ to avoid
# documenting imported objects
return True, safe_getmembers(self.object)
@@ -1502,117 +1403,56 @@ class InstanceAttributeDocumenter(AttributeDocumenter):
AttributeDocumenter.add_content(self, more_content, no_docstring=True)
-class AutoDirective(Directive):
- """
- The AutoDirective class is used for all autodoc directives. It dispatches
- most of the work to one of the Documenters, which it selects through its
- *_registry* dictionary.
+class DeprecatedDict(dict):
+ def __init__(self, message):
+ self.message = message
+ super(DeprecatedDict, self).__init__()
+
+ def __setitem__(self, key, value):
+ warnings.warn(self.message, RemovedInSphinx20Warning)
+ super(DeprecatedDict, self).__setitem__(key, value)
- The *_special_attrgetters* attribute is used to customize ``getattr()``
- calls that the Documenters make; its entries are of the form ``type:
- getattr_function``.
+ def setdefault(self, key, default=None):
+ warnings.warn(self.message, RemovedInSphinx20Warning)
+ super(DeprecatedDict, self).setdefault(key, default)
+
+ def update(self, other=None):
+ warnings.warn(self.message, RemovedInSphinx20Warning)
+ super(DeprecatedDict, self).update(other)
+
+
+class AutodocRegistry(object):
+ """
+ A registry of Documenters and attrgetters.
Note: When importing an object, all items along the import chain are
accessed using the descendant's *_special_attrgetters*, thus this
dictionary should include all necessary functions for accessing
attributes of the parents.
"""
- # a registry of objtype -> documenter class
- _registry = {} # type: Dict[unicode, Type[Documenter]]
+ # a registry of objtype -> documenter class (Deprecated)
+ _registry = DeprecatedDict(
+ 'AutoDirective._registry has been deprecated. '
+ 'Please use app.add_autodocumenter() instead.'
+ ) # type: Dict[unicode, Type[Documenter]]
# a registry of type -> getattr function
- _special_attrgetters = {} # type: Dict[Type, Callable]
-
- # flags that can be given in autodoc_default_flags
- _default_flags = set([
- 'members', 'undoc-members', 'inherited-members', 'show-inheritance',
- 'private-members', 'special-members',
- ])
-
- # standard docutils directive settings
- has_content = True
- required_arguments = 1
- optional_arguments = 0
- final_argument_whitespace = True
- # allow any options to be passed; the options are parsed further
- # by the selected Documenter
- option_spec = DefDict(identity)
-
- def warn(self, msg):
- # type: (unicode) -> None
- logger.warning(msg, line=self.lineno)
+ _special_attrgetters = DeprecatedDict(
+ 'AutoDirective._special_attrgetters has been deprecated. '
+ 'Please use app.add_autodoc_attrgetter() instead.'
+ ) # type: Dict[Type, Callable]
- def run(self):
- # type: () -> List[nodes.Node]
- self.filename_set = set() # type: Set[unicode]
- # a set of dependent filenames
- self.reporter = self.state.document.reporter
- self.env = self.state.document.settings.env
- self.result = ViewList()
- try:
- source, lineno = self.reporter.get_source_and_line(self.lineno)
- except AttributeError:
- source = lineno = None
- logger.debug('[autodoc] %s:%s: input:\n%s',
- source, lineno, self.block_text)
-
- # find out what documenter to call
- objtype = self.name[4:]
- doc_class = self._registry[objtype]
- # add default flags
- for flag in self._default_flags:
- if flag not in doc_class.option_spec:
- continue
- negated = self.options.pop('no-' + flag, 'not given') is None
- if flag in self.env.config.autodoc_default_flags and \
- not negated:
- self.options[flag] = None
- # process the options with the selected documenter's option_spec
- try:
- self.genopt = Options(assemble_option_dict(
- self.options.items(), doc_class.option_spec))
- 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),
- line=self.lineno)
- return [msg]
- # generate the output
- documenter = doc_class(self, self.arguments[0])
- documenter.generate(more_content=self.content)
- if not self.result:
- return []
-
- logger.debug('[autodoc] output:\n%s', '\n'.join(self.result))
-
- # record all filenames as dependencies -- this will at least
- # partially make automatic invalidation possible
- for fn in self.filename_set:
- self.state.document.settings.record_dependencies.add(fn)
-
- # use a custom reporter that correctly assigns lines to source
- # filename/description and lineno
- old_reporter = self.state.memo.reporter
- self.state.memo.reporter = AutodocReporter(self.result,
- self.state.memo.reporter)
-
- if documenter.titles_allowed:
- node = nodes.section()
- # necessary so that the child nodes get the right source/line set
- node.document = self.state.document
- nested_parse_with_titles(self.state, self.result, node)
- else:
- node = nodes.paragraph()
- node.document = self.state.document
- self.state.nested_parse(self.result, 0, node)
- self.state.memo.reporter = old_reporter
- return node.children
+AutoDirective = AutodocRegistry # for backward compatibility
def add_documenter(cls):
# type: (Type[Documenter]) -> None
"""Register a new Documenter."""
+ warnings.warn('sphinx.ext.autodoc.add_documenter() has been deprecated. '
+ 'Please use app.add_autodocumenter() instead.',
+ RemovedInSphinx20Warning)
+
if not issubclass(cls, Documenter):
raise ExtensionError('autodoc documenter %r must be a subclass '
'of Documenter' % cls)
@@ -1623,6 +1463,29 @@ def add_documenter(cls):
AutoDirective._registry[cls.objtype] = cls
+def get_documenters(app):
+ # type: (Sphinx) -> Dict[unicode, Type[Documenter]]
+ """Returns registered Documenter classes"""
+ classes = dict(AutoDirective._registry) # registered directly
+ if app:
+ classes.update(app.registry.documenters) # registered by API
+ return classes
+
+
+def autodoc_attrgetter(app, obj, name, *defargs):
+ # type: (Sphinx, Any, unicode, Any) -> Any
+ """Alternative getattr() for types"""
+ candidates = dict(AutoDirective._special_attrgetters)
+ if app:
+ candidates.update(app.registry.autodoc_attrgettrs)
+
+ for typ, func in iteritems(candidates):
+ if isinstance(obj, typ):
+ return func(obj, name, *defargs)
+
+ return safe_getattr(obj, name, *defargs)
+
+
def setup(app):
# type: (Sphinx) -> Dict[unicode, Any]
app.add_autodocumenter(ModuleDocumenter)
diff --git a/sphinx/ext/autodoc/directive.py b/sphinx/ext/autodoc/directive.py
new file mode 100644
index 000000000..6de6e5517
--- /dev/null
+++ b/sphinx/ext/autodoc/directive.py
@@ -0,0 +1,155 @@
+# -*- coding: utf-8 -*-
+"""
+ sphinx.ext.autodoc.directive
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :license: BSD, see LICENSE for details.
+"""
+
+from docutils import nodes
+from docutils.parsers.rst import Directive
+from docutils.statemachine import ViewList
+from docutils.utils import assemble_option_dict
+
+from sphinx.ext.autodoc import get_documenters
+from sphinx.util import logging
+from sphinx.util.docutils import switch_source_input
+from sphinx.util.nodes import nested_parse_with_titles
+
+if False:
+ # For type annotation
+ from typing import Any, Dict, List, Set, Type # NOQA
+ from docutils.statemachine import State, StateMachine, StringList # NOQA
+ from docutils.utils import Reporter # NOQA
+ from sphinx.config import Config # NOQA
+ from sphinx.environment import BuildEnvironment # NOQA
+ from sphinx.ext.autodoc import Documenter # NOQA
+
+logger = logging.getLogger(__name__)
+
+
+# common option names for autodoc directives
+AUTODOC_DEFAULT_OPTIONS = ['members', 'undoc-members', 'inherited-members',
+ 'show-inheritance', 'private-members', 'special-members',
+ 'ignore-module-all']
+
+
+class DummyOptionSpec(object):
+ """An option_spec allows any options."""
+
+ def __getitem__(self, key):
+ # type: (Any) -> Any
+ return lambda x: x
+
+
+class Options(dict):
+ """A dict/attribute hybrid that returns None on nonexisting keys."""
+ def __getattr__(self, name):
+ # type: (unicode) -> Any
+ try:
+ return self[name.replace('_', '-')]
+ except KeyError:
+ return None
+
+
+class DocumenterBridge(object):
+ """A parameters container for Documenters."""
+
+ def __init__(self, env, reporter, options, lineno):
+ # type: (BuildEnvironment, Reporter, Options, int) -> None
+ self.env = env
+ self.reporter = reporter
+ self.genopt = options
+ self.lineno = lineno
+ self.filename_set = set() # type: Set[unicode]
+ self.result = ViewList()
+
+ def warn(self, msg):
+ # type: (unicode) -> None
+ logger.warning(msg, line=self.lineno)
+
+
+def process_documenter_options(documenter, config, options):
+ # type: (Type[Documenter], Config, Dict) -> Options
+ """Recognize options of Documenter from user input."""
+ for name in AUTODOC_DEFAULT_OPTIONS:
+ if name not in documenter.option_spec:
+ continue
+ else:
+ negated = options.pop('no-' + name, True) is None
+ if name in config.autodoc_default_flags and not negated:
+ options[name] = None
+
+ return Options(assemble_option_dict(options.items(), documenter.option_spec))
+
+
+def parse_generated_content(state, content, documenter):
+ # type: (State, StringList, Documenter) -> List[nodes.Node]
+ """Parse a generated content by Documenter."""
+ with switch_source_input(state, content):
+ if documenter.titles_allowed:
+ node = nodes.section()
+ # necessary so that the child nodes get the right source/line set
+ node.document = state.document
+ nested_parse_with_titles(state, content, node)
+ else:
+ node = nodes.paragraph()
+ node.document = state.document
+ state.nested_parse(content, 0, node)
+
+ return node.children
+
+
+class AutodocDirective(Directive):
+ """A directive class for all autodoc directives. It works as a dispatcher of Documenters.
+
+ It invokes a Documenter on running. After the processing, it parses and returns
+ the generated content by Documenter.
+ """
+ option_spec = DummyOptionSpec()
+ has_content = True
+ required_arguments = 1
+ optional_arguments = 0
+ final_argument_whitespace = True
+
+ def run(self):
+ # type: () -> List[nodes.Node]
+ env = self.state.document.settings.env
+ reporter = self.state.document.reporter
+
+ try:
+ source, lineno = reporter.get_source_and_line(self.lineno)
+ except AttributeError:
+ source, lineno = (None, None)
+ logger.debug('[autodoc] %s:%s: input:\n%s', source, lineno, self.block_text)
+
+ # look up target Documenter
+ objtype = self.name[4:] # strip prefix (auto-).
+ doccls = get_documenters(env.app)[objtype]
+
+ # process the options with the selected documenter's option_spec
+ try:
+ documenter_options = process_documenter_options(doccls, env.config, self.options)
+ except (KeyError, ValueError, TypeError) as exc:
+ # an option is either unknown or has a wrong type
+ logger.error('An option to %s is either unknown or has an invalid value: %s' %
+ (self.name, exc), line=lineno)
+ return []
+
+ # generate the output
+ params = DocumenterBridge(env, reporter, documenter_options, lineno)
+ documenter = doccls(params, self.arguments[0])
+ documenter.generate(more_content=self.content)
+ if not params.result:
+ return []
+
+ logger.debug('[autodoc] output:\n%s', '\n'.join(params.result))
+
+ # record all filenames as dependencies -- this will at least
+ # partially make automatic invalidation possible
+ for fn in params.filename_set:
+ self.state.document.settings.record_dependencies.add(fn)
+
+ result = parse_generated_content(self.state, params.result, documenter)
+ return result
diff --git a/sphinx/ext/autodoc/importer.py b/sphinx/ext/autodoc/importer.py
index 5c28f490d..101cb930f 100644
--- a/sphinx/ext/autodoc/importer.py
+++ b/sphinx/ext/autodoc/importer.py
@@ -5,7 +5,7 @@
Importer utilities for autodoc
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -13,13 +13,17 @@ import sys
import warnings
import traceback
import contextlib
+from collections import namedtuple
from types import FunctionType, MethodType, ModuleType
+from six import PY2
+
from sphinx.util import logging
+from sphinx.util.inspect import isenumclass, safe_getattr
if False:
# For type annotation
- from typing import Any, Generator, List, Set # NOQA
+ from typing import Any, Callable, Dict, Generator, List, Optional, Set # NOQA
logger = logging.getLogger(__name__)
@@ -144,3 +148,86 @@ def import_module(modname, warningiserror=False):
# Importing modules may cause any side effects, including
# SystemExit, so we need to catch all errors.
raise ImportError(exc, traceback.format_exc())
+
+
+def import_object(modname, objpath, objtype='', attrgetter=safe_getattr, warningiserror=False):
+ # type: (str, List[unicode], str, Callable[[Any, unicode], Any], bool) -> Any
+ if objpath:
+ logger.debug('[autodoc] from %s import %s', modname, '.'.join(objpath))
+ else:
+ logger.debug('[autodoc] import %s', modname)
+
+ try:
+ module = import_module(modname, warningiserror=warningiserror)
+ logger.debug('[autodoc] => %r', module)
+ obj = module
+ parent = None
+ object_name = None
+ for attrname in objpath:
+ parent = obj
+ logger.debug('[autodoc] getattr(_, %r)', attrname)
+ obj = attrgetter(obj, attrname)
+ logger.debug('[autodoc] => %r', obj)
+ object_name = attrname
+ return [module, parent, object_name, obj]
+ except (AttributeError, ImportError) as exc:
+ if objpath:
+ errmsg = ('autodoc: failed to import %s %r from module %r' %
+ (objtype, '.'.join(objpath), modname))
+ else:
+ errmsg = 'autodoc: failed to import %s %r' % (objtype, modname)
+
+ if isinstance(exc, ImportError):
+ # import_module() raises ImportError having real exception obj and
+ # traceback
+ real_exc, traceback_msg = exc.args
+ if isinstance(real_exc, SystemExit):
+ errmsg += ('; the module executes module level statement '
+ 'and it might call sys.exit().')
+ elif isinstance(real_exc, ImportError):
+ errmsg += '; the following exception was raised:\n%s' % real_exc.args[0]
+ else:
+ errmsg += '; the following exception was raised:\n%s' % traceback_msg
+ else:
+ errmsg += '; the following exception was raised:\n%s' % traceback.format_exc()
+
+ if PY2:
+ errmsg = errmsg.decode('utf-8') # type: ignore
+ logger.debug(errmsg)
+ raise ImportError(errmsg)
+
+
+Attribute = namedtuple('Attribute', ['name', 'directly_defined', 'value'])
+
+
+def get_object_members(subject, objpath, attrgetter, analyzer=None):
+ # type: (Any, List[unicode], Callable, Any) -> Dict[str, Attribute] # NOQA
+ """Get members and attributes of target object."""
+ # the members directly defined in the class
+ obj_dict = attrgetter(subject, '__dict__', {})
+
+ # Py34 doesn't have enum members in __dict__.
+ if sys.version_info[:2] == (3, 4) and isenumclass(subject):
+ obj_dict = dict(obj_dict)
+ for name, value in subject.__members__.items():
+ obj_dict[name] = value
+
+ members = {}
+ for name in dir(subject):
+ try:
+ value = attrgetter(subject, name)
+ directly_defined = name in obj_dict
+ members[name] = Attribute(name, directly_defined, value)
+ except AttributeError:
+ continue
+
+ if analyzer:
+ # append instance attributes (cf. self.attr1) if analyzer knows
+ from sphinx.ext.autodoc import INSTANCEATTR
+
+ namespace = '.'.join(objpath)
+ for (ns, name) in analyzer.find_attr_docs():
+ if namespace == ns and name not in members:
+ members[name] = Attribute(name, True, INSTANCEATTR)
+
+ return members
diff --git a/sphinx/ext/autodoc/inspector.py b/sphinx/ext/autodoc/inspector.py
index 50c5a9082..6e07c9547 100644
--- a/sphinx/ext/autodoc/inspector.py
+++ b/sphinx/ext/autodoc/inspector.py
@@ -5,7 +5,7 @@
Inspect utilities for autodoc
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/ext/autosectionlabel.py b/sphinx/ext/autosectionlabel.py
index 69b8c6873..fbb7d037f 100644
--- a/sphinx/ext/autosectionlabel.py
+++ b/sphinx/ext/autosectionlabel.py
@@ -5,7 +5,7 @@
Allow reference sections by :ref: role using its title.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/ext/autosummary/__init__.py b/sphinx/ext/autosummary/__init__.py
index 21bfe7b13..7a9e59c73 100644
--- a/sphinx/ext/autosummary/__init__.py
+++ b/sphinx/ext/autosummary/__init__.py
@@ -49,7 +49,7 @@
resolved to a Python object, and otherwise it becomes simple emphasis.
This can be used as the default role to make links 'smart'.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -72,7 +72,8 @@ from sphinx import addnodes
from sphinx.environment.adapters.toctree import TocTree
from sphinx.util import import_object, rst, logging
from sphinx.pycode import ModuleAnalyzer, PycodeError
-from sphinx.ext.autodoc import Options
+from sphinx.ext.autodoc import get_documenters
+from sphinx.ext.autodoc.directive import DocumenterBridge, Options
from sphinx.ext.autodoc.importer import import_module
if False:
@@ -153,13 +154,13 @@ def autosummary_table_visit_html(self, node):
# -- autodoc integration -------------------------------------------------------
-class FakeDirective(object):
- env = {} # type: Dict
- genopt = Options()
+class FakeDirective(DocumenterBridge):
+ def __init__(self):
+ super(FakeDirective, self).__init__({}, None, Options(), 0) # type: ignore
-def get_documenter(obj, parent):
- # type: (Any, Any) -> Type[Documenter]
+def get_documenter(app, obj, parent):
+ # type: (Sphinx, Any, Any) -> Type[Documenter]
"""Get an autodoc.Documenter class suitable for documenting the given
object.
@@ -167,8 +168,7 @@ def get_documenter(obj, parent):
another Python object (e.g. a module or a class) to which *obj*
belongs to.
"""
- from sphinx.ext.autodoc import AutoDirective, DataDocumenter, \
- ModuleDocumenter
+ from sphinx.ext.autodoc import DataDocumenter, ModuleDocumenter
if inspect.ismodule(obj):
# ModuleDocumenter.can_document_member always returns False
@@ -176,7 +176,7 @@ def get_documenter(obj, parent):
# Construct a fake documenter for *parent*
if parent is not None:
- parent_doc_cls = get_documenter(parent, None)
+ parent_doc_cls = get_documenter(app, parent, None)
else:
parent_doc_cls = ModuleDocumenter
@@ -186,7 +186,7 @@ def get_documenter(obj, parent):
parent_doc = parent_doc_cls(FakeDirective(), "")
# Get the corrent documenter class for *obj*
- classes = [cls for cls in AutoDirective._registry.values()
+ classes = [cls for cls in get_documenters(app).values()
if cls.can_document_member(obj, '', False, parent_doc)]
if classes:
classes.sort(key=lambda cls: cls.priority)
@@ -289,7 +289,7 @@ class Autosummary(Directive):
full_name = modname + '::' + full_name[len(modname) + 1:]
# NB. using full_name here is important, since Documenters
# handle module prefixes slightly differently
- documenter = get_documenter(obj, parent)(self, full_name)
+ documenter = get_documenter(self.env.app, obj, parent)(self, full_name)
if not documenter.parse_name():
self.warn('failed to parse name %s' % real_name)
items.append((display_name, '', '', real_name))
@@ -325,7 +325,7 @@ class Autosummary(Directive):
# -- Grab the summary
documenter.add_content(None)
- doc = list(documenter.process_doc([self.result.data]))
+ doc = self.result.data
while doc and not doc[0].strip():
doc.pop(0)
@@ -615,7 +615,8 @@ def process_generate_options(app):
generate_autosummary_docs(genfiles, builder=app.builder,
warn=logger.warning, info=logger.info,
- suffix=suffix, base_path=app.srcdir)
+ suffix=suffix, base_path=app.srcdir,
+ app=app)
def setup(app):
diff --git a/sphinx/ext/autosummary/generate.py b/sphinx/ext/autosummary/generate.py
index f02c50692..aeffcb564 100644
--- a/sphinx/ext/autosummary/generate.py
+++ b/sphinx/ext/autosummary/generate.py
@@ -14,7 +14,7 @@
generate:
sphinx-autogen -o source/generated source/*.rst
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
from __future__ import print_function
@@ -33,24 +33,11 @@ from sphinx import __display_version__
from sphinx import package_dir
from sphinx.ext.autosummary import import_by_name, get_documenter
from sphinx.jinja2glue import BuiltinTemplateLoader
+from sphinx.registry import SphinxComponentRegistry
from sphinx.util.osutil import ensuredir
from sphinx.util.inspect import safe_getattr
from sphinx.util.rst import escape as rst_escape
-# Add documenters to AutoDirective registry
-from sphinx.ext.autodoc import add_documenter, \
- ModuleDocumenter, ClassDocumenter, ExceptionDocumenter, DataDocumenter, \
- FunctionDocumenter, MethodDocumenter, AttributeDocumenter, \
- InstanceAttributeDocumenter
-add_documenter(ModuleDocumenter)
-add_documenter(ClassDocumenter)
-add_documenter(ExceptionDocumenter)
-add_documenter(DataDocumenter)
-add_documenter(FunctionDocumenter)
-add_documenter(MethodDocumenter)
-add_documenter(AttributeDocumenter)
-add_documenter(InstanceAttributeDocumenter)
-
if False:
# For type annotation
from typing import Any, Callable, Dict, Tuple, List # NOQA
@@ -60,6 +47,30 @@ if False:
from sphinx.environment import BuildEnvironment # NOQA
+class DummyApplication(object):
+ """Dummy Application class for sphinx-autogen command."""
+
+ def __init__(self):
+ # type: () -> None
+ self.registry = SphinxComponentRegistry()
+
+
+def setup_documenters(app):
+ # type: (Any) -> None
+ from sphinx.ext.autodoc import (
+ ModuleDocumenter, ClassDocumenter, ExceptionDocumenter, DataDocumenter,
+ FunctionDocumenter, MethodDocumenter, AttributeDocumenter,
+ InstanceAttributeDocumenter
+ )
+ documenters = [
+ ModuleDocumenter, ClassDocumenter, ExceptionDocumenter, DataDocumenter,
+ FunctionDocumenter, MethodDocumenter, AttributeDocumenter,
+ InstanceAttributeDocumenter
+ ]
+ for documenter in documenters:
+ app.registry.add_documenter(documenter.objtype, documenter)
+
+
def _simple_info(msg):
# type: (unicode) -> None
print(msg)
@@ -81,8 +92,8 @@ def _underline(title, line='='):
def generate_autosummary_docs(sources, output_dir=None, suffix='.rst',
warn=_simple_warn, info=_simple_info,
base_path=None, builder=None, template_dir=None,
- imported_members=False):
- # type: (List[unicode], unicode, unicode, Callable, Callable, unicode, Builder, unicode, bool) -> None # NOQA
+ imported_members=False, app=None):
+ # type: (List[unicode], unicode, unicode, Callable, Callable, unicode, Builder, unicode, bool, Any) -> None # NOQA
showed_sources = list(sorted(sources))
if len(showed_sources) > 20:
@@ -148,7 +159,7 @@ def generate_autosummary_docs(sources, output_dir=None, suffix='.rst',
new_files.append(fn)
with open(fn, 'w') as f:
- doc = get_documenter(obj, parent)
+ doc = get_documenter(app, obj, parent)
if template_name is not None:
template = template_env.get_template(template_name)
@@ -167,7 +178,7 @@ def generate_autosummary_docs(sources, output_dir=None, suffix='.rst',
value = safe_getattr(obj, name)
except AttributeError:
continue
- documenter = get_documenter(value, obj)
+ documenter = get_documenter(app, value, obj)
if documenter.objtype == typ:
if typ == 'method':
items.append(name)
@@ -392,11 +403,14 @@ The format of the autosummary directive is documented in the
def main(argv=sys.argv[1:]):
# type: (List[str]) -> None
+ app = DummyApplication()
+ setup_documenters(app)
args = get_parser().parse_args(argv)
generate_autosummary_docs(args.source_file, args.output_dir,
'.' + args.suffix,
template_dir=args.templates,
- imported_members=args.imported_members)
+ imported_members=args.imported_members,
+ app=app)
if __name__ == '__main__':
diff --git a/sphinx/ext/coverage.py b/sphinx/ext/coverage.py
index 02843ac83..476a0ed46 100644
--- a/sphinx/ext/coverage.py
+++ b/sphinx/ext/coverage.py
@@ -6,7 +6,7 @@
Check Python modules and C API for coverage. Mostly written by Josip
Dzolonga for the Google Highly Open Participation contest.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/ext/doctest.py b/sphinx/ext/doctest.py
index 4110d9c90..e0ce050f7 100644
--- a/sphinx/ext/doctest.py
+++ b/sphinx/ext/doctest.py
@@ -6,7 +6,7 @@
Mimic doctest by automatically executing code snippets and checking
their results.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
from __future__ import absolute_import
diff --git a/sphinx/ext/extlinks.py b/sphinx/ext/extlinks.py
index 00180b35c..c247e6722 100644
--- a/sphinx/ext/extlinks.py
+++ b/sphinx/ext/extlinks.py
@@ -20,7 +20,7 @@
You can also give an explicit caption, e.g. :exmpl:`Foo <foo>`.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/ext/githubpages.py b/sphinx/ext/githubpages.py
index 028b65622..7d673a72d 100644
--- a/sphinx/ext/githubpages.py
+++ b/sphinx/ext/githubpages.py
@@ -5,7 +5,7 @@
To publish HTML docs at GitHub Pages, create .nojekyll file.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/ext/graphviz.py b/sphinx/ext/graphviz.py
index 2a83474ce..546594843 100644
--- a/sphinx/ext/graphviz.py
+++ b/sphinx/ext/graphviz.py
@@ -6,7 +6,7 @@
Allow graphviz-formatted graphs to be included in Sphinx-generated
documents inline.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/ext/ifconfig.py b/sphinx/ext/ifconfig.py
index c700649dd..16042ac3f 100644
--- a/sphinx/ext/ifconfig.py
+++ b/sphinx/ext/ifconfig.py
@@ -16,7 +16,7 @@
namespace of the project configuration (that is, all variables from
``conf.py`` are available.)
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/ext/imgconverter.py b/sphinx/ext/imgconverter.py
index d2894b2a3..95f579e36 100644
--- a/sphinx/ext/imgconverter.py
+++ b/sphinx/ext/imgconverter.py
@@ -5,7 +5,7 @@
Image converter extension for Sphinx
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
import subprocess
diff --git a/sphinx/ext/imgmath.py b/sphinx/ext/imgmath.py
index 8bf4fcad5..b5f67e713 100644
--- a/sphinx/ext/imgmath.py
+++ b/sphinx/ext/imgmath.py
@@ -5,7 +5,7 @@
Render math in HTML via dvipng or dvisvgm.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -30,6 +30,7 @@ from sphinx.util.png import read_png_depth, write_png_depth
from sphinx.util.osutil import ensuredir, ENOENT, cd
from sphinx.util.pycompat import sys_encoding
from sphinx.ext.mathbase import setup_math as mathbase_setup, wrap_displaymath
+from sphinx.ext.mathbase import get_node_equation_number
if False:
# For type annotation
@@ -333,7 +334,8 @@ def html_visit_displaymath(self, node):
self.body.append(self.starttag(node, 'div', CLASS='math'))
self.body.append('<p>')
if node['number']:
- self.body.append('<span class="eqno">(%s)' % node['number'])
+ number = get_node_equation_number(self, node)
+ self.body.append('<span class="eqno">(%s)' % number)
self.add_permalink_ref(node, _('Permalink to this equation'))
self.body.append('</span>')
if fname is None:
diff --git a/sphinx/ext/inheritance_diagram.py b/sphinx/ext/inheritance_diagram.py
index a6e6e7aba..8e01a1b28 100644
--- a/sphinx/ext/inheritance_diagram.py
+++ b/sphinx/ext/inheritance_diagram.py
@@ -32,7 +32,7 @@ r"""
The graph is inserted as a PNG+image map into HTML and a PDF in
LaTeX.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/ext/intersphinx.py b/sphinx/ext/intersphinx.py
index 1ee58353c..52683d309 100644
--- a/sphinx/ext/intersphinx.py
+++ b/sphinx/ext/intersphinx.py
@@ -20,7 +20,7 @@
also be specified individually, e.g. if the docs should be buildable
without Internet access.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -304,6 +304,7 @@ def missing_reference(app, env, node, contnode):
in_set = setname
to_try.append((inventories.named_inventory[setname], newtarget))
if domain:
+ node['reftarget'] = newtarget
full_qualified_name = env.get_domain(domain).get_full_qualified_name(node)
if full_qualified_name:
to_try.append((inventories.named_inventory[setname], full_qualified_name))
diff --git a/sphinx/ext/jsmath.py b/sphinx/ext/jsmath.py
index dc57c13c6..a74f0641a 100644
--- a/sphinx/ext/jsmath.py
+++ b/sphinx/ext/jsmath.py
@@ -6,7 +6,7 @@
Set up everything for use of JSMath to display math in HTML
via JavaScript.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -16,6 +16,7 @@ import sphinx
from sphinx.locale import _
from sphinx.application import ExtensionError
from sphinx.ext.mathbase import setup_math as mathbase_setup
+from sphinx.ext.mathbase import get_node_equation_number
def html_visit_math(self, node):
@@ -35,7 +36,8 @@ def html_visit_displaymath(self, node):
if i == 0:
# necessary to e.g. set the id property correctly
if node['number']:
- self.body.append('<span class="eqno">(%s)' % node['number'])
+ number = get_node_equation_number(self, node)
+ self.body.append('<span class="eqno">(%s)' % number)
self.add_permalink_ref(node, _('Permalink to this equation'))
self.body.append('</span>')
self.body.append(self.starttag(node, 'div', CLASS='math'))
diff --git a/sphinx/ext/linkcode.py b/sphinx/ext/linkcode.py
index e74ee8529..a42dab528 100644
--- a/sphinx/ext/linkcode.py
+++ b/sphinx/ext/linkcode.py
@@ -5,7 +5,7 @@
Add external links to module code in Python object descriptions.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/ext/mathbase.py b/sphinx/ext/mathbase.py
index f83ca5da8..f2e15b485 100644
--- a/sphinx/ext/mathbase.py
+++ b/sphinx/ext/mathbase.py
@@ -5,7 +5,7 @@
Set up math support in source files and LaTeX/text output.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -84,6 +84,13 @@ class MathDomain(Domain):
newnode['target'] = target
return newnode
else:
+ if env.config.math_numfig and env.config.numfig:
+ if docname in env.toc_fignumbers:
+ id = 'equation-' + target
+ number = env.toc_fignumbers[docname]['displaymath'].get(id, ())
+ number = '.'.join(map(str, number))
+ else:
+ number = ''
try:
eqref_format = env.config.math_eqref_format or "({number})"
title = nodes.Text(eqref_format.format(number=number))
@@ -126,6 +133,23 @@ class MathDomain(Domain):
return len(targets) + 1
+def get_node_equation_number(writer, node):
+ if writer.builder.config.math_numfig and writer.builder.config.numfig:
+ figtype = 'displaymath'
+ if writer.builder.name == 'singlehtml':
+ key = u"%s/%s" % (writer.docnames[-1], figtype)
+ else:
+ key = figtype
+
+ id = node['ids'][0]
+ number = writer.builder.fignumbers.get(key, {}).get(id, ())
+ number = '.'.join(map(str, number))
+ else:
+ number = node['number']
+
+ return number
+
+
def wrap_displaymath(math, label, numbering):
# type: (unicode, unicode, bool) -> unicode
def is_equation(part):
@@ -341,6 +365,7 @@ def setup_math(app, htmlinlinevisitors, htmldisplayvisitors):
# type: (Sphinx, Tuple[Callable, Any], Tuple[Callable, Any]) -> None
app.add_config_value('math_number_all', False, 'env')
app.add_config_value('math_eqref_format', None, 'env', string_classes)
+ app.add_config_value('math_numfig', True, 'env')
app.add_domain(MathDomain)
app.add_node(math, override=True,
latex=(latex_visit_math, None),
@@ -348,12 +373,12 @@ def setup_math(app, htmlinlinevisitors, htmldisplayvisitors):
man=(man_visit_math, None),
texinfo=(texinfo_visit_math, None),
html=htmlinlinevisitors)
- app.add_node(displaymath,
- latex=(latex_visit_displaymath, None),
- text=(text_visit_displaymath, None),
- man=(man_visit_displaymath, man_depart_displaymath),
- texinfo=(texinfo_visit_displaymath, texinfo_depart_displaymath),
- html=htmldisplayvisitors)
+ app.add_enumerable_node(displaymath, 'displaymath',
+ latex=(latex_visit_displaymath, None),
+ text=(text_visit_displaymath, None),
+ man=(man_visit_displaymath, man_depart_displaymath),
+ texinfo=(texinfo_visit_displaymath, texinfo_depart_displaymath),
+ html=htmldisplayvisitors)
app.add_node(eqref, latex=(latex_visit_eqref, None))
app.add_role('math', math_role)
app.add_role('eq', EqXRefRole(warn_dangling=True))
diff --git a/sphinx/ext/mathjax.py b/sphinx/ext/mathjax.py
index f25f91e74..8698e2801 100644
--- a/sphinx/ext/mathjax.py
+++ b/sphinx/ext/mathjax.py
@@ -7,7 +7,7 @@
Sphinx's HTML writer -- requires the MathJax JavaScript library on your
webserver/computer.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -17,6 +17,7 @@ import sphinx
from sphinx.locale import _
from sphinx.errors import ExtensionError
from sphinx.ext.mathbase import setup_math as mathbase_setup
+from sphinx.ext.mathbase import get_node_equation_number
def html_visit_math(self, node):
@@ -36,7 +37,8 @@ def html_visit_displaymath(self, node):
# necessary to e.g. set the id property correctly
if node['number']:
- self.body.append('<span class="eqno">(%s)' % node['number'])
+ number = get_node_equation_number(self, node)
+ self.body.append('<span class="eqno">(%s)' % number)
self.add_permalink_ref(node, _('Permalink to this equation'))
self.body.append('</span>')
self.body.append(self.builder.config.mathjax_display[0])
diff --git a/sphinx/ext/napoleon/__init__.py b/sphinx/ext/napoleon/__init__.py
index 7aca9b629..b65f7f2a1 100644
--- a/sphinx/ext/napoleon/__init__.py
+++ b/sphinx/ext/napoleon/__init__.py
@@ -5,7 +5,7 @@
Support for NumPy and Google style docstrings.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/ext/napoleon/docstring.py b/sphinx/ext/napoleon/docstring.py
index d3a64049b..b349c761f 100644
--- a/sphinx/ext/napoleon/docstring.py
+++ b/sphinx/ext/napoleon/docstring.py
@@ -7,7 +7,7 @@
Classes for docstring parsing and formatting.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/ext/napoleon/iterators.py b/sphinx/ext/napoleon/iterators.py
index b03bcf047..b4bba8863 100644
--- a/sphinx/ext/napoleon/iterators.py
+++ b/sphinx/ext/napoleon/iterators.py
@@ -7,7 +7,7 @@
A collection of helpful iterators.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
diff --git a/sphinx/ext/pngmath.py b/sphinx/ext/pngmath.py
index 85010b799..717d51756 100644
--- a/sphinx/ext/pngmath.py
+++ b/sphinx/ext/pngmath.py
@@ -6,7 +6,7 @@
Render math in HTML via dvipng. This extension has been deprecated; please
use sphinx.ext.imgmath instead.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -30,6 +30,7 @@ from sphinx.util.png import read_png_depth, write_png_depth
from sphinx.util.osutil import ensuredir, ENOENT, cd
from sphinx.util.pycompat import sys_encoding
from sphinx.ext.mathbase import setup_math as mathbase_setup, wrap_displaymath
+from sphinx.ext.mathbase import get_node_equation_number
if False:
# For type annotation
@@ -242,7 +243,8 @@ def html_visit_displaymath(self, node):
self.body.append(self.starttag(node, 'div', CLASS='math'))
self.body.append('<p>')
if node['number']:
- self.body.append('<span class="eqno">(%s)</span>' % node['number'])
+ number = get_node_equation_number(self, node)
+ self.body.append('<span class="eqno">(%s)</span>' % number)
if fname is None:
# something failed -- use text-only as a bad substitute
self.body.append('<span class="math">%s</span></p>\n</div>' %
diff --git a/sphinx/ext/todo.py b/sphinx/ext/todo.py
index a58422793..e60620b5b 100644
--- a/sphinx/ext/todo.py
+++ b/sphinx/ext/todo.py
@@ -8,7 +8,7 @@
all todos of your project and lists them along with a backlink to the
original location.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
@@ -178,7 +178,8 @@ def process_todo_nodes(app, doctree, fromdocname):
todo_entry = todo_info['todo']
# Remove targetref from the (copied) node to avoid emitting a
# duplicate label of the original entry when we walk this node.
- del todo_entry['targetref']
+ if 'targetref' in todo_entry:
+ del todo_entry['targetref']
# (Recursively) resolve references in the todo content
env.resolve_references(todo_entry, todo_info['docname'],
diff --git a/sphinx/ext/viewcode.py b/sphinx/ext/viewcode.py
index ce67081d3..d4ff7cebb 100644
--- a/sphinx/ext/viewcode.py
+++ b/sphinx/ext/viewcode.py
@@ -5,7 +5,7 @@
Add links to module code in Python object descriptions.
- :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""