summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGES38
-rw-r--r--doc/extdev/deprecated.rst30
-rw-r--r--doc/usage/extensions/autosummary.rst10
-rw-r--r--doc/usage/extensions/ifconfig.rst5
-rw-r--r--doc/usage/extensions/math.rst16
-rw-r--r--sphinx/application.py2
-rw-r--r--sphinx/builders/dirhtml.py9
-rw-r--r--sphinx/builders/html.py22
-rw-r--r--sphinx/builders/htmlhelp.py2
-rw-r--r--sphinx/builders/latex/__init__.py2
-rw-r--r--sphinx/builders/latex/transforms.py7
-rw-r--r--sphinx/builders/singlehtml.py9
-rw-r--r--sphinx/directives/__init__.py2
-rw-r--r--sphinx/domains/citation.py167
-rw-r--r--sphinx/domains/cpp.py4
-rw-r--r--sphinx/domains/python.py118
-rw-r--r--sphinx/domains/std.py224
-rw-r--r--sphinx/ext/autosummary/__init__.py4
-rw-r--r--sphinx/ext/ifconfig.py4
-rw-r--r--sphinx/ext/napoleon/docstring.py8
-rw-r--r--sphinx/templates/quickstart/Makefile.new_t14
-rw-r--r--sphinx/themes/basic/static/searchtools.js10
-rw-r--r--sphinx/themes/bizstyle/static/bizstyle.css_t14
-rw-r--r--sphinx/transforms/__init__.py53
-rw-r--r--sphinx/util/osutil.py22
-rw-r--r--sphinx/writers/html5.py1
-rw-r--r--tests/roots/test-ext-autosummary-imported_members/autosummary_dummy_package/__init__.py1
-rw-r--r--tests/roots/test-ext-autosummary-imported_members/autosummary_dummy_package/autosummary_dummy_module.py8
-rw-r--r--tests/roots/test-ext-autosummary-imported_members/conf.py7
-rw-r--r--tests/roots/test-ext-autosummary-imported_members/index.rst7
-rw-r--r--tests/test_domain_cpp.py14
-rw-r--r--tests/test_domain_std.py159
-rw-r--r--tests/test_environment_indexentries.py203
-rw-r--r--tests/test_ext_autosummary.py20
-rw-r--r--tests/test_ext_inheritance_diagram.py14
-rw-r--r--tests/test_ext_napoleon_docstring.py9
36 files changed, 858 insertions, 381 deletions
diff --git a/CHANGES b/CHANGES
index 33cc8ccf7..359b70694 100644
--- a/CHANGES
+++ b/CHANGES
@@ -32,6 +32,8 @@ Incompatible changes
* Ignore filenames without file extension given to ``Builder.build_specific()``
API directly
+* #6230: The anchor of term in glossary directive is changed if it is consisted
+ by non-ASCII characters
Deprecated
----------
@@ -54,11 +56,17 @@ Deprecated
* ``sphinx.directives.TabularColumns``
* ``sphinx.directives.TocTree``
* ``sphinx.directives.VersionChange``
+* ``sphinx.domains.std.StandardDomain._resolve_citation_xref()``
+* ``sphinx.domains.std.StandardDomain.note_citations()``
+* ``sphinx.domains.std.StandardDomain.note_citation_refs()``
+* ``sphinx.domains.std.StandardDomain.note_labels()``
* ``sphinx.environment.NoUri``
* ``sphinx.ext.autodoc.importer.MockFinder``
* ``sphinx.ext.autodoc.importer.MockLoader``
* ``sphinx.ext.autodoc.importer.mock()``
* ``sphinx.ext.autosummary.autolink_role()``
+* ``sphinx.transforms.CitationReferences``
+* ``sphinx.transforms.SmartQuotesSkipper``
* ``sphinx.util.docfields.DocFieldTransformer.preprocess_fieldtypes()``
* ``sphinx.util.node.find_source_node()``
* ``sphinx.util.i18n.find_catalog()``
@@ -71,14 +79,27 @@ Features added
--------------
* Add a helper class ``sphinx.transforms.post_transforms.SphinxPostTransform``
-* Add a helper method ``SphinxDirective.set_source_info()``
+* Add helper methods
+
+ - ``PythonDomain.note_module()``
+ - ``PythonDomain.note_object()``
+ - ``SphinxDirective.set_source_info()``
+
* #6180: Support ``--keep-going`` with BuildDoc setup command
* ``math`` directive now supports ``:class:`` option
* todo: ``todo`` directive now supports ``:name:`` option
+* #6232: Enable CLI override of Makefile variables
+* #6212 autosummary: Add :confval:`autosummary_imported_members` to display
+ imported members on autosummary
Bugs fixed
----------
+* #6230: Inappropriate node_id has been generated by glossary directive if term
+ is consisted by non-ASCII characters
+* #6213: ifconfig: contents after headings are not shown
+* commented term in glossary directive is wrongly recognized
+
Testing
--------
@@ -101,6 +122,21 @@ Bugs fixed
----------
* LaTeX: some system labels are not translated
+* RemovedInSphinx30Warning is marked as pending
+* deprecation warnings are not emitted
+
+ - sphinx.application.CONFIG_FILENAME
+ - sphinx.builders.htmlhelp
+ - :confval:`viewcode_import`
+
+* #6208: C++, properly parse full xrefs that happen to have a short xref as prefix.
+* #6220, #6225: napoleon: AttributeError is raised for raised section having
+ references
+* #6245: circular import error on importing SerializingHTMLBuilder
+* #6243: LaTeX: 'releasename' setting for latex_elements is ignored
+* #6244: html: Search function is broken with 3rd party themes
+* #6263: html: HTML5Translator crashed with invalid field node
+* #6262: html theme: The style of field lists has changed in bizstyle theme
Testing
--------
diff --git a/doc/extdev/deprecated.rst b/doc/extdev/deprecated.rst
index 99abc56eb..beeafab08 100644
--- a/doc/extdev/deprecated.rst
+++ b/doc/extdev/deprecated.rst
@@ -116,6 +116,26 @@ The following is a list of deprecated interfaces.
- 4.0
- ``sphinx.directives.other.VersionChange``
+ * - ``sphinx.domains.std.StandardDomain._resolve_citation_xref()``
+ - 2.1
+ - 4.0
+ - ``sphinx.domains.citation.CitationDomain.resolve_xref()``
+
+ * - ``sphinx.domains.std.StandardDomain.note_citations()``
+ - 2.1
+ - 4.0
+ - ``sphinx.domains.citation.CitationDomain.note_citation()``
+
+ * - ``sphinx.domains.std.StandardDomain.note_citation_refs()``
+ - 2.1
+ - 4.0
+ - ``sphinx.domains.citation.CitationDomain.note_citation_reference()``
+
+ * - ``sphinx.domains.std.StandardDomain.note_labels()``
+ - 2.1
+ - 4.0
+ - ``sphinx.domains.std.StandardDomain.process_doc()``
+
* - ``sphinx.environment.NoUri``
- 2.1
- 4.0
@@ -141,6 +161,16 @@ The following is a list of deprecated interfaces.
- 4.0
- ``sphinx.ext.autosummary.AutoLink``
+ * - ``sphinx.transforms.CitationReferences``
+ - 2.1
+ - 4.0
+ - ``sphinx.domains.citation.CitationReferenceTransform``
+
+ * - ``sphinx.transforms.SmartQuotesSkipper``
+ - 2.1
+ - 4.0
+ - ``sphinx.domains.citation.CitationDefinitionTransform``
+
* - ``sphinx.util.docfields.DocFieldTransformer.preprocess_fieldtypes()``
- 2.1
- 4.0
diff --git a/doc/usage/extensions/autosummary.rst b/doc/usage/extensions/autosummary.rst
index d1ac0ad32..16a8cea7e 100644
--- a/doc/usage/extensions/autosummary.rst
+++ b/doc/usage/extensions/autosummary.rst
@@ -149,6 +149,16 @@ also use these config values:
:confval:`autodoc_mock_imports` for more details. It defaults to
:confval:`autodoc_mock_imports`.
+ .. versionadded:: 2.0
+
+.. confval:: autosummary_imported_members
+
+ A boolean flag indicating whether to document classes and functions imported
+ in modules. Default is ``False``
+
+ .. versionadded:: 2.1
+
+
Customizing templates
---------------------
diff --git a/doc/usage/extensions/ifconfig.rst b/doc/usage/extensions/ifconfig.rst
index f64ca6c58..2bd9d0e3b 100644
--- a/doc/usage/extensions/ifconfig.rst
+++ b/doc/usage/extensions/ifconfig.rst
@@ -8,6 +8,11 @@
This extension is quite simple, and features only one directive:
+.. warning::
+
+ This directive is designed to control only content of document. It could
+ not control sections, labels and so on.
+
.. rst:directive:: ifconfig
Include content of the directive only if the Python expression given as an
diff --git a/doc/usage/extensions/math.rst b/doc/usage/extensions/math.rst
index 9e62c1425..e6ccc2941 100644
--- a/doc/usage/extensions/math.rst
+++ b/doc/usage/extensions/math.rst
@@ -15,7 +15,8 @@ Math support for HTML outputs in Sphinx
So mathbase extension is no longer needed.
Since mathematical notation isn't natively supported by HTML in any way, Sphinx
-gives a math support to HTML document with several extensions.
+gives a math support to HTML document with several extensions. These use the
+reStructuredText math :rst:dir:`directive <math>` and :rst:role:`role <math>`.
:mod:`sphinx.ext.imgmath` -- Render math as images
--------------------------------------------------
@@ -131,7 +132,13 @@ MathJax_ is then loaded and transforms the LaTeX markup to readable math live in
the browser.
Because MathJax (and the necessary fonts) is very large, it is not included in
-Sphinx.
+Sphinx but is set to automatically include it from a third-party site.
+
+.. attention::
+
+ You should use the math :rst:dir:`directive <math>` and
+ :rst:role:`role <math>`, not the native MathJax ``$$``, ``\(``, etc.
+
.. confval:: mathjax_path
@@ -140,8 +147,9 @@ Sphinx.
The default is the ``https://`` URL that loads the JS files from the
`cdnjs`__ Content Delivery Network. See the `MathJax Getting Started
- page`__ for details. If you want MathJax to be available offline, you have
- to download it and set this value to a different path.
+ page`__ for details. If you want MathJax to be available offline or
+ without including resources from a third-party site, you have to
+ download it and set this value to a different path.
__ https://cdnjs.com
diff --git a/sphinx/application.py b/sphinx/application.py
index 516b7be58..afcdc02ed 100644
--- a/sphinx/application.py
+++ b/sphinx/application.py
@@ -23,7 +23,6 @@ from docutils.parsers.rst import Directive, roles
import sphinx
from sphinx import package_dir, locale
from sphinx.config import Config
-from sphinx.config import CONFIG_FILENAME # NOQA # for compatibility (RemovedInSphinx30)
from sphinx.deprecation import RemovedInSphinx40Warning
from sphinx.environment import BuildEnvironment
from sphinx.errors import ApplicationError, ConfigError, VersionRequirementError
@@ -73,6 +72,7 @@ builtin_extensions = (
'sphinx.config',
'sphinx.domains.c',
'sphinx.domains.changeset',
+ 'sphinx.domains.citation',
'sphinx.domains.cpp',
'sphinx.domains.javascript',
'sphinx.domains.math',
diff --git a/sphinx/builders/dirhtml.py b/sphinx/builders/dirhtml.py
index 7ab6fad98..d5d61c273 100644
--- a/sphinx/builders/dirhtml.py
+++ b/sphinx/builders/dirhtml.py
@@ -11,6 +11,7 @@
from os import path
from sphinx.builders.html import StandaloneHTMLBuilder
+from sphinx.deprecation import RemovedInSphinx40Warning, deprecated_alias
from sphinx.util import logging
from sphinx.util.osutil import SEP, os_path
@@ -55,6 +56,14 @@ class DirectoryHTMLBuilder(StandaloneHTMLBuilder):
self.globalcontext['no_search_suffix'] = True
+# for compatibility
+deprecated_alias('sphinx.builders.html',
+ {
+ 'DirectoryHTMLBuilder': DirectoryHTMLBuilder,
+ },
+ RemovedInSphinx40Warning)
+
+
def setup(app):
# type: (Sphinx) -> Dict[str, Any]
app.setup_extension('sphinx.builders.html')
diff --git a/sphinx/builders/html.py b/sphinx/builders/html.py
index 5621f9a75..97774c668 100644
--- a/sphinx/builders/html.py
+++ b/sphinx/builders/html.py
@@ -24,7 +24,7 @@ from docutils.utils import relative_path
from sphinx import package_dir, __display_version__
from sphinx.builders import Builder
-from sphinx.deprecation import RemovedInSphinx40Warning, deprecated_alias
+from sphinx.deprecation import RemovedInSphinx40Warning
from sphinx.environment.adapters.asset import ImageAdapter
from sphinx.environment.adapters.indexentries import IndexEntries
from sphinx.environment.adapters.toctree import TocTree
@@ -1187,23 +1187,9 @@ def validate_math_renderer(app):
# for compatibility
-from sphinx.builders.dirhtml import DirectoryHTMLBuilder # NOQA
-from sphinx.builders.singlehtml import SingleFileHTMLBuilder # NOQA
-from sphinxcontrib.serializinghtml import ( # NOQA
- LAST_BUILD_FILENAME, JSONHTMLBuilder, PickleHTMLBuilder, SerializingHTMLBuilder
-)
-
-deprecated_alias('sphinx.builders.html',
- {
- 'LAST_BUILD_FILENAME': LAST_BUILD_FILENAME,
- 'DirectoryHTMLBuilder': DirectoryHTMLBuilder,
- 'JSONHTMLBuilder': JSONHTMLBuilder,
- 'PickleHTMLBuilder': PickleHTMLBuilder,
- 'SerializingHTMLBuilder': SerializingHTMLBuilder,
- 'SingleFileHTMLBuilder': SingleFileHTMLBuilder,
- 'WebHTMLBuilder': PickleHTMLBuilder,
- },
- RemovedInSphinx40Warning)
+import sphinx.builders.dirhtml # NOQA
+import sphinx.builders.singlehtml # NOQA
+import sphinxcontrib.serializinghtml # NOQA
def setup(app):
diff --git a/sphinx/builders/htmlhelp.py b/sphinx/builders/htmlhelp.py
index 2e7e8f083..be365ef7e 100644
--- a/sphinx/builders/htmlhelp.py
+++ b/sphinx/builders/htmlhelp.py
@@ -24,7 +24,7 @@ if False:
from sphinx.application import Sphinx # NOQA
-deprecated_alias('sphinx.builders.devhelp',
+deprecated_alias('sphinx.builders.htmlhelp',
{
'chm_locales': chm_locales,
'chm_htmlescape': chm_htmlescape,
diff --git a/sphinx/builders/latex/__init__.py b/sphinx/builders/latex/__init__.py
index e6467601d..973e7c67d 100644
--- a/sphinx/builders/latex/__init__.py
+++ b/sphinx/builders/latex/__init__.py
@@ -208,7 +208,7 @@ class LaTeXBuilder(Builder):
self.context['indexname'] = _('Index')
if self.config.release:
# Show the release label only if release value exists
- self.context['releasename'] = _('Release')
+ self.context.setdefault('releasename', _('Release'))
def init_babel(self):
# type: () -> None
diff --git a/sphinx/builders/latex/transforms.py b/sphinx/builders/latex/transforms.py
index 746446fbc..6381780ae 100644
--- a/sphinx/builders/latex/transforms.py
+++ b/sphinx/builders/latex/transforms.py
@@ -16,6 +16,7 @@ from sphinx import addnodes
from sphinx.builders.latex.nodes import (
captioned_literal_block, footnotemark, footnotetext, math_reference, thebibliography
)
+from sphinx.domains.citation import CitationDomain
from sphinx.transforms import SphinxTransform
from sphinx.transforms.post_transforms import SphinxPostTransform
from sphinx.util.nodes import NodeMatcher
@@ -545,10 +546,10 @@ class CitationReferenceTransform(SphinxPostTransform):
def run(self, **kwargs):
# type: (Any) -> None
- matcher = NodeMatcher(addnodes.pending_xref, refdomain='std', reftype='citation')
- citations = self.env.get_domain('std').data['citations']
+ domain = cast(CitationDomain, self.env.get_domain('citation'))
+ matcher = NodeMatcher(addnodes.pending_xref, refdomain='citation', reftype='ref')
for node in self.document.traverse(matcher): # type: addnodes.pending_xref
- docname, labelid, _ = citations.get(node['reftarget'], ('', '', 0))
+ docname, labelid, _ = domain.citations.get(node['reftarget'], ('', '', 0))
if docname:
citation_ref = nodes.citation_reference('', '', *node.children,
docname=docname, refname=labelid)
diff --git a/sphinx/builders/singlehtml.py b/sphinx/builders/singlehtml.py
index 1ee5a37b1..068d1c1c2 100644
--- a/sphinx/builders/singlehtml.py
+++ b/sphinx/builders/singlehtml.py
@@ -13,6 +13,7 @@ from os import path
from docutils import nodes
from sphinx.builders.html import StandaloneHTMLBuilder
+from sphinx.deprecation import RemovedInSphinx40Warning, deprecated_alias
from sphinx.environment.adapters.toctree import TocTree
from sphinx.locale import __
from sphinx.util import logging
@@ -201,6 +202,14 @@ class SingleFileHTMLBuilder(StandaloneHTMLBuilder):
self.handle_page('opensearch', {}, 'opensearch.xml', outfilename=fn)
+# for compatibility
+deprecated_alias('sphinx.builders.html',
+ {
+ 'SingleFileHTMLBuilder': SingleFileHTMLBuilder,
+ },
+ RemovedInSphinx40Warning)
+
+
def setup(app):
# type: (Sphinx) -> Dict[str, Any]
app.setup_extension('sphinx.builders.html')
diff --git a/sphinx/directives/__init__.py b/sphinx/directives/__init__.py
index fd7bec586..40f838c48 100644
--- a/sphinx/directives/__init__.py
+++ b/sphinx/directives/__init__.py
@@ -166,7 +166,7 @@ class ObjectDescription(SphinxDirective):
node['objtype'] = node['desctype'] = self.objtype
node['noindex'] = noindex = ('noindex' in self.options)
- self.names = [] # type: List[str]
+ self.names = [] # type: List[Any]
signatures = self.get_signatures()
for i, sig in enumerate(signatures):
# add a signature node for each signature in the current unit
diff --git a/sphinx/domains/citation.py b/sphinx/domains/citation.py
new file mode 100644
index 000000000..2bb49def9
--- /dev/null
+++ b/sphinx/domains/citation.py
@@ -0,0 +1,167 @@
+"""
+ sphinx.domains.citation
+ ~~~~~~~~~~~~~~~~~~~~~~~
+
+ The citation domain.
+
+ :copyright: Copyright 2007-2019 by the Sphinx team, see AUTHORS.
+ :license: BSD, see LICENSE for details.
+"""
+
+from typing import cast
+
+from docutils import nodes
+
+from sphinx import addnodes
+from sphinx.domains import Domain
+from sphinx.locale import __
+from sphinx.transforms import SphinxTransform
+from sphinx.util import logging
+from sphinx.util.nodes import copy_source_info, make_refnode
+
+if False:
+ # For type annotation
+ from typing import Any, Dict, List, Set, Tuple, Union # NOQA
+ from sphinx.application import Sphinx # NOQA
+ from sphinx.builders import Builder # NOQA
+ from sphinx.environment import BuildEnvironment # NOQA
+
+logger = logging.getLogger(__name__)
+
+
+class CitationDomain(Domain):
+ """Domain for citations."""
+
+ name = 'citation'
+ label = 'citation'
+
+ dangling_warnings = {
+ 'ref': 'citation not found: %(target)s',
+ }
+
+ @property
+ def citations(self):
+ # type: () -> Dict[str, Tuple[str, str, int]]
+ return self.data.setdefault('citations', {})
+
+ @property
+ def citation_refs(self):
+ # type: () -> Dict[str, Set[str]]
+ return self.data.setdefault('citation_refs', {})
+
+ def clear_doc(self, docname):
+ # type: (str) -> None
+ for key, (fn, _l, lineno) in list(self.citations.items()):
+ if fn == docname:
+ del self.citations[key]
+ for key, docnames in list(self.citation_refs.items()):
+ if docnames == {docname}:
+ del self.citation_refs[key]
+ elif docname in docnames:
+ docnames.remove(docname)
+
+ def merge_domaindata(self, docnames, otherdata):
+ # type: (List[str], Dict) -> None
+ # XXX duplicates?
+ for key, data in otherdata['citations'].items():
+ if data[0] in docnames:
+ self.citations[key] = data
+ for key, data in otherdata['citation_refs'].items():
+ citation_refs = self.citation_refs.setdefault(key, set())
+ for docname in data:
+ if docname in docnames:
+ citation_refs.add(docname)
+
+ def note_citation(self, node):
+ # type: (nodes.citation) -> None
+ label = node[0].astext()
+ if label in self.citations:
+ path = self.env.doc2path(self.citations[label][0])
+ logger.warning(__('duplicate citation %s, other instance in %s'), label, path,
+ location=node, type='ref', subtype='citation')
+ self.citations[label] = (node['docname'], node['ids'][0], node.line)
+
+ def note_citation_reference(self, node):
+ # type: (addnodes.pending_xref) -> None
+ docnames = self.citation_refs.setdefault(node['reftarget'], set())
+ docnames.add(self.env.docname)
+
+ def check_consistency(self):
+ # type: () -> None
+ for name, (docname, labelid, lineno) in self.citations.items():
+ if name not in self.citation_refs:
+ logger.warning(__('Citation [%s] is not referenced.'), name,
+ type='ref', subtype='citation', location=(docname, lineno))
+
+ def resolve_xref(self, env, fromdocname, builder, typ, target, node, contnode):
+ # type: (BuildEnvironment, str, Builder, str, str, addnodes.pending_xref, nodes.Element) -> nodes.Element # NOQA
+ docname, labelid, lineno = self.citations.get(target, ('', '', 0))
+ if not docname:
+ return None
+
+ return make_refnode(builder, fromdocname, docname,
+ labelid, contnode)
+
+ def resolve_any_xref(self, env, fromdocname, builder, target, node, contnode):
+ # type: (BuildEnvironment, str, Builder, str, addnodes.pending_xref, nodes.Element) -> List[Tuple[str, nodes.Element]] # NOQA
+ refnode = self.resolve_xref(env, fromdocname, builder, 'ref', target, node, contnode)
+ if refnode is None:
+ return []
+ else:
+ return [('ref', refnode)]
+
+
+class CitationDefinitionTransform(SphinxTransform):
+ """Mark citation definition labels as not smartquoted."""
+ default_priority = 619
+
+ def apply(self, **kwargs):
+ # type: (Any) -> None
+ domain = cast(CitationDomain, self.env.get_domain('citation'))
+ for node in self.document.traverse(nodes.citation):
+ # register citation node to domain
+ node['docname'] = self.env.docname
+ domain.note_citation(node)
+
+ # mark citation labels as not smartquoted
+ label = cast(nodes.label, node[0])
+ label['support_smartquotes'] = False
+
+
+class CitationReferenceTransform(SphinxTransform):
+ """
+ Replace citation references by pending_xref nodes before the default
+ docutils transform tries to resolve them.
+ """
+ default_priority = 619
+
+ def apply(self, **kwargs):
+ # type: (Any) -> None
+ domain = cast(CitationDomain, self.env.get_domain('citation'))
+ for node in self.document.traverse(nodes.citation_reference):
+ target = node.astext()
+ ref = addnodes.pending_xref(target, refdomain='citation', reftype='ref',
+ reftarget=target, refwarn=True,
+ support_smartquotes=False,
+ ids=node["ids"],
+ classes=node.get('classes', []))
+ ref += nodes.inline(target, '[%s]' % target)
+ copy_source_info(node, ref)
+ node.replace_self(ref)
+
+ # register reference node to domain
+ domain.note_citation_reference(ref)
+
+
+def setup(app):
+ # type: (Sphinx) -> Dict[str, Any]
+ app.add_domain(CitationDomain)
+ app.add_transform(CitationDefinitionTransform)
+ app.add_transform(CitationReferenceTransform)
+
+ return {
+ 'version': 'builtin',
+ 'env_version': 1,
+ 'parallel_read_safe': True,
+ 'parallel_write_safe': True,
+ }
diff --git a/sphinx/domains/cpp.py b/sphinx/domains/cpp.py
index 6760328d4..a9f4b1c24 100644
--- a/sphinx/domains/cpp.py
+++ b/sphinx/domains/cpp.py
@@ -6391,6 +6391,7 @@ class DefinitionParser:
# if there are '()' left, just skip them
self.skip_ws()
self.skip_string('()')
+ self.assert_end()
templatePrefix = self._check_template_consistency(name, templatePrefix,
fullSpecShorthand=True)
res1 = ASTNamespace(name, templatePrefix)
@@ -6403,6 +6404,7 @@ class DefinitionParser:
# if there are '()' left, just skip them
self.skip_ws()
self.skip_string('()')
+ self.assert_end()
return res2, False
except DefinitionError as e2:
errs = []
@@ -7145,7 +7147,6 @@ class CPPDomain(Domain):
parser = DefinitionParser(target, warner, env.config)
try:
ast, isShorthand = parser.parse_xref_object()
- parser.assert_end()
except DefinitionError as e:
def findWarning(e): # as arg to stop flake8 from complaining
if typ != 'any' and typ != 'func':
@@ -7154,7 +7155,6 @@ class CPPDomain(Domain):
parser2 = DefinitionParser(target[:-2], warner, env.config)
try:
parser2.parse_xref_object()
- parser2.assert_end()
except DefinitionError as e2:
return target[:-2], e2
# strange, that we don't get the error now, use the original
diff --git a/sphinx/domains/python.py b/sphinx/domains/python.py
index 2203ee6e3..1e334959f 100644
--- a/sphinx/domains/python.py
+++ b/sphinx/domains/python.py
@@ -9,6 +9,7 @@
"""
import re
+from typing import cast
from docutils import nodes
from docutils.parsers.rst import directives
@@ -309,14 +310,13 @@ class PyObject(ObjectDescription):
return fullname, prefix
def get_index_text(self, modname, name):
- # type: (str, str) -> str
+ # type: (str, Tuple[str, str]) -> str
"""Return the text for the index entry of the object."""
raise NotImplementedError('must be implemented in subclasses')
def add_target_and_index(self, name_cls, sig, signode):
- # type: (str, str, addnodes.desc_signature) -> None
- modname = self.options.get(
- 'module', self.env.ref_context.get('py:module'))
+ # type: (Tuple[str, str], str, addnodes.desc_signature) -> None
+ modname = self.options.get('module', self.env.ref_context.get('py:module'))
fullname = (modname and modname + '.' or '') + name_cls[0]
# note target
if fullname not in self.state.document.ids:
@@ -324,15 +324,9 @@ class PyObject(ObjectDescription):
signode['ids'].append(fullname)
signode['first'] = (not self.names)
self.state.document.note_explicit_target(signode)
- objects = self.env.domaindata['py']['objects']
- if fullname in objects:
- self.state_machine.reporter.warning(
- 'duplicate object description of %s, ' % fullname +
- 'other instance in ' +
- self.env.doc2path(objects[fullname][0]) +
- ', use :noindex: for one of them',
- line=self.lineno)
- objects[fullname] = (self.env.docname, self.objtype)
+
+ domain = cast(PythonDomain, self.env.get_domain('py'))
+ domain.note_object(fullname, self.objtype)
indextext = self.get_index_text(modname, name_cls)
if indextext:
@@ -410,7 +404,7 @@ class PyModulelevel(PyObject):
return self.objtype == 'function'
def get_index_text(self, modname, name_cls):
- # type: (str, str) -> str
+ # type: (str, Tuple[str, str]) -> str
if self.objtype == 'function':
if not modname:
return _('%s() (built-in function)') % name_cls[0]
@@ -435,7 +429,7 @@ class PyClasslike(PyObject):
return self.objtype + ' '
def get_index_text(self, modname, name_cls):
- # type: (str, str) -> str
+ # type: (str, Tuple[str, str]) -> str
if self.objtype == 'class':
if not modname:
return _('%s (built-in class)') % name_cls[0]
@@ -464,7 +458,7 @@ class PyClassmember(PyObject):
return ''
def get_index_text(self, modname, name_cls):
- # type: (str, str) -> str
+ # type: (str, Tuple[str, str]) -> str
name, cls = name_cls
add_modules = self.env.config.add_module_names
if self.objtype == 'method':
@@ -575,18 +569,20 @@ class PyModule(SphinxDirective):
def run(self):
# type: () -> List[nodes.Node]
+ domain = cast(PythonDomain, self.env.get_domain('py'))
+
modname = self.arguments[0].strip()
noindex = 'noindex' in self.options
self.env.ref_context['py:module'] = modname
ret = [] # type: List[nodes.Node]
if not noindex:
- self.env.domaindata['py']['modules'][modname] = (self.env.docname,
- self.options.get('synopsis', ''),
- self.options.get('platform', ''),
- 'deprecated' in self.options)
- # make a duplicate entry in 'objects' to facilitate searching for
- # the module in PythonDomain.find_obj()
- self.env.domaindata['py']['objects'][modname] = (self.env.docname, 'module')
+ # note module to the domain
+ domain.note_module(modname,
+ self.options.get('synopsis', ''),
+ self.options.get('platform', ''),
+ 'deprecated' in self.options)
+ domain.note_object(modname, 'module')
+
targetnode = nodes.target('', '', ids=['module-' + modname],
ismod=True)
self.state.document.note_explicit_target(targetnode)
@@ -769,24 +765,55 @@ class PythonDomain(Domain):
PythonModuleIndex,
]
+ @property
+ def objects(self):
+ # type: () -> Dict[str, Tuple[str, str]]
+ return self.data.setdefault('objects', {}) # fullname -> docname, objtype
+
+ def note_object(self, name, objtype, location=None):
+ # type: (str, str, Any) -> None
+ """Note a python object for cross reference.
+
+ .. versionadded:: 2.1
+ """
+ if name in self.objects:
+ docname = self.objects[name][0]
+ logger.warning(__('duplicate object description of %s, '
+ 'other instance in %s, use :noindex: for one of them'),
+ name, docname, location=location)
+ self.objects[name] = (self.env.docname, objtype)
+
+ @property
+ def modules(self):
+ # type: () -> Dict[str, Tuple[str, str, str, bool]]
+ return self.data.setdefault('modules', {}) # modname -> docname, synopsis, platform, deprecated # NOQA
+
+ def note_module(self, name, synopsis, platform, deprecated):
+ # type: (str, str, str, bool) -> None
+ """Note a python module for cross reference.
+
+ .. versionadded:: 2.1
+ """
+ self.modules[name] = (self.env.docname, synopsis, platform, deprecated)
+
def clear_doc(self, docname):
# type: (str) -> None
- for fullname, (fn, _l) in list(self.data['objects'].items()):
+ for fullname, (fn, _l) in list(self.objects.items()):
if fn == docname:
- del self.data['objects'][fullname]
- for modname, (fn, _x, _x, _x) in list(self.data['modules'].items()):
+ del self.objects[fullname]
+ for modname, (fn, _x, _x, _y) in list(self.modules.items()):
if fn == docname:
- del self.data['modules'][modname]
+ del self.modules[modname]
def merge_domaindata(self, docnames, otherdata):
# type: (List[str], Dict) -> None
# XXX check duplicates?
for fullname, (fn, objtype) in otherdata['objects'].items():
if fn in docnames:
- self.data['objects'][fullname] = (fn, objtype)
+ self.objects[fullname] = (fn, objtype)
for modname, data in otherdata['modules'].items():
if data[0] in docnames:
- self.data['modules'][modname] = data
+ self.modules[modname] = data
def find_obj(self, env, modname, classname, name, type, searchmode=0):
# type: (BuildEnvironment, str, str, str, str, int) -> List[Tuple[str, Any]]
@@ -800,7 +827,6 @@ class PythonDomain(Domain):
if not name:
return []
- objects = self.data['objects']
matches = [] # type: List[Tuple[str, Any]]
newname = None
@@ -812,44 +838,44 @@ class PythonDomain(Domain):
if objtypes is not None:
if modname and classname:
fullname = modname + '.' + classname + '.' + name
- if fullname in objects and objects[fullname][1] in objtypes:
+ if fullname in self.objects and self.objects[fullname][1] in objtypes:
newname = fullname
if not newname:
- if modname and modname + '.' + name in objects and \
- objects[modname + '.' + name][1] in objtypes:
+ if modname and modname + '.' + name in self.objects and \
+ self.objects[modname + '.' + name][1] in objtypes:
newname = modname + '.' + name
- elif name in objects and objects[name][1] in objtypes:
+ elif name in self.objects and self.objects[name][1] in objtypes:
newname = name
else:
# "fuzzy" searching mode
searchname = '.' + name
- matches = [(oname, objects[oname]) for oname in objects
+ matches = [(oname, self.objects[oname]) for oname in self.objects
if oname.endswith(searchname) and
- objects[oname][1] in objtypes]
+ self.objects[oname][1] in objtypes]
else:
# NOTE: searching for exact match, object type is not considered
- if name in objects:
+ if name in self.objects:
newname = name
elif type == 'mod':
# only exact matches allowed for modules
return []
- elif classname and classname + '.' + name in objects:
+ elif classname and classname + '.' + name in self.objects:
newname = classname + '.' + name
- elif modname and modname + '.' + name in objects:
+ elif modname and modname + '.' + name in self.objects:
newname = modname + '.' + name
elif modname and classname and \
- modname + '.' + classname + '.' + name in objects:
+ modname + '.' + classname + '.' + name in self.objects:
newname = modname + '.' + classname + '.' + name
# special case: builtin exceptions have module "exceptions" set
elif type == 'exc' and '.' not in name and \
- 'exceptions.' + name in objects:
+ 'exceptions.' + name in self.objects:
newname = 'exceptions.' + name
# special case: object methods
elif type in ('func', 'meth') and '.' not in name and \
- 'object.' + name in objects:
+ 'object.' + name in self.objects:
newname = 'object.' + name
if newname is not None:
- matches.append((newname, objects[newname]))
+ matches.append((newname, self.objects[newname]))
return matches
def resolve_xref(self, env, fromdocname, builder,
@@ -896,7 +922,7 @@ class PythonDomain(Domain):
def _make_module_refnode(self, builder, fromdocname, name, contnode):
# type: (Builder, str, str, nodes.Node) -> nodes.Element
# get additional info for modules
- docname, synopsis, platform, deprecated = self.data['modules'][name]
+ docname, synopsis, platform, deprecated = self.modules[name]
title = name
if synopsis:
title += ': ' + synopsis
@@ -909,9 +935,9 @@ class PythonDomain(Domain):
def get_objects(self):
# type: () -> Iterator[Tuple[str, str, str, str, str, int]]
- for modname, info in self.data['modules'].items():
+ for modname, info in self.modules.items():
yield (modname, modname, 'module', info[0], 'module-' + modname, 0)
- for refname, (docname, type) in self.data['objects'].items():
+ for refname, (docname, type) in self.objects.items():
if type != 'module': # modules are already handled
yield (refname, refname, type, docname, refname, 1)
diff --git a/sphinx/domains/std.py b/sphinx/domains/std.py
index e73c660e6..6574c9b76 100644
--- a/sphinx/domains/std.py
+++ b/sphinx/domains/std.py
@@ -18,6 +18,7 @@ from docutils.parsers.rst import directives
from docutils.statemachine import StringList
from sphinx import addnodes
+from sphinx.deprecation import RemovedInSphinx40Warning
from sphinx.directives import ObjectDescription
from sphinx.domains import Domain, ObjType
from sphinx.errors import NoUri
@@ -255,6 +256,9 @@ def make_glossary_term(env, textnodes, index_key, source, lineno, new_id=None):
termtext = term.astext()
if new_id is None:
new_id = nodes.make_id('term-' + termtext)
+ if new_id == 'term':
+ # the term is not good for node_id. Generate it by sequence number instead.
+ new_id = 'term-' + str(len(gloss_entries))
if new_id in gloss_entries:
new_id = 'term-' + str(len(gloss_entries))
gloss_entries.add(new_id)
@@ -300,6 +304,7 @@ class Glossary(SphinxDirective):
# first, collect single entries
entries = [] # type: List[Tuple[List[Tuple[str, str, int]], StringList]]
in_definition = True
+ in_comment = False
was_empty = True
messages = [] # type: List[nodes.Node]
for line, (source, lineno) in zip(self.content, self.content.items):
@@ -313,27 +318,33 @@ class Glossary(SphinxDirective):
if line and not line[0].isspace():
# enable comments
if line.startswith('.. '):
+ in_comment = True
continue
+ else:
+ in_comment = False
+
# first term of definition
if in_definition:
if not was_empty:
- messages.append(self.state.reporter.system_message(
- 2, 'glossary term must be preceded by empty line',
+ messages.append(self.state.reporter.warning(
+ _('glossary term must be preceded by empty line'),
source=source, line=lineno))
entries.append(([(line, source, lineno)], StringList()))
in_definition = False
# second term and following
else:
if was_empty:
- messages.append(self.state.reporter.system_message(
- 2, 'glossary terms must not be separated by empty '
- 'lines', source=source, line=lineno))
+ messages.append(self.state.reporter.warning(
+ _('glossary terms must not be separated by empty lines'),
+ source=source, line=lineno))
if entries:
entries[-1][0].append((line, source, lineno))
else:
- messages.append(self.state.reporter.system_message(
- 2, 'glossary seems to be misformatted, check '
- 'indentation', source=source, line=lineno))
+ messages.append(self.state.reporter.warning(
+ _('glossary seems to be misformatted, check indentation'),
+ source=source, line=lineno))
+ elif in_comment:
+ pass
else:
if not in_definition:
# first line of definition, determines indentation
@@ -342,9 +353,9 @@ class Glossary(SphinxDirective):
if entries:
entries[-1][1].append(line[indent_len:], source, lineno)
else:
- messages.append(self.state.reporter.system_message(
- 2, 'glossary seems to be misformatted, check '
- 'indentation', source=source, line=lineno))
+ messages.append(self.state.reporter.warning(
+ _('glossary seems to be misformatted, check indentation'),
+ source=source, line=lineno))
was_empty = False
# now, parse all the entries into a big definition list
@@ -494,8 +505,6 @@ class StandardDomain(Domain):
initial_data = {
'progoptions': {}, # (program, name) -> docname, labelid
'objects': {}, # (type, name) -> docname, labelid
- 'citations': {}, # citation_name -> docname, labelid, lineno
- 'citation_refs': {}, # citation_name -> list of docnames
'labels': { # labelname -> docname, labelid, sectionname
'genindex': ('genindex', '', _('Index')),
'modindex': ('py-modindex', '', _('Module Index')),
@@ -516,7 +525,6 @@ class StandardDomain(Domain):
'keyword': 'unknown keyword: %(target)s',
'doc': 'unknown document: %(target)s',
'option': 'unknown option: %(target)s',
- 'citation': 'citation not found: %(target)s',
}
enumerable_nodes = { # node_class -> (figtype, title_getter)
@@ -534,81 +542,60 @@ class StandardDomain(Domain):
for node, settings in env.app.registry.enumerable_nodes.items():
self.enumerable_nodes[node] = settings
+ @property
+ def objects(self):
+ # type: () -> Dict[Tuple[str, str], Tuple[str, str]]
+ return self.data.setdefault('objects', {}) # (objtype, name) -> docname, labelid
+
+ @property
+ def progoptions(self):
+ # type: () -> Dict[Tuple[str, str], Tuple[str, str]]
+ return self.data.setdefault('progoptions', {}) # (program, name) -> docname, labelid
+
+ @property
+ def labels(self):
+ # type: () -> Dict[str, Tuple[str, str, str]]
+ return self.data.setdefault('labels', {}) # labelname -> docname, labelid, sectionname
+
+ @property
+ def anonlabels(self):
+ # type: () -> Dict[str, Tuple[str, str]]
+ return self.data.setdefault('anonlabels', {}) # labelname -> docname, labelid
+
def clear_doc(self, docname):
# type: (str) -> None
- for key, (fn, _l) in list(self.data['progoptions'].items()):
- if fn == docname:
- del self.data['progoptions'][key]
- for key, (fn, _l) in list(self.data['objects'].items()):
+ key = None # type: Any
+ for key, (fn, _l) in list(self.progoptions.items()):
if fn == docname:
- del self.data['objects'][key]
- for key, (fn, _l, lineno) in list(self.data['citations'].items()):
+ del self.progoptions[key]
+ for key, (fn, _l) in list(self.objects.items()):
if fn == docname:
- del self.data['citations'][key]
- for key, docnames in list(self.data['citation_refs'].items()):
- if docnames == [docname]:
- del self.data['citation_refs'][key]
- elif docname in docnames:
- docnames.remove(docname)
- for key, (fn, _l, _l) in list(self.data['labels'].items()):
+ del self.objects[key]
+ for key, (fn, _l, _l) in list(self.labels.items()):
if fn == docname:
- del self.data['labels'][key]
- for key, (fn, _l) in list(self.data['anonlabels'].items()):
+ del self.labels[key]
+ for key, (fn, _l) in list(self.anonlabels.items()):
if fn == docname:
- del self.data['anonlabels'][key]
+ del self.anonlabels[key]
def merge_domaindata(self, docnames, otherdata):
# type: (List[str], Dict) -> None
# XXX duplicates?
for key, data in otherdata['progoptions'].items():
if data[0] in docnames:
- self.data['progoptions'][key] = data
+ self.progoptions[key] = data
for key, data in otherdata['objects'].items():
if data[0] in docnames:
- self.data['objects'][key] = data
- for key, data in otherdata['citations'].items():
- if data[0] in docnames:
- self.data['citations'][key] = data
- for key, data in otherdata['citation_refs'].items():
- citation_refs = self.data['citation_refs'].setdefault(key, [])
- for docname in data:
- if docname in docnames:
- citation_refs.append(docname)
+ self.objects[key] = data
for key, data in otherdata['labels'].items():
if data[0] in docnames:
- self.data['labels'][key] = data
+ self.labels[key] = data
for key, data in otherdata['anonlabels'].items():
if data[0] in docnames:
- self.data['anonlabels'][key] = data
+ self.anonlabels[key] = data
def process_doc(self, env, docname, document):
# type: (BuildEnvironment, str, nodes.document) -> None
- self.note_citations(env, docname, document)
- self.note_citation_refs(env, docname, document)
- self.note_labels(env, docname, document)
-
- def note_citations(self, env, docname, document):
- # type: (BuildEnvironment, str, nodes.document) -> None
- for node in document.traverse(nodes.citation):
- node['docname'] = docname
- label = cast(nodes.label, node[0]).astext()
- if label in self.data['citations']:
- path = env.doc2path(self.data['citations'][label][0])
- logger.warning(__('duplicate citation %s, other instance in %s'), label, path,
- location=node, type='ref', subtype='citation')
- self.data['citations'][label] = (docname, node['ids'][0], node.line)
-
- def note_citation_refs(self, env, docname, document):
- # type: (BuildEnvironment, str, nodes.document) -> None
- for node in document.traverse(addnodes.pending_xref):
- if node['refdomain'] == 'std' and node['reftype'] == 'citation':
- label = node['reftarget']
- citation_refs = self.data['citation_refs'].setdefault(label, [])
- citation_refs.append(docname)
-
- def note_labels(self, env, docname, document):
- # type: (BuildEnvironment, str, nodes.document) -> None
- labels, anonlabels = self.data['labels'], self.data['anonlabels']
for name, explicit in document.nametypes.items():
if not explicit:
continue
@@ -626,11 +613,11 @@ class StandardDomain(Domain):
# ignore footnote labels, labels automatically generated from a
# link and object descriptions
continue
- if name in labels:
+ if name in self.labels:
logger.warning(__('duplicate label %s, other instance in %s'),
- name, env.doc2path(labels[name][0]),
+ name, env.doc2path(self.labels[name][0]),
location=node)
- anonlabels[name] = docname, labelid
+ self.anonlabels[name] = docname, labelid
if node.tagname in ('section', 'rubric'):
title = cast(nodes.title, node[0])
sectname = clean_astext(title)
@@ -647,23 +634,15 @@ class StandardDomain(Domain):
else:
# anonymous-only labels
continue
- labels[name] = docname, labelid, sectname
+ self.labels[name] = docname, labelid, sectname
def add_object(self, objtype, name, docname, labelid):
# type: (str, str, str, str) -> None
- self.data['objects'][objtype, name] = (docname, labelid)
+ self.objects[objtype, name] = (docname, labelid)
def add_program_option(self, program, name, docname, labelid):
# type: (str, str, str, str) -> None
- self.data['progoptions'][program, name] = (docname, labelid)
-
- def check_consistency(self):
- # type: () -> None
- for name, (docname, labelid, lineno) in self.data['citations'].items():
- if name not in self.data['citation_refs']:
- logger.warning(__('Citation [%s] is not referenced.'), name,
- type='ref', subtype='citation',
- location=(docname, lineno))
+ self.progoptions[program, name] = (docname, labelid)
def build_reference_node(self, fromdocname, builder, docname, labelid,
sectname, rolename, **options):
@@ -703,7 +682,10 @@ class StandardDomain(Domain):
elif typ == 'option':
resolver = self._resolve_option_xref
elif typ == 'citation':
- resolver = self._resolve_citation_xref
+ warnings.warn('pending_xref(domain=std, type=citation) is deprecated: %r' % node,
+ RemovedInSphinx40Warning)
+ domain = env.get_domain('citation')
+ return domain.resolve_xref(env, fromdocname, builder, typ, target, node, contnode)
else:
resolver = self._resolve_obj_xref
@@ -714,13 +696,12 @@ class StandardDomain(Domain):
if node['refexplicit']:
# reference to anonymous label; the reference uses
# the supplied link caption
- docname, labelid = self.data['anonlabels'].get(target, ('', ''))
+ docname, labelid = self.anonlabels.get(target, ('', ''))
sectname = node.astext()
else:
# reference to named label; the final node will
# contain the section name after the label
- docname, labelid, sectname = self.data['labels'].get(target,
- ('', '', ''))
+ docname, labelid, sectname = self.labels.get(target, ('', '', ''))
if not docname:
return None
@@ -729,10 +710,10 @@ class StandardDomain(Domain):
def _resolve_numref_xref(self, env, fromdocname, builder, typ, target, node, contnode):
# type: (BuildEnvironment, str, Builder, str, str, addnodes.pending_xref, nodes.Element) -> nodes.Element # NOQA
- if target in self.data['labels']:
- docname, labelid, figname = self.data['labels'].get(target, ('', '', ''))
+ if target in self.labels:
+ docname, labelid, figname = self.labels.get(target, ('', '', ''))
else:
- docname, labelid = self.data['anonlabels'].get(target, ('', ''))
+ docname, labelid = self.anonlabels.get(target, ('', ''))
figname = None
if not docname:
@@ -791,7 +772,7 @@ class StandardDomain(Domain):
def _resolve_keyword_xref(self, env, fromdocname, builder, typ, target, node, contnode):
# type: (BuildEnvironment, str, Builder, str, str, addnodes.pending_xref, nodes.Element) -> nodes.Element # NOQA
# keywords are oddballs: they are referenced by named labels
- docname, labelid, _ = self.data['labels'].get(target, ('', '', ''))
+ docname, labelid, _ = self.labels.get(target, ('', '', ''))
if not docname:
return None
return make_refnode(builder, fromdocname, docname,
@@ -817,7 +798,7 @@ class StandardDomain(Domain):
# type: (BuildEnvironment, str, Builder, str, str, addnodes.pending_xref, nodes.Element) -> nodes.Element # NOQA
progname = node.get('std:program')
target = target.strip()
- docname, labelid = self.data['progoptions'].get((progname, target), ('', ''))
+ docname, labelid = self.progoptions.get((progname, target), ('', ''))
if not docname:
commands = []
while ws_re.search(target):
@@ -825,8 +806,7 @@ class StandardDomain(Domain):
commands.append(subcommand)
progname = "-".join(commands)
- docname, labelid = self.data['progoptions'].get((progname, target),
- ('', ''))
+ docname, labelid = self.progoptions.get((progname, target), ('', ''))
if docname:
break
else:
@@ -835,33 +815,12 @@ class StandardDomain(Domain):
return make_refnode(builder, fromdocname, docname,
labelid, contnode)
- def _resolve_citation_xref(self, env, fromdocname, builder, typ, target, node, contnode):
- # type: (BuildEnvironment, str, Builder, str, str, addnodes.pending_xref, nodes.Element) -> nodes.Element # NOQA
- docname, labelid, lineno = self.data['citations'].get(target, ('', '', 0))
- if not docname:
- if 'ids' in node:
- # remove ids attribute that annotated at
- # transforms.CitationReference.apply.
- del node['ids'][:]
- return None
-
- try:
- return make_refnode(builder, fromdocname, docname,
- labelid, contnode)
- except NoUri:
- # remove the ids we added in the CitationReferences
- # transform since they can't be transfered to
- # the contnode (if it's a Text node)
- if not isinstance(contnode, nodes.Element):
- del node['ids'][:]
- raise
-
def _resolve_obj_xref(self, env, fromdocname, builder, typ, target, node, contnode):
# type: (BuildEnvironment, str, Builder, str, str, addnodes.pending_xref, nodes.Element) -> nodes.Element # NOQA
objtypes = self.objtypes_for_role(typ) or []
for objtype in objtypes:
- if (objtype, target) in self.data['objects']:
- docname, labelid = self.data['objects'][objtype, target]
+ if (objtype, target) in self.objects:
+ docname, labelid = self.objects[objtype, target]
break
else:
docname, labelid = '', ''
@@ -885,8 +844,8 @@ class StandardDomain(Domain):
key = (objtype, target)
if objtype == 'term':
key = (objtype, ltarget)
- if key in self.data['objects']:
- docname, labelid = self.data['objects'][key]
+ if key in self.objects:
+ docname, labelid = self.objects[key]
results.append(('std:' + self.role_for_objtype(objtype),
make_refnode(builder, fromdocname, docname,
labelid, contnode)))
@@ -897,22 +856,22 @@ class StandardDomain(Domain):
# handle the special 'doc' reference here
for doc in self.env.all_docs:
yield (doc, clean_astext(self.env.titles[doc]), 'doc', doc, '', -1)
- for (prog, option), info in self.data['progoptions'].items():
+ for (prog, option), info in self.progoptions.items():
if prog:
fullname = ".".join([prog, option])
yield (fullname, fullname, 'cmdoption', info[0], info[1], 1)
else:
yield (option, option, 'cmdoption', info[0], info[1], 1)
- for (type, name), info in self.data['objects'].items():
+ for (type, name), info in self.objects.items():
yield (name, name, type, info[0], info[1],
self.object_types[type].attrs['searchprio'])
- for name, info in self.data['labels'].items():
- yield (name, info[2], 'label', info[0], info[1], -1)
+ for name, (docname, labelid, sectionname) in self.labels.items():
+ yield (name, sectionname, 'label', docname, labelid, -1)
# add anonymous-only labels as well
- non_anon_labels = set(self.data['labels'])
- for name, info in self.data['anonlabels'].items():
+ non_anon_labels = set(self.labels)
+ for name, (docname, labelid) in self.anonlabels.items():
if name not in non_anon_labels:
- yield (name, name, 'label', info[0], info[1], -1)
+ yield (name, name, 'label', docname, labelid, -1)
def get_type_name(self, type, primary=False):
# type: (ObjType, bool) -> str
@@ -993,6 +952,21 @@ class StandardDomain(Domain):
else:
return None
+ def note_citations(self, env, docname, document):
+ # type: (BuildEnvironment, str, nodes.document) -> None
+ warnings.warn('StandardDomain.note_citations() is deprecated.',
+ RemovedInSphinx40Warning)
+
+ def note_citation_refs(self, env, docname, document):
+ # type: (BuildEnvironment, str, nodes.document) -> None
+ warnings.warn('StandardDomain.note_citation_refs() is deprecated.',
+ RemovedInSphinx40Warning)
+
+ def note_labels(self, env, docname, document):
+ # type: (BuildEnvironment, str, nodes.document) -> None
+ warnings.warn('StandardDomain.note_labels() is deprecated.',
+ RemovedInSphinx40Warning)
+
def setup(app):
# type: (Sphinx) -> Dict[str, Any]
diff --git a/sphinx/ext/autosummary/__init__.py b/sphinx/ext/autosummary/__init__.py
index b201a8a56..952bd9e2a 100644
--- a/sphinx/ext/autosummary/__init__.py
+++ b/sphinx/ext/autosummary/__init__.py
@@ -733,11 +733,12 @@ def process_generate_options(app):
'But your source_suffix does not contain .rst. Skipped.'))
return
+ imported_members = app.config.autosummary_imported_members
with mock(app.config.autosummary_mock_imports):
generate_autosummary_docs(genfiles, builder=app.builder,
warn=logger.warning, info=logger.info,
suffix=suffix, base_path=app.srcdir,
- app=app)
+ app=app, imported_members=imported_members)
def setup(app):
@@ -763,5 +764,6 @@ def setup(app):
app.add_config_value('autosummary_generate', [], True, [bool])
app.add_config_value('autosummary_mock_imports',
lambda config: config.autodoc_mock_imports, 'env')
+ app.add_config_value('autosummary_imported_members', [], False, [bool])
return {'version': sphinx.__display_version__, 'parallel_read_safe': True}
diff --git a/sphinx/ext/ifconfig.py b/sphinx/ext/ifconfig.py
index bad5953d3..1768acf18 100644
--- a/sphinx/ext/ifconfig.py
+++ b/sphinx/ext/ifconfig.py
@@ -23,6 +23,7 @@ from docutils import nodes
import sphinx
from sphinx.util.docutils import SphinxDirective
+from sphinx.util.nodes import nested_parse_with_titles
if False:
# For type annotation
@@ -48,8 +49,7 @@ class IfConfig(SphinxDirective):
node.document = self.state.document
self.set_source_info(node)
node['expr'] = self.arguments[0]
- self.state.nested_parse(self.content, self.content_offset,
- node, match_titles=True)
+ nested_parse_with_titles(self.state, self.content, node)
return [node]
diff --git a/sphinx/ext/napoleon/docstring.py b/sphinx/ext/napoleon/docstring.py
index 00a41afe8..0fea99fb8 100644
--- a/sphinx/ext/napoleon/docstring.py
+++ b/sphinx/ext/napoleon/docstring.py
@@ -100,7 +100,7 @@ class GoogleDocstring:
"""
- _name_rgx = re.compile(r"^\s*(:(?P<role>\w+):`(?P<name>[a-zA-Z0-9_.-]+)`|"
+ _name_rgx = re.compile(r"^\s*((?::(?P<role>\S+):)?`(?P<name>[a-zA-Z0-9_.-]+)`|"
r" (?P<name2>[a-zA-Z0-9_.-]+))\s*", re.X)
def __init__(self, docstring, config=None, app=None, what='', name='',
@@ -700,9 +700,9 @@ class GoogleDocstring:
fields = self._consume_fields(parse_type=False, prefer_type=True)
lines = [] # type: List[str]
for _name, _type, _desc in fields:
- m = self._name_rgx.match(_type).groupdict()
- if m['role']:
- _type = m['name']
+ m = self._name_rgx.match(_type)
+ if m and m.group('name'):
+ _type = m.group('name')
_type = ' ' + _type if _type else ''
_desc = self._strip_empty(_desc)
_descs = ' ' + '\n '.join(_desc) if any(_desc) else ''
diff --git a/sphinx/templates/quickstart/Makefile.new_t b/sphinx/templates/quickstart/Makefile.new_t
index 16a9d482f..7532398b2 100644
--- a/sphinx/templates/quickstart/Makefile.new_t
+++ b/sphinx/templates/quickstart/Makefile.new_t
@@ -1,11 +1,14 @@
# Minimal makefile for Sphinx documentation
#
-# You can set these variables from the command line.
-SPHINXOPTS =
-SPHINXBUILD = sphinx-build
-SOURCEDIR = {{ rsrcdir }}
-BUILDDIR = {{ rbuilddir }}
+# You can set these variables from the command line. For example:
+# SPHINXOPTS='-E -W -n' make html
+# will run the html builder in a clean environment (-E), treating warnings
+# as errors (-W), in nitpicky mode (-n).
+SPHINXOPTS ?=
+SPHINXBUILD ?= sphinx-build
+SOURCEDIR ?= {{ rsrcdir }}
+BUILDDIR ?= {{ rbuilddir }}
# Put it first so that "make" without argument is like "make help".
help:
@@ -17,3 +20,4 @@ help:
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
+
diff --git a/sphinx/themes/basic/static/searchtools.js b/sphinx/themes/basic/static/searchtools.js
index 4c5826411..bdc270655 100644
--- a/sphinx/themes/basic/static/searchtools.js
+++ b/sphinx/themes/basic/static/searchtools.js
@@ -75,6 +75,16 @@ var Search = {
}
},
+ loadIndex : function(url) {
+ $.ajax({type: "GET", url: url, data: null,
+ dataType: "script", cache: true,
+ complete: function(jqxhr, textstatus) {
+ if (textstatus != "success") {
+ document.getElementById("searchindexloader").src = url;
+ }
+ }});
+ },
+
setIndex : function(index) {
var q;
this._index = index;
diff --git a/sphinx/themes/bizstyle/static/bizstyle.css_t b/sphinx/themes/bizstyle/static/bizstyle.css_t
index 949d86c6a..f2b400688 100644
--- a/sphinx/themes/bizstyle/static/bizstyle.css_t
+++ b/sphinx/themes/bizstyle/static/bizstyle.css_t
@@ -410,6 +410,20 @@ p.versionchanged span.versionmodified {
background-color: #DCE6A0;
}
+dl.field-list > dt {
+ color: white;
+ padding-left: 0.5em;
+ padding-right: 5px;
+ background-color: #82A0BE;
+}
+
+dl.field-list > dd {
+ padding-left: 0.5em;
+ margin-top: 0em;
+ margin-left: 0em;
+ background-color: #f7f7f7;
+}
+
/* -- table styles ---------------------------------------------------------- */
table.docutils {
diff --git a/sphinx/transforms/__init__.py b/sphinx/transforms/__init__.py
index 2fc1b6e72..16849c46c 100644
--- a/sphinx/transforms/__init__.py
+++ b/sphinx/transforms/__init__.py
@@ -9,7 +9,6 @@
"""
import re
-from typing import cast
from docutils import nodes
from docutils.transforms import Transform, Transformer
@@ -19,13 +18,12 @@ from docutils.utils import normalize_language_tag
from docutils.utils.smartquotes import smartchars
from sphinx import addnodes
+from sphinx.deprecation import RemovedInSphinx40Warning, deprecated_alias
from sphinx.locale import _, __
from sphinx.util import logging
from sphinx.util.docutils import new_document
from sphinx.util.i18n import format_date
-from sphinx.util.nodes import (
- NodeMatcher, apply_source_workaround, copy_source_info, is_smartquotable
-)
+from sphinx.util.nodes import NodeMatcher, apply_source_workaround, is_smartquotable
if False:
# For type annotation
@@ -200,39 +198,6 @@ class SortIds(SphinxTransform):
node['ids'] = node['ids'][1:] + [node['ids'][0]]
-class SmartQuotesSkipper(SphinxTransform):
- """Mark specific nodes as not smartquoted."""
- default_priority = 619
-
- def apply(self, **kwargs):
- # type: (Any) -> None
- # citation labels
- for node in self.document.traverse(nodes.citation):
- label = cast(nodes.label, node[0])
- label['support_smartquotes'] = False
-
-
-class CitationReferences(SphinxTransform):
- """
- Replace citation references by pending_xref nodes before the default
- docutils transform tries to resolve them.
- """
- default_priority = 619
-
- def apply(self, **kwargs):
- # type: (Any) -> None
- for node in self.document.traverse(nodes.citation_reference):
- target = node.astext()
- ref = addnodes.pending_xref(target, refdomain='std', reftype='citation',
- reftarget=target, refwarn=True,
- support_smartquotes=False,
- ids=node["ids"],
- classes=node.get('classes', []))
- ref += nodes.inline(target, '[%s]' % target)
- copy_source_info(node, ref)
- node.replace_self(ref)
-
-
TRANSLATABLE_NODES = {
'literal-block': nodes.literal_block,
'doctest-block': nodes.doctest_block,
@@ -440,12 +405,22 @@ class ManpageLink(SphinxTransform):
node.attributes.update(info)
+from sphinx.domains.citation import ( # NOQA
+ CitationDefinitionTransform, CitationReferenceTransform
+)
+
+deprecated_alias('sphinx.transforms',
+ {
+ 'CitationReferences': CitationReferenceTransform,
+ 'SmartQuotesSkipper': CitationDefinitionTransform,
+ },
+ RemovedInSphinx40Warning)
+
+
def setup(app):
# type: (Sphinx) -> Dict[str, Any]
app.add_transform(ApplySourceWorkaround)
app.add_transform(ExtraTranslatableNodes)
- app.add_transform(SmartQuotesSkipper)
- app.add_transform(CitationReferences)
app.add_transform(DefaultSubstitutions)
app.add_transform(MoveModuleTargets)
app.add_transform(HandleCodeBlocks)
diff --git a/sphinx/util/osutil.py b/sphinx/util/osutil.py
index 4cd646f96..fe98783e7 100644
--- a/sphinx/util/osutil.py
+++ b/sphinx/util/osutil.py
@@ -20,6 +20,7 @@ from io import StringIO
from os import path
from sphinx.deprecation import RemovedInSphinx40Warning
+from sphinx.testing.path import path as Path
if False:
# For type annotation
@@ -167,15 +168,18 @@ fs_encoding = sys.getfilesystemencoding() or sys.getdefaultencoding()
def abspath(pathdir):
# type: (str) -> str
- pathdir = path.abspath(pathdir)
- if isinstance(pathdir, bytes):
- try:
- pathdir = pathdir.decode(fs_encoding)
- except UnicodeDecodeError:
- raise UnicodeDecodeError('multibyte filename not supported on '
- 'this filesystem encoding '
- '(%r)' % fs_encoding)
- return pathdir
+ if isinstance(pathdir, Path):
+ return pathdir.abspath()
+ else:
+ pathdir = path.abspath(pathdir)
+ if isinstance(pathdir, bytes):
+ try:
+ pathdir = pathdir.decode(fs_encoding)
+ except UnicodeDecodeError:
+ raise UnicodeDecodeError('multibyte filename not supported on '
+ 'this filesystem encoding '
+ '(%r)' % fs_encoding)
+ return pathdir
def getcwd():
diff --git a/sphinx/writers/html5.py b/sphinx/writers/html5.py
index afab35950..b78dd1fcf 100644
--- a/sphinx/writers/html5.py
+++ b/sphinx/writers/html5.py
@@ -67,6 +67,7 @@ class HTML5Translator(SphinxTranslator, BaseTranslator):
self.param_separator = ''
self.optional_param_level = 0
self._table_row_index = 0
+ self._fieldlist_row_index = 0
self.required_params_left = 0
def visit_start_of_file(self, node):
diff --git a/tests/roots/test-ext-autosummary-imported_members/autosummary_dummy_package/__init__.py b/tests/roots/test-ext-autosummary-imported_members/autosummary_dummy_package/__init__.py
new file mode 100644
index 000000000..0a7d9f382
--- /dev/null
+++ b/tests/roots/test-ext-autosummary-imported_members/autosummary_dummy_package/__init__.py
@@ -0,0 +1 @@
+from .autosummary_dummy_module import Bar, foo
diff --git a/tests/roots/test-ext-autosummary-imported_members/autosummary_dummy_package/autosummary_dummy_module.py b/tests/roots/test-ext-autosummary-imported_members/autosummary_dummy_package/autosummary_dummy_module.py
new file mode 100644
index 000000000..9c93f064e
--- /dev/null
+++ b/tests/roots/test-ext-autosummary-imported_members/autosummary_dummy_package/autosummary_dummy_module.py
@@ -0,0 +1,8 @@
+class Bar:
+ """Bar class"""
+ pass
+
+
+def foo():
+ """Foo function"""
+ pass
diff --git a/tests/roots/test-ext-autosummary-imported_members/conf.py b/tests/roots/test-ext-autosummary-imported_members/conf.py
new file mode 100644
index 000000000..4cfff02dc
--- /dev/null
+++ b/tests/roots/test-ext-autosummary-imported_members/conf.py
@@ -0,0 +1,7 @@
+import os
+import sys
+sys.path.insert(0, os.path.abspath('.'))
+
+extensions = ['sphinx.ext.autosummary']
+autosummary_generate = True
+autosummary_imported_members = True
diff --git a/tests/roots/test-ext-autosummary-imported_members/index.rst b/tests/roots/test-ext-autosummary-imported_members/index.rst
new file mode 100644
index 000000000..608ca2954
--- /dev/null
+++ b/tests/roots/test-ext-autosummary-imported_members/index.rst
@@ -0,0 +1,7 @@
+test-ext-autosummary-mock_imports
+=================================
+
+.. autosummary::
+ :toctree: generated
+
+ autosummary_dummy_package
diff --git a/tests/test_domain_cpp.py b/tests/test_domain_cpp.py
index 46019b4a9..d6470dc7c 100644
--- a/tests/test_domain_cpp.py
+++ b/tests/test_domain_cpp.py
@@ -755,6 +755,20 @@ def test_attributes():
check('member', 'int *[[attr]] *i', {1: 'i__iPP', 2: '1i'})
+def test_xref_parsing():
+ def check(target):
+ class Config:
+ cpp_id_attributes = ["id_attr"]
+ cpp_paren_attributes = ["paren_attr"]
+ parser = DefinitionParser(target, None, Config())
+ ast, isShorthand = parser.parse_xref_object()
+ parser.assert_end()
+ check('f')
+ check('f()')
+ check('void f()')
+ check('T f()')
+
+
# def test_print():
# # used for getting all the ids out for checking
# for a in ids:
diff --git a/tests/test_domain_std.py b/tests/test_domain_std.py
index 15daeeea6..dd77c11dd 100644
--- a/tests/test_domain_std.py
+++ b/tests/test_domain_std.py
@@ -11,8 +11,12 @@
from unittest import mock
from docutils import nodes
+from docutils.nodes import definition, definition_list, definition_list_item, term
+from sphinx.addnodes import glossary, index
from sphinx.domains.std import StandardDomain
+from sphinx.testing import restructuredtext
+from sphinx.testing.util import assert_node
def test_process_doc_handle_figure_caption():
@@ -80,3 +84,158 @@ def test_get_full_qualified_name():
kwargs = {'std:program': 'ls'}
node = nodes.reference(reftype='option', reftarget='-l', **kwargs)
assert domain.get_full_qualified_name(node) == 'ls.-l'
+
+
+def test_glossary(app):
+ text = (".. glossary::\n"
+ "\n"
+ " term1\n"
+ " term2\n"
+ " description\n"
+ "\n"
+ " term3 : classifier\n"
+ " description\n"
+ " description\n"
+ "\n"
+ " term4 : class1 : class2\n"
+ " description\n")
+
+ # doctree
+ doctree = restructuredtext.parse(app, text)
+ assert_node(doctree, (
+ [glossary, definition_list, ([definition_list_item, ([term, ("term1",
+ index)],
+ [term, ("term2",
+ index)],
+ definition)],
+ [definition_list_item, ([term, ("term3",
+ index)],
+ definition)],
+ [definition_list_item, ([term, ("term4",
+ index)],
+ definition)])],
+ ))
+ assert_node(doctree[0][0][0][0][1],
+ entries=[("single", "term1", "term-term1", "main", None)])
+ assert_node(doctree[0][0][0][1][1],
+ entries=[("single", "term2", "term-term2", "main", None)])
+ assert_node(doctree[0][0][0][2],
+ [definition, nodes.paragraph, "description"])
+ assert_node(doctree[0][0][1][0][1],
+ entries=[("single", "term3", "term-term3", "main", "classifier")])
+ assert_node(doctree[0][0][1][1],
+ [definition, nodes.paragraph, ("description\n"
+ "description")])
+ assert_node(doctree[0][0][2][0][1],
+ entries=[("single", "term4", "term-term4", "main", "class1")])
+ assert_node(doctree[0][0][2][1],
+ [nodes.definition, nodes.paragraph, "description"])
+
+ # index
+ objects = list(app.env.get_domain("std").get_objects())
+ assert ("term1", "term1", "term", "index", "term-term1", -1) in objects
+ assert ("term2", "term2", "term", "index", "term-term2", -1) in objects
+ assert ("term3", "term3", "term", "index", "term-term3", -1) in objects
+ assert ("term4", "term4", "term", "index", "term-term4", -1) in objects
+
+
+def test_glossary_warning(app, status, warning):
+ # empty line between terms
+ text = (".. glossary::\n"
+ "\n"
+ " term1\n"
+ "\n"
+ " term2\n")
+ restructuredtext.parse(app, text, "case1")
+ assert ("case1.rst:4: WARNING: glossary terms must not be separated by empty lines"
+ in warning.getvalue())
+
+ # glossary starts with indented item
+ text = (".. glossary::\n"
+ "\n"
+ " description\n"
+ " term\n")
+ restructuredtext.parse(app, text, "case2")
+ assert ("case2.rst:3: WARNING: glossary term must be preceded by empty line"
+ in warning.getvalue())
+
+ # empty line between terms
+ text = (".. glossary::\n"
+ "\n"
+ " term1\n"
+ " description\n"
+ " term2\n")
+ restructuredtext.parse(app, text, "case3")
+ assert ("case3.rst:4: WARNING: glossary term must be preceded by empty line"
+ in warning.getvalue())
+
+
+def test_glossary_comment(app):
+ text = (".. glossary::\n"
+ "\n"
+ " term1\n"
+ " description\n"
+ " .. term2\n"
+ " description\n"
+ " description\n")
+ doctree = restructuredtext.parse(app, text)
+ assert_node(doctree, (
+ [glossary, definition_list, definition_list_item, ([term, ("term1",
+ index)],
+ definition)],
+ ))
+ assert_node(doctree[0][0][0][1],
+ [nodes.definition, nodes.paragraph, "description"])
+
+
+def test_glossary_comment2(app):
+ text = (".. glossary::\n"
+ "\n"
+ " term1\n"
+ " description\n"
+ "\n"
+ " .. term2\n"
+ " term3\n"
+ " description\n"
+ " description\n")
+ doctree = restructuredtext.parse(app, text)
+ assert_node(doctree, (
+ [glossary, definition_list, ([definition_list_item, ([term, ("term1",
+ index)],
+ definition)],
+ [definition_list_item, ([term, ("term3",
+ index)],
+ definition)])],
+ ))
+ assert_node(doctree[0][0][0][1],
+ [nodes.definition, nodes.paragraph, "description"])
+ assert_node(doctree[0][0][1][1],
+ [nodes.definition, nodes.paragraph, ("description\n"
+ "description")])
+
+
+def test_glossary_sorted(app):
+ text = (".. glossary::\n"
+ " :sorted:\n"
+ "\n"
+ " term3\n"
+ " description\n"
+ "\n"
+ " term2\n"
+ " term1\n"
+ " description\n")
+ doctree = restructuredtext.parse(app, text)
+ assert_node(doctree, (
+ [glossary, definition_list, ([definition_list_item, ([term, ("term2",
+ index)],
+ [term, ("term1",
+ index)],
+ definition)],
+ [definition_list_item, ([term, ("term3",
+ index)],
+ definition)])],
+ ))
+ assert_node(doctree[0][0][0][2],
+ [nodes.definition, nodes.paragraph, "description"])
+ assert_node(doctree[0][0][1][1],
+ [nodes.definition, nodes.paragraph, "description"])
diff --git a/tests/test_environment_indexentries.py b/tests/test_environment_indexentries.py
index 62e4ffb79..ec76acdc0 100644
--- a/tests/test_environment_indexentries.py
+++ b/tests/test_environment_indexentries.py
@@ -8,135 +8,116 @@
:license: BSD, see LICENSE for details.
"""
-from collections import namedtuple
-from unittest import mock
+import pytest
-from sphinx import locale
from sphinx.environment.adapters.indexentries import IndexEntries
-
-Environment = namedtuple('Environment', 'indexentries')
-
-dummy_builder = mock.Mock()
-dummy_builder.get_relative_uri.return_value = ''
-
-
-def test_create_single_index():
- # type, value, tid, main, index_key
- env = Environment({
- 'index': [
- ('single', 'docutils', 'id1', '', None),
- ('single', 'Python', 'id2', '', None),
- ('single', 'pip; install', 'id3', '', None),
- ('single', 'pip; upgrade', 'id4', '', None),
- ('single', 'Sphinx', 'id5', '', None),
- ('single', 'Ель', 'id6', '', None),
- ('single', 'ёлка', 'id7', '', None),
- ('single', '‏תירבע‎', 'id8', '', None),
- ('single', '9-symbol', 'id9', '', None),
- ('single', '&-symbol', 'id10', '', None),
- ],
- })
- index = IndexEntries(env).create_index(dummy_builder)
+from sphinx.testing import restructuredtext
+
+
+@pytest.mark.sphinx('dummy')
+def test_create_single_index(app):
+ app.env.indexentries.clear()
+ text = (".. index:: docutils\n"
+ ".. index:: Python\n"
+ ".. index:: pip; install\n"
+ ".. index:: pip; upgrade\n"
+ ".. index:: Sphinx\n"
+ ".. index:: Ель\n"
+ ".. index:: ёлка\n"
+ ".. index:: ‏תירבע‎\n"
+ ".. index:: 9-symbol\n"
+ ".. index:: &-symbol\n")
+ restructuredtext.parse(app, text)
+ index = IndexEntries(app.env).create_index(app.builder)
assert len(index) == 6
- assert index[0] == ('Symbols', [('&-symbol', [[('', '#id10')], [], None]),
- ('9-symbol', [[('', '#id9')], [], None])])
- assert index[1] == ('D', [('docutils', [[('', '#id1')], [], None])])
- assert index[2] == ('P', [('pip', [[], [('install', [('', '#id3')]),
- ('upgrade', [('', '#id4')])], None]),
- ('Python', [[('', '#id2')], [], None])])
- assert index[3] == ('S', [('Sphinx', [[('', '#id5')], [], None])])
- assert index[4] == ('Е', [('ёлка', [[('', '#id7')], [], None]),
- ('Ель', [[('', '#id6')], [], None])])
- assert index[5] == ('ת', [('‏תירבע‎', [[('', '#id8')], [], None])])
-
-
-def test_create_pair_index():
- # type, value, tid, main, index_key
- env = Environment({
- 'index': [
- ('pair', 'docutils; reStructuredText', 'id1', '', None),
- ('pair', 'Python; interpreter', 'id2', '', None),
- ('pair', 'Sphinx; documentation tool', 'id3', '', None),
- ],
- })
- index = IndexEntries(env).create_index(dummy_builder)
+ assert index[0] == ('Symbols', [('&-symbol', [[('', '#index-9')], [], None]),
+ ('9-symbol', [[('', '#index-8')], [], None])])
+ assert index[1] == ('D', [('docutils', [[('', '#index-0')], [], None])])
+ assert index[2] == ('P', [('pip', [[], [('install', [('', '#index-2')]),
+ ('upgrade', [('', '#index-3')])], None]),
+ ('Python', [[('', '#index-1')], [], None])])
+ assert index[3] == ('S', [('Sphinx', [[('', '#index-4')], [], None])])
+ assert index[4] == ('Е', [('ёлка', [[('', '#index-6')], [], None]),
+ ('Ель', [[('', '#index-5')], [], None])])
+ assert index[5] == ('ת', [('‏תירבע‎', [[('', '#index-7')], [], None])])
+
+
+@pytest.mark.sphinx('dummy')
+def test_create_pair_index(app):
+ app.env.indexentries.clear()
+ text = (".. index:: pair: docutils; reStructuredText\n"
+ ".. index:: pair: Python; interpreter\n"
+ ".. index:: pair: Sphinx; documentation tool\n")
+ restructuredtext.parse(app, text)
+ index = IndexEntries(app.env).create_index(app.builder)
assert len(index) == 5
assert index[0] == ('D',
- [('documentation tool', [[], [('Sphinx', [('', '#id3')])], None]),
- ('docutils', [[], [('reStructuredText', [('', '#id1')])], None])])
- assert index[1] == ('I', [('interpreter', [[], [('Python', [('', '#id2')])], None])])
- assert index[2] == ('P', [('Python', [[], [('interpreter', [('', '#id2')])], None])])
+ [('documentation tool', [[], [('Sphinx', [('', '#index-2')])], None]),
+ ('docutils', [[], [('reStructuredText', [('', '#index-0')])], None])])
+ assert index[1] == ('I', [('interpreter', [[], [('Python', [('', '#index-1')])], None])])
+ assert index[2] == ('P', [('Python', [[], [('interpreter', [('', '#index-1')])], None])])
assert index[3] == ('R',
- [('reStructuredText', [[], [('docutils', [('', '#id1')])], None])])
+ [('reStructuredText', [[], [('docutils', [('', '#index-0')])], None])])
assert index[4] == ('S',
- [('Sphinx', [[], [('documentation tool', [('', '#id3')])], None])])
-
-
-def test_create_triple_index():
- # type, value, tid, main, index_key
- env = Environment({
- 'index': [
- ('triple', 'foo; bar; baz', 'id1', '', None),
- ('triple', 'Python; Sphinx; reST', 'id2', '', None),
- ],
- })
- index = IndexEntries(env).create_index(dummy_builder)
+ [('Sphinx', [[], [('documentation tool', [('', '#index-2')])], None])])
+
+
+@pytest.mark.sphinx('dummy')
+def test_create_triple_index(app):
+ app.env.indexentries.clear()
+ text = (".. index:: triple: foo; bar; baz\n"
+ ".. index:: triple: Python; Sphinx; reST\n")
+ restructuredtext.parse(app, text)
+ index = IndexEntries(app.env).create_index(app.builder)
assert len(index) == 5
- assert index[0] == ('B', [('bar', [[], [('baz, foo', [('', '#id1')])], None]),
- ('baz', [[], [('foo bar', [('', '#id1')])], None])])
- assert index[1] == ('F', [('foo', [[], [('bar baz', [('', '#id1')])], None])])
- assert index[2] == ('P', [('Python', [[], [('Sphinx reST', [('', '#id2')])], None])])
- assert index[3] == ('R', [('reST', [[], [('Python Sphinx', [('', '#id2')])], None])])
- assert index[4] == ('S', [('Sphinx', [[], [('reST, Python', [('', '#id2')])], None])])
-
-
-def test_create_see_index():
- locale.init([], None)
-
- # type, value, tid, main, index_key
- env = Environment({
- 'index': [
- ('see', 'docutils; reStructuredText', 'id1', '', None),
- ('see', 'Python; interpreter', 'id2', '', None),
- ('see', 'Sphinx; documentation tool', 'id3', '', None),
- ],
- })
- index = IndexEntries(env).create_index(dummy_builder)
+ assert index[0] == ('B', [('bar', [[], [('baz, foo', [('', '#index-0')])], None]),
+ ('baz', [[], [('foo bar', [('', '#index-0')])], None])])
+ assert index[1] == ('F', [('foo', [[], [('bar baz', [('', '#index-0')])], None])])
+ assert index[2] == ('P', [('Python', [[], [('Sphinx reST', [('', '#index-1')])], None])])
+ assert index[3] == ('R', [('reST', [[], [('Python Sphinx', [('', '#index-1')])], None])])
+ assert index[4] == ('S', [('Sphinx', [[], [('reST, Python', [('', '#index-1')])], None])])
+
+
+@pytest.mark.sphinx('dummy')
+def test_create_see_index(app):
+ app.env.indexentries.clear()
+ text = (".. index:: see: docutils; reStructuredText\n"
+ ".. index:: see: Python; interpreter\n"
+ ".. index:: see: Sphinx; documentation tool\n")
+ restructuredtext.parse(app, text)
+ index = IndexEntries(app.env).create_index(app.builder)
assert len(index) == 3
assert index[0] == ('D', [('docutils', [[], [('see reStructuredText', [])], None])])
assert index[1] == ('P', [('Python', [[], [('see interpreter', [])], None])])
assert index[2] == ('S', [('Sphinx', [[], [('see documentation tool', [])], None])])
-def test_create_seealso_index():
- locale.init([], None)
-
- # type, value, tid, main, index_key
- env = Environment({
- 'index': [
- ('seealso', 'docutils; reStructuredText', 'id1', '', None),
- ('seealso', 'Python; interpreter', 'id2', '', None),
- ('seealso', 'Sphinx; documentation tool', 'id3', '', None),
- ],
- })
- index = IndexEntries(env).create_index(dummy_builder)
+@pytest.mark.sphinx('dummy')
+def test_create_seealso_index(app):
+ app.env.indexentries.clear()
+ text = (".. index:: seealso: docutils; reStructuredText\n"
+ ".. index:: seealso: Python; interpreter\n"
+ ".. index:: seealso: Sphinx; documentation tool\n")
+ restructuredtext.parse(app, text)
+ index = IndexEntries(app.env).create_index(app.builder)
assert len(index) == 3
assert index[0] == ('D', [('docutils', [[], [('see also reStructuredText', [])], None])])
assert index[1] == ('P', [('Python', [[], [('see also interpreter', [])], None])])
assert index[2] == ('S', [('Sphinx', [[], [('see also documentation tool', [])], None])])
-def test_create_index_by_key():
- # type, value, tid, main, index_key
- env = Environment({
- 'index': [
- ('single', 'docutils', 'id1', '', None),
- ('single', 'Python', 'id2', '', None),
- ('single', 'スフィンクス', 'id3', '', 'ス'),
- ],
- })
- index = IndexEntries(env).create_index(dummy_builder)
+@pytest.mark.sphinx('dummy')
+def test_create_index_by_key(app):
+ app.env.indexentries.clear()
+ # At present, only glossary directive is able to create index key
+ text = (".. glossary::\n"
+ "\n"
+ " docutils\n"
+ " Python\n"
+ " スフィンクス : ス\n")
+ restructuredtext.parse(app, text)
+ index = IndexEntries(app.env).create_index(app.builder)
assert len(index) == 3
- assert index[0] == ('D', [('docutils', [[('', '#id1')], [], None])])
- assert index[1] == ('P', [('Python', [[('', '#id2')], [], None])])
- assert index[2] == ('ス', [('スフィンクス', [[('', '#id3')], [], 'ス'])])
+ assert index[0] == ('D', [('docutils', [[('main', '#term-docutils')], [], None])])
+ assert index[1] == ('P', [('Python', [[('main', '#term-python')], [], None])])
+ assert index[2] == ('ス', [('スフィンクス', [[('main', '#term-2')], [], 'ス'])])
diff --git a/tests/test_ext_autosummary.py b/tests/test_ext_autosummary.py
index 3cc9710d8..2ccfd9342 100644
--- a/tests/test_ext_autosummary.py
+++ b/tests/test_ext_autosummary.py
@@ -242,3 +242,23 @@ def test_autosummary_mock_imports(app, status, warning):
assert app.env.get_doctree('generated/foo')
finally:
sys.modules.pop('foo', None) # unload foo module
+
+
+@pytest.mark.sphinx('dummy', testroot='ext-autosummary-imported_members')
+def test_autosummary_imported_members(app, status, warning):
+ try:
+ app.build()
+ # generated/foo is generated successfully
+ assert app.env.get_doctree('generated/autosummary_dummy_package')
+
+ module = (app.srcdir / 'generated' / 'autosummary_dummy_package.rst').text()
+ assert (' .. autosummary::\n'
+ ' \n'
+ ' Bar\n'
+ ' \n' in module)
+ assert (' .. autosummary::\n'
+ ' \n'
+ ' foo\n'
+ ' \n' in module)
+ finally:
+ sys.modules.pop('autosummary_dummy_package', None)
diff --git a/tests/test_ext_inheritance_diagram.py b/tests/test_ext_inheritance_diagram.py
index 03b5bb689..30ad625aa 100644
--- a/tests/test_ext_inheritance_diagram.py
+++ b/tests/test_ext_inheritance_diagram.py
@@ -90,7 +90,7 @@ def test_inheritance_diagram_latex_alias(app, status, warning):
def test_import_classes(rootdir):
- from sphinx.application import Sphinx, TemplateBridge
+ from sphinx.parsers import Parser, RSTParser
from sphinx.util.i18n import CatalogInfo
try:
@@ -120,16 +120,16 @@ def test_import_classes(rootdir):
assert classes == []
# all of classes in the module
- classes = import_classes('sphinx.application', None)
- assert set(classes) == {Sphinx, TemplateBridge}
+ classes = import_classes('sphinx.parsers', None)
+ assert set(classes) == {Parser, RSTParser}
# specified class in the module
- classes = import_classes('sphinx.application.Sphinx', None)
- assert classes == [Sphinx]
+ classes = import_classes('sphinx.parsers.Parser', None)
+ assert classes == [Parser]
# specified class in current module
- classes = import_classes('Sphinx', 'sphinx.application')
- assert classes == [Sphinx]
+ classes = import_classes('Parser', 'sphinx.parsers')
+ assert classes == [Parser]
# relative module name to current module
classes = import_classes('i18n.CatalogInfo', 'sphinx.util')
diff --git a/tests/test_ext_napoleon_docstring.py b/tests/test_ext_napoleon_docstring.py
index 86ded7d89..a333dc47b 100644
--- a/tests/test_ext_napoleon_docstring.py
+++ b/tests/test_ext_napoleon_docstring.py
@@ -473,12 +473,21 @@ Raises:
A setting wasn't specified, or was invalid.
ValueError:
Something something value error.
+ :py:class:`AttributeError`
+ errors for missing attributes.
+ ~InvalidDimensionsError
+ If the dimensions couldn't be parsed.
+ `InvalidArgumentsError`
+ If the arguments are invalid.
""", """
Example Function
:raises RuntimeError: A setting wasn't specified, or was invalid.
:raises ValueError: Something something value error.
+:raises AttributeError: errors for missing attributes.
+:raises ~InvalidDimensionsError: If the dimensions couldn't be parsed.
+:raises InvalidArgumentsError: If the arguments are invalid.
"""),
################################
("""