-
{{ _('Quick search') }}
+
{{ _('Quick search') }}
diff --git a/sphinx/themes/basic/static/basic.css_t b/sphinx/themes/basic/static/basic.css_t
index 90a14286f..91fd35755 100644
--- a/sphinx/themes/basic/static/basic.css_t
+++ b/sphinx/themes/basic/static/basic.css_t
@@ -289,6 +289,12 @@ img.align-center, .figure.align-center, object.align-center {
margin-right: auto;
}
+img.align-default, .figure.align-default {
+ display: block;
+ margin-left: auto;
+ margin-right: auto;
+}
+
.align-left {
text-align: left;
}
@@ -297,6 +303,10 @@ img.align-center, .figure.align-center, object.align-center {
text-align: center;
}
+.align-default {
+ text-align: center;
+}
+
.align-right {
text-align: right;
}
@@ -368,6 +378,11 @@ table.align-center {
margin-right: auto;
}
+table.align-default {
+ margin-left: auto;
+ margin-right: auto;
+}
+
table caption span.caption-number {
font-style: italic;
}
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..a4e6e52bf 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,
@@ -328,7 +293,7 @@ class FigureAligner(SphinxTransform):
# type: (Any) -> None
matcher = NodeMatcher(nodes.table, nodes.figure)
for node in self.document.traverse(matcher): # type: nodes.Element
- node.setdefault('align', 'center')
+ node.setdefault('align', 'default')
class FilterSystemMessages(SphinxTransform):
@@ -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/transforms/references.py b/sphinx/transforms/references.py
index de512f437..9cdc28c78 100644
--- a/sphinx/transforms/references.py
+++ b/sphinx/transforms/references.py
@@ -9,7 +9,7 @@
"""
from docutils import nodes
-from docutils.transforms.references import Substitutions
+from docutils.transforms.references import DanglingReferences, Substitutions
from sphinx.transforms import SphinxTransform
@@ -31,6 +31,22 @@ class SubstitutionDefinitionsRemover(SphinxTransform):
node.parent.remove(node)
+class SphinxDanglingReferences(DanglingReferences):
+ """DanglingReferences transform which does not output info messages."""
+
+ def apply(self, **kwargs):
+ # type: (Any) -> None
+ try:
+ reporter = self.document.reporter
+ report_level = reporter.report_level
+
+ # suppress INFO level messages for a while
+ reporter.report_level = max(reporter.WARNING_LEVEL, reporter.report_level)
+ super().apply()
+ finally:
+ reporter.report_level = report_level
+
+
class SphinxDomains(SphinxTransform):
"""Collect objects to Sphinx domains for cross references."""
default_priority = 850
@@ -44,6 +60,7 @@ class SphinxDomains(SphinxTransform):
def setup(app):
# type: (Sphinx) -> Dict[str, Any]
app.add_transform(SubstitutionDefinitionsRemover)
+ app.add_transform(SphinxDanglingReferences)
app.add_transform(SphinxDomains)
return {
diff --git a/sphinx/util/docstrings.py b/sphinx/util/docstrings.py
index 97dd60294..31943b2cb 100644
--- a/sphinx/util/docstrings.py
+++ b/sphinx/util/docstrings.py
@@ -15,8 +15,8 @@ if False:
from typing import List # NOQA
-def prepare_docstring(s, ignore=1):
- # type: (str, int) -> List[str]
+def prepare_docstring(s, ignore=1, tabsize=8):
+ # type: (str, int, int) -> List[str]
"""Convert a docstring into lines of parseable reST. Remove common leading
indentation, where the indentation of a given number of lines (usually just
one) is ignored.
@@ -25,7 +25,7 @@ def prepare_docstring(s, ignore=1):
ViewList (used as argument of nested_parse().) An empty line is added to
act as a separator between this docstring and following content.
"""
- lines = s.expandtabs().splitlines()
+ lines = s.expandtabs(tabsize).splitlines()
# Find minimum indentation of any non-blank lines after ignored lines.
margin = sys.maxsize
for line in lines[ignore:]:
diff --git a/sphinx/util/inspect.py b/sphinx/util/inspect.py
index ff399c439..caf333493 100644
--- a/sphinx/util/inspect.py
+++ b/sphinx/util/inspect.py
@@ -14,7 +14,10 @@ import inspect
import re
import sys
import typing
-from functools import partial
+from functools import partial, partialmethod
+from inspect import ( # NOQA
+ isclass, ismethod, ismethoddescriptor, isroutine
+)
from io import StringIO
from sphinx.util import logging
@@ -24,6 +27,17 @@ if False:
# For type annotation
from typing import Any, Callable, Mapping, List, Tuple, Type # NOQA
+if sys.version_info > (3, 7):
+ from types import (
+ ClassMethodDescriptorType,
+ MethodDescriptorType,
+ WrapperDescriptorType
+ )
+else:
+ ClassMethodDescriptorType = type(object.__init__)
+ MethodDescriptorType = type(str.join)
+ WrapperDescriptorType = type(dict.__dict__['fromkeys'])
+
logger = logging.getLogger(__name__)
memory_address_re = re.compile(r' at 0x[0-9a-f]{8,16}(?=>)', re.IGNORECASE)
@@ -113,7 +127,7 @@ def isenumattribute(x):
def ispartial(obj):
# type: (Any) -> bool
"""Check if the object is partial."""
- return isinstance(obj, partial)
+ return isinstance(obj, (partial, partialmethod))
def isclassmethod(obj):
@@ -156,6 +170,34 @@ def isdescriptor(x):
return False
+def isattributedescriptor(obj):
+ # type: (Any) -> bool
+ """Check if the object is an attribute like descriptor."""
+ if inspect.isdatadescriptor(object):
+ # data descriptor is kind of attribute
+ return True
+ elif isdescriptor(obj):
+ # non data descriptor
+ if isfunction(obj) or isbuiltin(obj) or inspect.ismethod(obj):
+ # attribute must not be either function, builtin and method
+ return False
+ elif inspect.isclass(obj):
+ # attribute must not be a class
+ return False
+ elif isinstance(obj, (ClassMethodDescriptorType,
+ MethodDescriptorType,
+ WrapperDescriptorType)):
+ # attribute must not be a method descriptor
+ return False
+ elif type(obj).__name__ == "instancemethod":
+ # attribute must not be an instancemethod (C-API)
+ return False
+ else:
+ return True
+ else:
+ return False
+
+
def isfunction(obj):
# type: (Any) -> bool
"""Check if the object is function."""
@@ -168,6 +210,24 @@ def isbuiltin(obj):
return inspect.isbuiltin(obj) or ispartial(obj) and inspect.isbuiltin(obj.func)
+def iscoroutinefunction(obj):
+ # type: (Any) -> bool
+ """Check if the object is coroutine-function."""
+ if inspect.iscoroutinefunction(obj):
+ return True
+ elif ispartial(obj) and inspect.iscoroutinefunction(obj.func):
+ # partialed
+ return True
+ else:
+ return False
+
+
+def isproperty(obj):
+ # type: (Any) -> bool
+ """Check if the object is property."""
+ return isinstance(obj, property)
+
+
def safe_getattr(obj, name, *defargs):
# type: (Any, str, str) -> object
"""A getattr() that turns all exceptions into AttributeErrors."""
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/util/rst.py b/sphinx/util/rst.py
index c3d32feb7..c897b075a 100644
--- a/sphinx/util/rst.py
+++ b/sphinx/util/rst.py
@@ -9,11 +9,14 @@
"""
import re
+from collections import defaultdict
from contextlib import contextmanager
+from unicodedata import east_asian_width
from docutils.parsers.rst import roles
from docutils.parsers.rst.languages import en as english
from docutils.utils import Reporter
+from jinja2 import environmentfilter
from sphinx.locale import __
from sphinx.util import docutils
@@ -21,13 +24,20 @@ from sphinx.util import logging
if False:
# For type annotation
- from typing import Generator # NOQA
+ from typing import Callable, Dict, Generator # NOQA
from docutils.statemachine import StringList # NOQA
+ from jinja2 import Environment # NOQA
logger = logging.getLogger(__name__)
docinfo_re = re.compile(':\\w+:.*?')
symbols_re = re.compile(r'([!-\-/:-@\[-`{-~])') # symbols without dot(0x2e)
+SECTIONING_CHARS = ['=', '-', '~']
+
+# width of characters
+WIDECHARS = defaultdict(lambda: "WF") # type: Dict[str, str]
+ # WF: Wide + Full-width
+WIDECHARS["ja"] = "WFA" # In Japanese, Ambiguous characters also have double width
def escape(text):
@@ -37,6 +47,29 @@ def escape(text):
return text
+def textwidth(text, widechars='WF'):
+ # type: (str, str) -> int
+ """Get width of text."""
+ def charwidth(char, widechars):
+ # type: (str, str) -> int
+ if east_asian_width(char) in widechars:
+ return 2
+ else:
+ return 1
+
+ return sum(charwidth(c, widechars) for c in text)
+
+
+@environmentfilter
+def heading(env, text, level=1):
+ # type: (Environment, str, int) -> str
+ """Create a heading for *level*."""
+ assert level <= 3
+ width = textwidth(text, WIDECHARS[env.language]) # type: ignore
+ sectioning_char = SECTIONING_CHARS[level - 1]
+ return '%s\n%s' % (text, sectioning_char * width)
+
+
@contextmanager
def default_role(docname, name):
# type: (str, str) -> Generator
diff --git a/sphinx/util/template.py b/sphinx/util/template.py
index 704a42c05..b521c5c79 100644
--- a/sphinx/util/template.py
+++ b/sphinx/util/template.py
@@ -15,7 +15,7 @@ from jinja2.sandbox import SandboxedEnvironment
from sphinx import package_dir
from sphinx.jinja2glue import SphinxFileSystemLoader
from sphinx.locale import get_translator
-from sphinx.util import texescape
+from sphinx.util import rst, texescape
if False:
# For type annotation
@@ -67,9 +67,10 @@ class SphinxRenderer(FileRenderer):
class LaTeXRenderer(SphinxRenderer):
- def __init__(self):
- # type: () -> None
- template_path = os.path.join(package_dir, 'templates', 'latex')
+ def __init__(self, template_path=None):
+ # type: (str) -> None
+ if template_path is None:
+ template_path = os.path.join(package_dir, 'templates', 'latex')
super().__init__(template_path)
# use texescape as escape filter
@@ -83,3 +84,17 @@ class LaTeXRenderer(SphinxRenderer):
self.env.variable_end_string = '%>'
self.env.block_start_string = '<%'
self.env.block_end_string = '%>'
+
+
+class ReSTRenderer(SphinxRenderer):
+ def __init__(self, template_path=None, language=None):
+ # type: (str, str) -> None
+ super().__init__(template_path)
+
+ # add language to environment
+ self.env.extend(language=language)
+
+ # use texescape as escape filter
+ self.env.filters['e'] = rst.escape
+ self.env.filters['escape'] = rst.escape
+ self.env.filters['heading'] = rst.heading
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/sphinx/writers/latex.py b/sphinx/writers/latex.py
index b45a95eef..803dd1dbc 100644
--- a/sphinx/writers/latex.py
+++ b/sphinx/writers/latex.py
@@ -1527,6 +1527,7 @@ class LaTeXTranslator(SphinxTranslator):
(1, 'middle'): ('\\raisebox{-0.5\\height}{', '}'),
(1, 'bottom'): ('\\raisebox{-\\height}{', '}'),
(0, 'center'): ('{\\hspace*{\\fill}', '\\hspace*{\\fill}}'),
+ (0, 'default'): ('{\\hspace*{\\fill}', '\\hspace*{\\fill}}'),
# These 2 don't exactly do the right thing. The image should
# be floated alongside the paragraph. See
# https://www.w3.org/TR/html4/struct/objects.html#adef-align-IMG
diff --git a/sphinx/writers/manpage.py b/sphinx/writers/manpage.py
index 0856ee5ee..7811ccc5b 100644
--- a/sphinx/writers/manpage.py
+++ b/sphinx/writers/manpage.py
@@ -282,7 +282,7 @@ class ManualPageTranslator(SphinxTranslator, BaseTranslator):
def depart_rubric(self, node):
# type: (nodes.Element) -> None
- pass
+ self.body.append('\n')
def visit_seealso(self, node):
# type: (nodes.Element) -> None
diff --git a/tests/roots/test-ext-autodoc/target/bound_method.py b/tests/roots/test-ext-autodoc/target/bound_method.py
new file mode 100644
index 000000000..d48b9ee1c
--- /dev/null
+++ b/tests/roots/test-ext-autodoc/target/bound_method.py
@@ -0,0 +1,7 @@
+class Cls:
+ def method(self):
+ """Method docstring"""
+ pass
+
+
+bound_method = Cls().method
diff --git a/tests/roots/test-ext-autodoc/target/functions.py b/tests/roots/test-ext-autodoc/target/functions.py
new file mode 100644
index 000000000..8ff00f734
--- /dev/null
+++ b/tests/roots/test-ext-autodoc/target/functions.py
@@ -0,0 +1,15 @@
+from functools import partial
+
+
+def func():
+ pass
+
+
+async def coroutinefunc():
+ pass
+
+partial_func = partial(func)
+partial_coroutinefunc = partial(coroutinefunc)
+
+builtin_func = print
+partial_builtin_func = partial(print)
diff --git a/tests/roots/test-ext-autodoc/target/methods.py b/tests/roots/test-ext-autodoc/target/methods.py
new file mode 100644
index 000000000..ad5a6a952
--- /dev/null
+++ b/tests/roots/test-ext-autodoc/target/methods.py
@@ -0,0 +1,29 @@
+from functools import partialmethod
+
+
+class Base():
+ def meth(self):
+ pass
+
+ @staticmethod
+ def staticmeth():
+ pass
+
+ @classmethod
+ def classmeth(cls):
+ pass
+
+ @property
+ def prop(self):
+ pass
+
+ partialmeth = partialmethod(meth)
+
+ async def coroutinemeth(self):
+ pass
+
+ partial_coroutinemeth = partialmethod(coroutinemeth)
+
+
+class Inherited(Base):
+ pass
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_autodoc.py b/tests/test_autodoc.py
index cfb62d1ba..0c3de1fae 100644
--- a/tests/test_autodoc.py
+++ b/tests/test_autodoc.py
@@ -11,6 +11,7 @@
import platform
import sys
+from unittest.mock import Mock
from warnings import catch_warnings
import pytest
@@ -33,7 +34,9 @@ def do_autodoc(app, objtype, name, options=None):
app.env.temp_data.setdefault('docname', 'index') # set dummy docname
doccls = app.registry.documenters[objtype]
docoptions = process_documenter_options(doccls, app.config, options)
- bridge = DocumenterBridge(app.env, LoggingReporter(''), docoptions, 1)
+ state = Mock()
+ state.document.settings.tab_width = 8
+ bridge = DocumenterBridge(app.env, LoggingReporter(''), docoptions, 1, state)
documenter = doccls(bridge, name)
documenter.generate()
@@ -92,7 +95,9 @@ def setup_test():
genopt = options,
result = ViewList(),
filename_set = set(),
+ state = Mock(),
)
+ directive.state.document.settings.tab_width = 8
processed_docstrings = []
processed_signatures = []
@@ -256,6 +261,11 @@ def test_format_signature():
assert formatsig('method', 'H.foo', H.foo2, None, None) == '(*c)'
assert formatsig('method', 'H.foo', H.foo3, None, None) == r"(d='\\n')"
+ # test bound methods interpreted as functions
+ assert formatsig('function', 'foo', H().foo1, None, None) == '(b, *c)'
+ assert formatsig('function', 'foo', H().foo2, None, None) == '(*c)'
+ assert formatsig('function', 'foo', H().foo3, None, None) == r"(d='\\n')"
+
# test exception handling (exception is caught and args is '')
directive.env.config.autodoc_docstring_signature = False
assert formatsig('function', 'int', int, None, None) == ''
@@ -448,6 +458,14 @@ def test_get_doc():
directive.env.config.autoclass_content = 'both'
assert getdocl('class', I) == ['Class docstring', '', 'New docstring']
+ # verify that method docstrings get extracted in both normal case
+ # and in case of bound method posing as a function
+ class J: # NOQA
+ def foo(self):
+ """Method docstring"""
+ assert getdocl('method', J.foo) == ['Method docstring']
+ assert getdocl('function', J().foo) == ['Method docstring']
+
from target import Base, Derived
# NOTE: inspect.getdoc seems not to work with locally defined classes
@@ -689,9 +707,9 @@ def test_autodoc_members(app):
actual = do_autodoc(app, 'class', 'target.Base', options)
assert list(filter(lambda l: '::' in l, actual)) == [
'.. py:class:: Base',
- ' .. py:classmethod:: Base.inheritedclassmeth()',
+ ' .. py:method:: Base.inheritedclassmeth()',
' .. py:method:: Base.inheritedmeth()',
- ' .. py:staticmethod:: Base.inheritedstaticmeth(cls)'
+ ' .. py:method:: Base.inheritedstaticmeth(cls)'
]
# default specific-members
@@ -700,7 +718,7 @@ def test_autodoc_members(app):
assert list(filter(lambda l: '::' in l, actual)) == [
'.. py:class:: Base',
' .. py:method:: Base.inheritedmeth()',
- ' .. py:staticmethod:: Base.inheritedstaticmeth(cls)'
+ ' .. py:method:: Base.inheritedstaticmeth(cls)'
]
@@ -711,7 +729,7 @@ def test_autodoc_exclude_members(app):
actual = do_autodoc(app, 'class', 'target.Base', options)
assert list(filter(lambda l: '::' in l, actual)) == [
'.. py:class:: Base',
- ' .. py:classmethod:: Base.inheritedclassmeth()'
+ ' .. py:method:: Base.inheritedclassmeth()'
]
# members vs exclude-members
@@ -739,9 +757,9 @@ def test_autodoc_undoc_members(app):
' .. py:attribute:: Class.inst_attr_string',
' .. py:attribute:: Class.mdocattr',
' .. py:method:: Class.meth()',
- ' .. py:classmethod:: Class.moore(a, e, f) -> happiness',
- ' .. py:attribute:: Class.prop',
- ' .. py:classmethod:: Class.roger(a, *, b=2, c=3, d=4, e=5, f=6)',
+ ' .. py:method:: Class.moore(a, e, f) -> happiness',
+ ' .. py:method:: Class.prop',
+ ' .. py:method:: Class.roger(a, *, b=2, c=3, d=4, e=5, f=6)',
' .. py:attribute:: Class.skipattr',
' .. py:method:: Class.skipmeth()',
' .. py:attribute:: Class.udocattr',
@@ -756,11 +774,12 @@ def test_autodoc_inherited_members(app):
actual = do_autodoc(app, 'class', 'target.Class', options)
assert list(filter(lambda l: 'method::' in l, actual)) == [
' .. py:method:: Class.excludemeth()',
- ' .. py:classmethod:: Class.inheritedclassmeth()',
+ ' .. py:method:: Class.inheritedclassmeth()',
' .. py:method:: Class.inheritedmeth()',
- ' .. py:staticmethod:: Class.inheritedstaticmeth(cls)',
+ ' .. py:method:: Class.inheritedstaticmeth(cls)',
' .. py:method:: Class.meth()',
- ' .. py:classmethod:: Class.moore(a, e, f) -> happiness',
+ ' .. py:method:: Class.moore(a, e, f) -> happiness',
+ ' .. py:method:: Class.prop',
' .. py:method:: Class.skipmeth()'
]
@@ -819,9 +838,9 @@ def test_autodoc_special_members(app):
' .. py:attribute:: Class.inst_attr_string',
' .. py:attribute:: Class.mdocattr',
' .. py:method:: Class.meth()',
- ' .. py:classmethod:: Class.moore(a, e, f) -> happiness',
- ' .. py:attribute:: Class.prop',
- ' .. py:classmethod:: Class.roger(a, *, b=2, c=3, d=4, e=5, f=6)',
+ ' .. py:method:: Class.moore(a, e, f) -> happiness',
+ ' .. py:method:: Class.prop',
+ ' .. py:method:: Class.roger(a, *, b=2, c=3, d=4, e=5, f=6)',
' .. py:attribute:: Class.skipattr',
' .. py:method:: Class.skipmeth()',
' .. py:attribute:: Class.udocattr',
@@ -939,6 +958,34 @@ def test_autodoc_inner_class(app):
]
+@pytest.mark.sphinx('html', testroot='ext-autodoc')
+def test_autodoc_classmethod(app):
+ actual = do_autodoc(app, 'method', 'target.Base.inheritedclassmeth')
+ assert list(actual) == [
+ '',
+ '.. py:method:: Base.inheritedclassmeth()',
+ ' :module: target',
+ ' :classmethod:',
+ '',
+ ' Inherited class method.',
+ ' '
+ ]
+
+
+@pytest.mark.sphinx('html', testroot='ext-autodoc')
+def test_autodoc_staticmethod(app):
+ actual = do_autodoc(app, 'method', 'target.Base.inheritedstaticmeth')
+ assert list(actual) == [
+ '',
+ '.. py:method:: Base.inheritedstaticmeth(cls)',
+ ' :module: target',
+ ' :staticmethod:',
+ '',
+ ' Inherited static method.',
+ ' '
+ ]
+
+
@pytest.mark.sphinx('html', testroot='ext-autodoc')
def test_autodoc_descriptor(app):
actual = do_autodoc(app, 'attribute', 'target.Class.descr')
@@ -984,12 +1031,12 @@ def test_autodoc_member_order(app):
' .. py:method:: Class.excludemeth()',
' .. py:attribute:: Class.skipattr',
' .. py:attribute:: Class.attr',
- ' .. py:attribute:: Class.prop',
+ ' .. py:method:: Class.prop',
' .. py:attribute:: Class.docattr',
' .. py:attribute:: Class.udocattr',
' .. py:attribute:: Class.mdocattr',
- ' .. py:classmethod:: Class.roger(a, *, b=2, c=3, d=4, e=5, f=6)',
- ' .. py:classmethod:: Class.moore(a, e, f) -> happiness',
+ ' .. py:method:: Class.roger(a, *, b=2, c=3, d=4, e=5, f=6)',
+ ' .. py:method:: Class.moore(a, e, f) -> happiness',
' .. py:attribute:: Class.inst_attr_inline',
' .. py:attribute:: Class.inst_attr_comment',
' .. py:attribute:: Class.inst_attr_string',
@@ -1006,8 +1053,8 @@ def test_autodoc_member_order(app):
'.. py:class:: Class(arg)',
' .. py:method:: Class.excludemeth()',
' .. py:method:: Class.meth()',
- ' .. py:classmethod:: Class.moore(a, e, f) -> happiness',
- ' .. py:classmethod:: Class.roger(a, *, b=2, c=3, d=4, e=5, f=6)',
+ ' .. py:method:: Class.moore(a, e, f) -> happiness',
+ ' .. py:method:: Class.roger(a, *, b=2, c=3, d=4, e=5, f=6)',
' .. py:method:: Class.skipmeth()',
' .. py:method:: Class.undocmeth()',
' .. py:attribute:: Class._private_inst_attr',
@@ -1018,7 +1065,7 @@ def test_autodoc_member_order(app):
' .. py:attribute:: Class.inst_attr_inline',
' .. py:attribute:: Class.inst_attr_string',
' .. py:attribute:: Class.mdocattr',
- ' .. py:attribute:: Class.prop',
+ ' .. py:method:: Class.prop',
' .. py:attribute:: Class.skipattr',
' .. py:attribute:: Class.udocattr'
]
@@ -1040,9 +1087,9 @@ def test_autodoc_member_order(app):
' .. py:attribute:: Class.inst_attr_string',
' .. py:attribute:: Class.mdocattr',
' .. py:method:: Class.meth()',
- ' .. py:classmethod:: Class.moore(a, e, f) -> happiness',
- ' .. py:attribute:: Class.prop',
- ' .. py:classmethod:: Class.roger(a, *, b=2, c=3, d=4, e=5, f=6)',
+ ' .. py:method:: Class.moore(a, e, f) -> happiness',
+ ' .. py:method:: Class.prop',
+ ' .. py:method:: Class.roger(a, *, b=2, c=3, d=4, e=5, f=6)',
' .. py:attribute:: Class.skipattr',
' .. py:method:: Class.skipmeth()',
' .. py:attribute:: Class.udocattr',
@@ -1108,14 +1155,16 @@ def test_autodoc_docstring_signature(app):
' indented line',
' ',
' ',
- ' .. py:attribute:: DocstringSig.prop1',
+ ' .. py:method:: DocstringSig.prop1',
' :module: target',
+ ' :property:',
' ',
' First line of docstring',
' ',
' ',
- ' .. py:attribute:: DocstringSig.prop2',
+ ' .. py:method:: DocstringSig.prop2',
' :module: target',
+ ' :property:',
' ',
' First line of docstring',
' Second line of docstring',
@@ -1150,15 +1199,17 @@ def test_autodoc_docstring_signature(app):
' indented line',
' ',
' ',
- ' .. py:attribute:: DocstringSig.prop1',
+ ' .. py:method:: DocstringSig.prop1',
' :module: target',
+ ' :property:',
' ',
' DocstringSig.prop1(self)',
' First line of docstring',
' ',
' ',
- ' .. py:attribute:: DocstringSig.prop2',
+ ' .. py:method:: DocstringSig.prop2',
' :module: target',
+ ' :property:',
' ',
' First line of docstring',
' Second line of docstring',
@@ -1460,8 +1511,34 @@ def test_partialfunction():
]
+@pytest.mark.usefixtures('setup_test')
+def test_bound_method():
+ options = {"members": None}
+ actual = do_autodoc(app, 'module', 'target.bound_method', options)
+ assert list(actual) == [
+ '',
+ '.. py:module:: target.bound_method',
+ '',
+ '',
+ '.. py:function:: bound_method()',
+ ' :module: target.bound_method',
+ '',
+ ' Method docstring',
+ ' ',
+ ]
+
+
@pytest.mark.usefixtures('setup_test')
def test_coroutine():
+ actual = do_autodoc(app, 'function', 'target.functions.coroutinefunc')
+ assert list(actual) == [
+ '',
+ '.. py:function:: coroutinefunc()',
+ ' :module: target.functions',
+ ' :async:',
+ '',
+ ]
+
options = {"members": None}
actual = do_autodoc(app, 'class', 'target.coroutine.AsyncClass', options)
assert list(actual) == [
@@ -1472,6 +1549,7 @@ def test_coroutine():
' ',
' .. py:method:: AsyncClass.do_coroutine()',
' :module: target.coroutine',
+ ' :async:',
' ',
' A documented coroutine function',
' '
@@ -1527,6 +1605,8 @@ def test_autodoc_default_options(app):
assert ' .. py:attribute:: EnumCls.val4' not in actual
actual = do_autodoc(app, 'class', 'target.CustomIter')
assert ' .. py:method:: target.CustomIter' not in actual
+ actual = do_autodoc(app, 'module', 'target')
+ assert '.. py:function:: save_traceback(app)' not in actual
# with :members:
app.config.autodoc_default_options = {'members': None}
@@ -1590,6 +1670,15 @@ def test_autodoc_default_options(app):
assert ' .. py:method:: CustomIter.snafucate()' in actual
assert ' Makes this snafucated.' in actual
+ # with :imported-members:
+ app.config.autodoc_default_options = {
+ 'members': None,
+ 'imported-members': None,
+ 'ignore-module-all': None,
+ }
+ actual = do_autodoc(app, 'module', 'target')
+ assert '.. py:function:: save_traceback(app)' in actual
+
@pytest.mark.sphinx('html', testroot='ext-autodoc')
def test_autodoc_default_options_with_values(app):
@@ -1614,11 +1703,11 @@ def test_autodoc_default_options_with_values(app):
' .. py:method:: Class.skipmeth()',
' .. py:method:: Class.excludemeth()',
' .. py:attribute:: Class.attr',
- ' .. py:attribute:: Class.prop',
+ ' .. py:method:: Class.prop',
' .. py:attribute:: Class.docattr',
' .. py:attribute:: Class.udocattr',
' .. py:attribute:: Class.mdocattr',
- ' .. py:classmethod:: Class.moore(a, e, f) -> happiness',
+ ' .. py:method:: Class.moore(a, e, f) -> happiness',
' .. py:attribute:: Class.inst_attr_inline',
' .. py:attribute:: Class.inst_attr_comment',
' .. py:attribute:: Class.inst_attr_string',
diff --git a/tests/test_build_html.py b/tests/test_build_html.py
index 1dbf05a4a..2df1f8412 100644
--- a/tests/test_build_html.py
+++ b/tests/test_build_html.py
@@ -565,7 +565,7 @@ def test_numfig_disabled_warn(app, warning):
@pytest.mark.parametrize("fname,expect", flat_dict({
'index.html': [
- (".//div[@class='figure align-center']/p[@class='caption']/"
+ (".//div[@class='figure align-default']/p[@class='caption']/"
"span[@class='caption-number']", None, True),
(".//table/caption/span[@class='caption-number']", None, True),
(".//div[@class='code-block-caption']/"
@@ -582,21 +582,21 @@ def test_numfig_disabled_warn(app, warning):
(".//li/p/a/span", '^Sect.1 Foo$', True),
],
'foo.html': [
- (".//div[@class='figure align-center']/p[@class='caption']/"
+ (".//div[@class='figure align-default']/p[@class='caption']/"
"span[@class='caption-number']", None, True),
(".//table/caption/span[@class='caption-number']", None, True),
(".//div[@class='code-block-caption']/"
"span[@class='caption-number']", None, True),
],
'bar.html': [
- (".//div[@class='figure align-center']/p[@class='caption']/"
+ (".//div[@class='figure align-default']/p[@class='caption']/"
"span[@class='caption-number']", None, True),
(".//table/caption/span[@class='caption-number']", None, True),
(".//div[@class='code-block-caption']/"
"span[@class='caption-number']", None, True),
],
'baz.html': [
- (".//div[@class='figure align-center']/p[@class='caption']/"
+ (".//div[@class='figure align-default']/p[@class='caption']/"
"span[@class='caption-number']", None, True),
(".//table/caption/span[@class='caption-number']", None, True),
(".//div[@class='code-block-caption']/"
@@ -633,9 +633,9 @@ def test_numfig_without_numbered_toctree_warn(app, warning):
@pytest.mark.parametrize("fname,expect", flat_dict({
'index.html': [
- (".//div[@class='figure align-center']/p[@class='caption']/"
+ (".//div[@class='figure align-default']/p[@class='caption']/"
"span[@class='caption-number']", '^Fig. 9 $', True),
- (".//div[@class='figure align-center']/p[@class='caption']/"
+ (".//div[@class='figure align-default']/p[@class='caption']/"
"span[@class='caption-number']", '^Fig. 10 $', True),
(".//table/caption/span[@class='caption-number']",
'^Table 9 $', True),
@@ -657,13 +657,13 @@ def test_numfig_without_numbered_toctree_warn(app, warning):
(".//li/p/code/span", '^Sect.{number}$', True),
],
'foo.html': [
- (".//div[@class='figure align-center']/p[@class='caption']/"
+ (".//div[@class='figure align-default']/p[@class='caption']/"
"span[@class='caption-number']", '^Fig. 1 $', True),
- (".//div[@class='figure align-center']/p[@class='caption']/"
+ (".//div[@class='figure align-default']/p[@class='caption']/"
"span[@class='caption-number']", '^Fig. 2 $', True),
- (".//div[@class='figure align-center']/p[@class='caption']/"
+ (".//div[@class='figure align-default']/p[@class='caption']/"
"span[@class='caption-number']", '^Fig. 3 $', True),
- (".//div[@class='figure align-center']/p[@class='caption']/"
+ (".//div[@class='figure align-default']/p[@class='caption']/"
"span[@class='caption-number']", '^Fig. 4 $', True),
(".//table/caption/span[@class='caption-number']",
'^Table 1 $', True),
@@ -683,11 +683,11 @@ def test_numfig_without_numbered_toctree_warn(app, warning):
"span[@class='caption-number']", '^Listing 4 $', True),
],
'bar.html': [
- (".//div[@class='figure align-center']/p[@class='caption']/"
+ (".//div[@class='figure align-default']/p[@class='caption']/"
"span[@class='caption-number']", '^Fig. 5 $', True),
- (".//div[@class='figure align-center']/p[@class='caption']/"
+ (".//div[@class='figure align-default']/p[@class='caption']/"
"span[@class='caption-number']", '^Fig. 7 $', True),
- (".//div[@class='figure align-center']/p[@class='caption']/"
+ (".//div[@class='figure align-default']/p[@class='caption']/"
"span[@class='caption-number']", '^Fig. 8 $', True),
(".//table/caption/span[@class='caption-number']",
'^Table 5 $', True),
@@ -703,7 +703,7 @@ def test_numfig_without_numbered_toctree_warn(app, warning):
"span[@class='caption-number']", '^Listing 8 $', True),
],
'baz.html': [
- (".//div[@class='figure align-center']/p[@class='caption']/"
+ (".//div[@class='figure align-default']/p[@class='caption']/"
"span[@class='caption-number']", '^Fig. 6 $', True),
(".//table/caption/span[@class='caption-number']",
'^Table 6 $', True),
@@ -741,9 +741,9 @@ def test_numfig_with_numbered_toctree_warn(app, warning):
@pytest.mark.parametrize("fname,expect", flat_dict({
'index.html': [
- (".//div[@class='figure align-center']/p[@class='caption']/"
+ (".//div[@class='figure align-default']/p[@class='caption']/"
"span[@class='caption-number']", '^Fig. 1 $', True),
- (".//div[@class='figure align-center']/p[@class='caption']/"
+ (".//div[@class='figure align-default']/p[@class='caption']/"
"span[@class='caption-number']", '^Fig. 2 $', True),
(".//table/caption/span[@class='caption-number']",
'^Table 1 $', True),
@@ -765,13 +765,13 @@ def test_numfig_with_numbered_toctree_warn(app, warning):
(".//li/p/a/span", '^Sect.1 Foo$', True),
],
'foo.html': [
- (".//div[@class='figure align-center']/p[@class='caption']/"
+ (".//div[@class='figure align-default']/p[@class='caption']/"
"span[@class='caption-number']", '^Fig. 1.1 $', True),
- (".//div[@class='figure align-center']/p[@class='caption']/"
+ (".//div[@class='figure align-default']/p[@class='caption']/"
"span[@class='caption-number']", '^Fig. 1.2 $', True),
- (".//div[@class='figure align-center']/p[@class='caption']/"
+ (".//div[@class='figure align-default']/p[@class='caption']/"
"span[@class='caption-number']", '^Fig. 1.3 $', True),
- (".//div[@class='figure align-center']/p[@class='caption']/"
+ (".//div[@class='figure align-default']/p[@class='caption']/"
"span[@class='caption-number']", '^Fig. 1.4 $', True),
(".//table/caption/span[@class='caption-number']",
'^Table 1.1 $', True),
@@ -791,11 +791,11 @@ def test_numfig_with_numbered_toctree_warn(app, warning):
"span[@class='caption-number']", '^Listing 1.4 $', True),
],
'bar.html': [
- (".//div[@class='figure align-center']/p[@class='caption']/"
+ (".//div[@class='figure align-default']/p[@class='caption']/"
"span[@class='caption-number']", '^Fig. 2.1 $', True),
- (".//div[@class='figure align-center']/p[@class='caption']/"
+ (".//div[@class='figure align-default']/p[@class='caption']/"
"span[@class='caption-number']", '^Fig. 2.3 $', True),
- (".//div[@class='figure align-center']/p[@class='caption']/"
+ (".//div[@class='figure align-default']/p[@class='caption']/"
"span[@class='caption-number']", '^Fig. 2.4 $', True),
(".//table/caption/span[@class='caption-number']",
'^Table 2.1 $', True),
@@ -811,7 +811,7 @@ def test_numfig_with_numbered_toctree_warn(app, warning):
"span[@class='caption-number']", '^Listing 2.4 $', True),
],
'baz.html': [
- (".//div[@class='figure align-center']/p[@class='caption']/"
+ (".//div[@class='figure align-default']/p[@class='caption']/"
"span[@class='caption-number']", '^Fig. 2.2 $', True),
(".//table/caption/span[@class='caption-number']",
'^Table 2.2 $', True),
@@ -846,9 +846,9 @@ def test_numfig_with_prefix_warn(app, warning):
@pytest.mark.parametrize("fname,expect", flat_dict({
'index.html': [
- (".//div[@class='figure align-center']/p[@class='caption']/"
+ (".//div[@class='figure align-default']/p[@class='caption']/"
"span[@class='caption-number']", '^Figure:1 $', True),
- (".//div[@class='figure align-center']/p[@class='caption']/"
+ (".//div[@class='figure align-default']/p[@class='caption']/"
"span[@class='caption-number']", '^Figure:2 $', True),
(".//table/caption/span[@class='caption-number']",
'^Tab_1 $', True),
@@ -870,13 +870,13 @@ def test_numfig_with_prefix_warn(app, warning):
(".//li/p/a/span", '^Sect.1 Foo$', True),
],
'foo.html': [
- (".//div[@class='figure align-center']/p[@class='caption']/"
+ (".//div[@class='figure align-default']/p[@class='caption']/"
"span[@class='caption-number']", '^Figure:1.1 $', True),
- (".//div[@class='figure align-center']/p[@class='caption']/"
+ (".//div[@class='figure align-default']/p[@class='caption']/"
"span[@class='caption-number']", '^Figure:1.2 $', True),
- (".//div[@class='figure align-center']/p[@class='caption']/"
+ (".//div[@class='figure align-default']/p[@class='caption']/"
"span[@class='caption-number']", '^Figure:1.3 $', True),
- (".//div[@class='figure align-center']/p[@class='caption']/"
+ (".//div[@class='figure align-default']/p[@class='caption']/"
"span[@class='caption-number']", '^Figure:1.4 $', True),
(".//table/caption/span[@class='caption-number']",
'^Tab_1.1 $', True),
@@ -896,11 +896,11 @@ def test_numfig_with_prefix_warn(app, warning):
"span[@class='caption-number']", '^Code-1.4 $', True),
],
'bar.html': [
- (".//div[@class='figure align-center']/p[@class='caption']/"
+ (".//div[@class='figure align-default']/p[@class='caption']/"
"span[@class='caption-number']", '^Figure:2.1 $', True),
- (".//div[@class='figure align-center']/p[@class='caption']/"
+ (".//div[@class='figure align-default']/p[@class='caption']/"
"span[@class='caption-number']", '^Figure:2.3 $', True),
- (".//div[@class='figure align-center']/p[@class='caption']/"
+ (".//div[@class='figure align-default']/p[@class='caption']/"
"span[@class='caption-number']", '^Figure:2.4 $', True),
(".//table/caption/span[@class='caption-number']",
'^Tab_2.1 $', True),
@@ -916,7 +916,7 @@ def test_numfig_with_prefix_warn(app, warning):
"span[@class='caption-number']", '^Code-2.4 $', True),
],
'baz.html': [
- (".//div[@class='figure align-center']/p[@class='caption']/"
+ (".//div[@class='figure align-default']/p[@class='caption']/"
"span[@class='caption-number']", '^Figure:2.2 $', True),
(".//table/caption/span[@class='caption-number']",
'^Tab_2.2 $', True),
@@ -952,9 +952,9 @@ def test_numfig_with_secnum_depth_warn(app, warning):
@pytest.mark.parametrize("fname,expect", flat_dict({
'index.html': [
- (".//div[@class='figure align-center']/p[@class='caption']/"
+ (".//div[@class='figure align-default']/p[@class='caption']/"
"span[@class='caption-number']", '^Fig. 1 $', True),
- (".//div[@class='figure align-center']/p[@class='caption']/"
+ (".//div[@class='figure align-default']/p[@class='caption']/"
"span[@class='caption-number']", '^Fig. 2 $', True),
(".//table/caption/span[@class='caption-number']",
'^Table 1 $', True),
@@ -976,13 +976,13 @@ def test_numfig_with_secnum_depth_warn(app, warning):
(".//li/p/a/span", '^Sect.1 Foo$', True),
],
'foo.html': [
- (".//div[@class='figure align-center']/p[@class='caption']/"
+ (".//div[@class='figure align-default']/p[@class='caption']/"
"span[@class='caption-number']", '^Fig. 1.1 $', True),
- (".//div[@class='figure align-center']/p[@class='caption']/"
+ (".//div[@class='figure align-default']/p[@class='caption']/"
"span[@class='caption-number']", '^Fig. 1.1.1 $', True),
- (".//div[@class='figure align-center']/p[@class='caption']/"
+ (".//div[@class='figure align-default']/p[@class='caption']/"
"span[@class='caption-number']", '^Fig. 1.1.2 $', True),
- (".//div[@class='figure align-center']/p[@class='caption']/"
+ (".//div[@class='figure align-default']/p[@class='caption']/"
"span[@class='caption-number']", '^Fig. 1.2.1 $', True),
(".//table/caption/span[@class='caption-number']",
'^Table 1.1 $', True),
@@ -1002,11 +1002,11 @@ def test_numfig_with_secnum_depth_warn(app, warning):
"span[@class='caption-number']", '^Listing 1.2.1 $', True),
],
'bar.html': [
- (".//div[@class='figure align-center']/p[@class='caption']/"
+ (".//div[@class='figure align-default']/p[@class='caption']/"
"span[@class='caption-number']", '^Fig. 2.1.1 $', True),
- (".//div[@class='figure align-center']/p[@class='caption']/"
+ (".//div[@class='figure align-default']/p[@class='caption']/"
"span[@class='caption-number']", '^Fig. 2.1.3 $', True),
- (".//div[@class='figure align-center']/p[@class='caption']/"
+ (".//div[@class='figure align-default']/p[@class='caption']/"
"span[@class='caption-number']", '^Fig. 2.2.1 $', True),
(".//table/caption/span[@class='caption-number']",
'^Table 2.1.1 $', True),
@@ -1022,7 +1022,7 @@ def test_numfig_with_secnum_depth_warn(app, warning):
"span[@class='caption-number']", '^Listing 2.2.1 $', True),
],
'baz.html': [
- (".//div[@class='figure align-center']/p[@class='caption']/"
+ (".//div[@class='figure align-default']/p[@class='caption']/"
"span[@class='caption-number']", '^Fig. 2.1.2 $', True),
(".//table/caption/span[@class='caption-number']",
'^Table 2.1.2 $', True),
@@ -1043,9 +1043,9 @@ def test_numfig_with_secnum_depth(app, cached_etree_parse, fname, expect):
@pytest.mark.parametrize("fname,expect", flat_dict({
'index.html': [
- (".//div[@class='figure align-center']/p[@class='caption']/"
+ (".//div[@class='figure align-default']/p[@class='caption']/"
"span[@class='caption-number']", '^Fig. 1 $', True),
- (".//div[@class='figure align-center']/p[@class='caption']/"
+ (".//div[@class='figure align-default']/p[@class='caption']/"
"span[@class='caption-number']", '^Fig. 2 $', True),
(".//table/caption/span[@class='caption-number']",
'^Table 1 $', True),
@@ -1065,13 +1065,13 @@ def test_numfig_with_secnum_depth(app, cached_etree_parse, fname, expect):
(".//li/p/a/span", '^Section.2.1$', True),
(".//li/p/a/span", '^Fig.1 should be Fig.1$', True),
(".//li/p/a/span", '^Sect.1 Foo$', True),
- (".//div[@class='figure align-center']/p[@class='caption']/"
+ (".//div[@class='figure align-default']/p[@class='caption']/"
"span[@class='caption-number']", '^Fig. 1.1 $', True),
- (".//div[@class='figure align-center']/p[@class='caption']/"
+ (".//div[@class='figure align-default']/p[@class='caption']/"
"span[@class='caption-number']", '^Fig. 1.2 $', True),
- (".//div[@class='figure align-center']/p[@class='caption']/"
+ (".//div[@class='figure align-default']/p[@class='caption']/"
"span[@class='caption-number']", '^Fig. 1.3 $', True),
- (".//div[@class='figure align-center']/p[@class='caption']/"
+ (".//div[@class='figure align-default']/p[@class='caption']/"
"span[@class='caption-number']", '^Fig. 1.4 $', True),
(".//table/caption/span[@class='caption-number']",
'^Table 1.1 $', True),
@@ -1089,11 +1089,11 @@ def test_numfig_with_secnum_depth(app, cached_etree_parse, fname, expect):
"span[@class='caption-number']", '^Listing 1.3 $', True),
(".//div[@class='code-block-caption']/"
"span[@class='caption-number']", '^Listing 1.4 $', True),
- (".//div[@class='figure align-center']/p[@class='caption']/"
+ (".//div[@class='figure align-default']/p[@class='caption']/"
"span[@class='caption-number']", '^Fig. 2.1 $', True),
- (".//div[@class='figure align-center']/p[@class='caption']/"
+ (".//div[@class='figure align-default']/p[@class='caption']/"
"span[@class='caption-number']", '^Fig. 2.3 $', True),
- (".//div[@class='figure align-center']/p[@class='caption']/"
+ (".//div[@class='figure align-default']/p[@class='caption']/"
"span[@class='caption-number']", '^Fig. 2.4 $', True),
(".//table/caption/span[@class='caption-number']",
'^Table 2.1 $', True),
@@ -1107,7 +1107,7 @@ def test_numfig_with_secnum_depth(app, cached_etree_parse, fname, expect):
"span[@class='caption-number']", '^Listing 2.3 $', True),
(".//div[@class='code-block-caption']/"
"span[@class='caption-number']", '^Listing 2.4 $', True),
- (".//div[@class='figure align-center']/p[@class='caption']/"
+ (".//div[@class='figure align-default']/p[@class='caption']/"
"span[@class='caption-number']", '^Fig. 2.2 $', True),
(".//table/caption/span[@class='caption-number']",
'^Table 2.2 $', True),
@@ -1126,11 +1126,11 @@ def test_numfig_with_singlehtml(app, cached_etree_parse, fname, expect):
@pytest.mark.parametrize("fname,expect", flat_dict({
'index.html': [
- (".//div[@class='figure align-center']/p[@class='caption']"
+ (".//div[@class='figure align-default']/p[@class='caption']"
"/span[@class='caption-number']", "Fig. 1", True),
- (".//div[@class='figure align-center']/p[@class='caption']"
+ (".//div[@class='figure align-default']/p[@class='caption']"
"/span[@class='caption-number']", "Fig. 2", True),
- (".//div[@class='figure align-center']/p[@class='caption']"
+ (".//div[@class='figure align-default']/p[@class='caption']"
"/span[@class='caption-number']", "Fig. 3", True),
(".//div//span[@class='caption-number']", "No.1 ", True),
(".//div//span[@class='caption-number']", "No.2 ", True),
@@ -1338,7 +1338,7 @@ def test_html_sidebar(app, status, warning):
assert '
' in result
assert '
Navigation
' in result
assert '
Related Topics
' in result
- assert '
Quick search
' in result
+ assert '
Quick search
' in result
app.builder.add_sidebars('index', ctx)
assert ctx['sidebars'] == ['about.html', 'navigation.html', 'relations.html',
@@ -1353,7 +1353,7 @@ def test_html_sidebar(app, status, warning):
assert '
' not in result
assert '
Navigation
' not in result
assert '
Related Topics
' in result
- assert '
Quick search
' not in result
+ assert '
Quick search
' not in result
app.builder.add_sidebars('index', ctx)
assert ctx['sidebars'] == ['relations.html']
@@ -1367,7 +1367,7 @@ def test_html_sidebar(app, status, warning):
assert '
' not in result
assert '
Navigation
' not in result
assert '
Related Topics
' not in result
- assert '
Quick search
' not in result
+ assert '
Quick search
' not in result
app.builder.add_sidebars('index', ctx)
assert ctx['sidebars'] == []
diff --git a/tests/test_build_manpage.py b/tests/test_build_manpage.py
index 17a2f7eb8..a0a3efb00 100644
--- a/tests/test_build_manpage.py
+++ b/tests/test_build_manpage.py
@@ -59,3 +59,10 @@ def test_default_man_pages():
expected = [('index', 'stasi', 'STASI™ Documentation 1.0',
["Wolfgang Schäuble & G'Beckstein"], 1)]
assert default_man_pages(config) == expected
+
+
+@pytest.mark.sphinx('man', testroot='markup-rubric')
+def test_rubric(app, status, warning):
+ app.build()
+ content = (app.outdir / 'python.1').text()
+ assert 'This is a rubric\n' in content
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_py.py b/tests/test_domain_py.py
index fb6e70914..fac8a838f 100644
--- a/tests/test_domain_py.py
+++ b/tests/test_domain_py.py
@@ -290,3 +290,173 @@ def test_pyobject_prefix(app):
desc)])]))
assert doctree[1][1][1].astext().strip() == 'say' # prefix is stripped
assert doctree[1][1][3].astext().strip() == 'FooBar.say' # not stripped
+
+
+def test_pydata(app):
+ text = ".. py:data:: var\n"
+ domain = app.env.get_domain('py')
+ doctree = restructuredtext.parse(app, text)
+ assert_node(doctree, (addnodes.index,
+ [desc, ([desc_signature, desc_name, "var"],
+ [desc_content, ()])]))
+ assert 'var' in domain.objects
+ assert domain.objects['var'] == ('index', 'data')
+
+
+def test_pyfunction(app):
+ text = (".. py:function:: func1\n"
+ ".. py:function:: func2\n"
+ " :async:\n")
+ domain = app.env.get_domain('py')
+ doctree = restructuredtext.parse(app, text)
+ assert_node(doctree, (addnodes.index,
+ [desc, ([desc_signature, ([desc_name, "func1"],
+ [desc_parameterlist, ()])],
+ [desc_content, ()])],
+ addnodes.index,
+ [desc, ([desc_signature, ([desc_annotation, "async "],
+ [desc_name, "func2"],
+ [desc_parameterlist, ()])],
+ [desc_content, ()])]))
+ assert 'func1' in domain.objects
+ assert domain.objects['func1'] == ('index', 'function')
+ assert 'func2' in domain.objects
+ assert domain.objects['func2'] == ('index', 'function')
+
+
+def test_pymethod_options(app):
+ text = (".. py:class:: Class\n"
+ "\n"
+ " .. py:method:: meth1\n"
+ " .. py:method:: meth2\n"
+ " :classmethod:\n"
+ " .. py:method:: meth3\n"
+ " :staticmethod:\n"
+ " .. py:method:: meth4\n"
+ " :async:\n"
+ " .. py:method:: meth5\n"
+ " :property:\n")
+ domain = app.env.get_domain('py')
+ doctree = restructuredtext.parse(app, text)
+ assert_node(doctree, (addnodes.index,
+ [desc, ([desc_signature, ([desc_annotation, "class "],
+ [desc_name, "Class"])],
+ [desc_content, (addnodes.index,
+ desc,
+ addnodes.index,
+ desc,
+ addnodes.index,
+ desc,
+ addnodes.index,
+ desc,
+ addnodes.index,
+ desc)])]))
+
+ # method
+ assert_node(doctree[1][1][0], addnodes.index,
+ entries=[('single', 'meth1() (Class method)', 'Class.meth1', '', None)])
+ assert_node(doctree[1][1][1], ([desc_signature, ([desc_name, "meth1"],
+ [desc_parameterlist, ()])],
+ [desc_content, ()]))
+ assert 'Class.meth1' in domain.objects
+ assert domain.objects['Class.meth1'] == ('index', 'method')
+
+ # :classmethod:
+ assert_node(doctree[1][1][2], addnodes.index,
+ entries=[('single', 'meth2() (Class class method)', 'Class.meth2', '', None)])
+ assert_node(doctree[1][1][3], ([desc_signature, ([desc_annotation, "classmethod "],
+ [desc_name, "meth2"],
+ [desc_parameterlist, ()])],
+ [desc_content, ()]))
+ assert 'Class.meth2' in domain.objects
+ assert domain.objects['Class.meth2'] == ('index', 'method')
+
+ # :staticmethod:
+ assert_node(doctree[1][1][4], addnodes.index,
+ entries=[('single', 'meth3() (Class static method)', 'Class.meth3', '', None)])
+ assert_node(doctree[1][1][5], ([desc_signature, ([desc_annotation, "static "],
+ [desc_name, "meth3"],
+ [desc_parameterlist, ()])],
+ [desc_content, ()]))
+ assert 'Class.meth3' in domain.objects
+ assert domain.objects['Class.meth3'] == ('index', 'method')
+
+ # :async:
+ assert_node(doctree[1][1][6], addnodes.index,
+ entries=[('single', 'meth4() (Class method)', 'Class.meth4', '', None)])
+ assert_node(doctree[1][1][7], ([desc_signature, ([desc_annotation, "async "],
+ [desc_name, "meth4"],
+ [desc_parameterlist, ()])],
+ [desc_content, ()]))
+ assert 'Class.meth4' in domain.objects
+ assert domain.objects['Class.meth4'] == ('index', 'method')
+
+ # :property:
+ assert_node(doctree[1][1][8], addnodes.index,
+ entries=[('single', 'meth5() (Class property)', 'Class.meth5', '', None)])
+ assert_node(doctree[1][1][9], ([desc_signature, ([desc_annotation, "property "],
+ [desc_name, "meth5"])],
+ [desc_content, ()]))
+ assert 'Class.meth5' in domain.objects
+ assert domain.objects['Class.meth5'] == ('index', 'method')
+
+
+def test_pyclassmethod(app):
+ text = (".. py:class:: Class\n"
+ "\n"
+ " .. py:classmethod:: meth\n")
+ domain = app.env.get_domain('py')
+ doctree = restructuredtext.parse(app, text)
+ assert_node(doctree, (addnodes.index,
+ [desc, ([desc_signature, ([desc_annotation, "class "],
+ [desc_name, "Class"])],
+ [desc_content, (addnodes.index,
+ desc)])]))
+ assert_node(doctree[1][1][0], addnodes.index,
+ entries=[('single', 'meth() (Class class method)', 'Class.meth', '', None)])
+ assert_node(doctree[1][1][1], ([desc_signature, ([desc_annotation, "classmethod "],
+ [desc_name, "meth"],
+ [desc_parameterlist, ()])],
+ [desc_content, ()]))
+ assert 'Class.meth' in domain.objects
+ assert domain.objects['Class.meth'] == ('index', 'method')
+
+
+def test_pystaticmethod(app):
+ text = (".. py:class:: Class\n"
+ "\n"
+ " .. py:staticmethod:: meth\n")
+ domain = app.env.get_domain('py')
+ doctree = restructuredtext.parse(app, text)
+ assert_node(doctree, (addnodes.index,
+ [desc, ([desc_signature, ([desc_annotation, "class "],
+ [desc_name, "Class"])],
+ [desc_content, (addnodes.index,
+ desc)])]))
+ assert_node(doctree[1][1][0], addnodes.index,
+ entries=[('single', 'meth() (Class static method)', 'Class.meth', '', None)])
+ assert_node(doctree[1][1][1], ([desc_signature, ([desc_annotation, "static "],
+ [desc_name, "meth"],
+ [desc_parameterlist, ()])],
+ [desc_content, ()]))
+ assert 'Class.meth' in domain.objects
+ assert domain.objects['Class.meth'] == ('index', 'method')
+
+
+def test_pyattribute(app):
+ text = (".. py:class:: Class\n"
+ "\n"
+ " .. py:attribute:: attr\n")
+ domain = app.env.get_domain('py')
+ doctree = restructuredtext.parse(app, text)
+ assert_node(doctree, (addnodes.index,
+ [desc, ([desc_signature, ([desc_annotation, "class "],
+ [desc_name, "Class"])],
+ [desc_content, (addnodes.index,
+ desc)])]))
+ assert_node(doctree[1][1][0], addnodes.index,
+ entries=[('single', 'attr (Class attribute)', 'Class.attr', '', None)])
+ assert_node(doctree[1][1][1], ([desc_signature, desc_name, "attr"],
+ [desc_content, ()]))
+ assert 'Class.attr' in domain.objects
+ assert domain.objects['Class.attr'] == ('index', 'attribute')
diff --git a/tests/test_domain_rst.py b/tests/test_domain_rst.py
index 70004dcdd..207ff1ff3 100644
--- a/tests/test_domain_rst.py
+++ b/tests/test_domain_rst.py
@@ -8,7 +8,13 @@
:license: BSD, see LICENSE for details.
"""
+from sphinx import addnodes
+from sphinx.addnodes import (
+ desc, desc_addname, desc_annotation, desc_content, desc_name, desc_signature
+)
from sphinx.domains.rst import parse_directive
+from sphinx.testing import restructuredtext
+from sphinx.testing.util import assert_node
def test_parse_directive():
@@ -16,10 +22,119 @@ def test_parse_directive():
assert s == ('foö', '')
s = parse_directive(' .. foö :: ')
- assert s == ('foö', ' ')
+ assert s == ('foö', '')
s = parse_directive('.. foö:: args1 args2')
assert s == ('foö', ' args1 args2')
s = parse_directive('.. :: bar')
assert s == ('.. :: bar', '')
+
+
+def test_rst_directive(app):
+ # bare
+ text = ".. rst:directive:: toctree"
+ doctree = restructuredtext.parse(app, text)
+ assert_node(doctree, (addnodes.index,
+ [desc, ([desc_signature, desc_name, ".. toctree::"],
+ [desc_content, ()])]))
+ assert_node(doctree[0],
+ entries=[("single", "toctree (directive)", "directive-toctree", "", None)])
+ assert_node(doctree[1], addnodes.desc, desctype="directive",
+ domain="rst", objtype="directive", noindex=False)
+
+ # decorated
+ text = ".. rst:directive:: .. toctree::"
+ doctree = restructuredtext.parse(app, text)
+ assert_node(doctree, (addnodes.index,
+ [desc, ([desc_signature, desc_name, ".. toctree::"],
+ [desc_content, ()])]))
+ assert_node(doctree[0],
+ entries=[("single", "toctree (directive)", "directive-toctree", "", None)])
+ assert_node(doctree[1], addnodes.desc, desctype="directive",
+ domain="rst", objtype="directive", noindex=False)
+
+
+def test_rst_directive_with_argument(app):
+ text = ".. rst:directive:: .. toctree:: foo bar baz"
+ doctree = restructuredtext.parse(app, text)
+ assert_node(doctree, (addnodes.index,
+ [desc, ([desc_signature, ([desc_name, ".. toctree::"],
+ [desc_addname, " foo bar baz"])],
+ [desc_content, ()])]))
+ assert_node(doctree[0],
+ entries=[("single", "toctree (directive)", "directive-toctree", "", None)])
+ assert_node(doctree[1], addnodes.desc, desctype="directive",
+ domain="rst", objtype="directive", noindex=False)
+
+
+def test_rst_directive_option(app):
+ text = ".. rst:directive:option:: foo"
+ doctree = restructuredtext.parse(app, text)
+ assert_node(doctree, (addnodes.index,
+ [desc, ([desc_signature, desc_name, ":foo:"],
+ [desc_content, ()])]))
+ assert_node(doctree[0],
+ entries=[("single", ":foo: (directive option)",
+ "directive:option--foo", "", "F")])
+ assert_node(doctree[1], addnodes.desc, desctype="directive:option",
+ domain="rst", objtype="directive:option", noindex=False)
+
+
+def test_rst_directive_option_with_argument(app):
+ text = ".. rst:directive:option:: foo: bar baz"
+ doctree = restructuredtext.parse(app, text)
+ assert_node(doctree, (addnodes.index,
+ [desc, ([desc_signature, ([desc_name, ":foo:"],
+ [desc_annotation, " bar baz"])],
+ [desc_content, ()])]))
+ assert_node(doctree[0],
+ entries=[("single", ":foo: (directive option)",
+ "directive:option--foo", "", "F")])
+ assert_node(doctree[1], addnodes.desc, desctype="directive:option",
+ domain="rst", objtype="directive:option", noindex=False)
+
+
+def test_rst_directive_option_type(app):
+ text = (".. rst:directive:option:: foo\n"
+ " :type: directives.flags\n")
+ doctree = restructuredtext.parse(app, text)
+ assert_node(doctree, (addnodes.index,
+ [desc, ([desc_signature, ([desc_name, ":foo:"],
+ [desc_annotation, " (directives.flags)"])],
+ [desc_content, ()])]))
+ assert_node(doctree[0],
+ entries=[("single", ":foo: (directive option)",
+ "directive:option--foo", "", "F")])
+ assert_node(doctree[1], addnodes.desc, desctype="directive:option",
+ domain="rst", objtype="directive:option", noindex=False)
+
+
+def test_rst_directive_and_directive_option(app):
+ text = (".. rst:directive:: foo\n"
+ "\n"
+ " .. rst:directive:option:: bar\n")
+ doctree = restructuredtext.parse(app, text)
+ assert_node(doctree, (addnodes.index,
+ [desc, ([desc_signature, desc_name, ".. foo::"],
+ [desc_content, (addnodes.index,
+ desc)])]))
+ assert_node(doctree[1][1][0],
+ entries=[("pair", "foo (directive); :bar: (directive option)",
+ "directive:option-foo-bar", "", "B")])
+ assert_node(doctree[1][1][1], ([desc_signature, desc_name, ":bar:"],
+ [desc_content, ()]))
+ assert_node(doctree[1][1][1], addnodes.desc, desctype="directive:option",
+ domain="rst", objtype="directive:option", noindex=False)
+
+
+def test_rst_role(app):
+ text = ".. rst:role:: ref"
+ doctree = restructuredtext.parse(app, text)
+ assert_node(doctree, (addnodes.index,
+ [desc, ([desc_signature, desc_name, ":ref:"],
+ [desc_content, ()])]))
+ assert_node(doctree[0],
+ entries=[("single", "ref (role)", "role-ref", "", None)])
+ assert_node(doctree[1], addnodes.desc, desctype="role",
+ domain="rst", objtype="role", noindex=False)
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 = ''
+from sphinx.testing import restructuredtext
-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)
+@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])])
+ 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])])
-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)
+@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])])
+ [('Sphinx', [[], [('documentation tool', [('', '#index-2')])], 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)
+@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])])
+ 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])])
-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)
+@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_apidoc.py b/tests/test_ext_apidoc.py
index 3f5f367c4..9fefb4ce9 100644
--- a/tests/test_ext_apidoc.py
+++ b/tests/test_ext_apidoc.py
@@ -13,6 +13,7 @@ from collections import namedtuple
import pytest
from sphinx.ext.apidoc import main as apidoc_main
+from sphinx.testing.path import path
@pytest.fixture()
@@ -398,3 +399,216 @@ def test_subpackage_in_toc(make_app, apidoc):
assert 'parent.child.foo' in parent_child
assert (outdir / 'parent.child.foo.rst').isfile()
+
+
+def test_toc_file(tempdir):
+ outdir = path(tempdir)
+ (outdir / 'module').makedirs()
+ (outdir / 'example.py').write_text('')
+ (outdir / 'module' / 'example.py').write_text('')
+ apidoc_main(['-o', tempdir, tempdir])
+ assert (outdir / 'modules.rst').exists()
+
+ content = (outdir / 'modules.rst').text()
+ assert content == ("test_toc_file0\n"
+ "==============\n"
+ "\n"
+ ".. toctree::\n"
+ " :maxdepth: 4\n"
+ "\n"
+ " example\n")
+
+
+def test_module_file(tempdir):
+ outdir = path(tempdir)
+ (outdir / 'example.py').write_text('')
+ apidoc_main(['-o', tempdir, tempdir])
+ assert (outdir / 'example.rst').exists()
+
+ content = (outdir / 'example.rst').text()
+ assert content == ("example module\n"
+ "==============\n"
+ "\n"
+ ".. automodule:: example\n"
+ " :members:\n"
+ " :undoc-members:\n"
+ " :show-inheritance:\n")
+
+
+def test_module_file_noheadings(tempdir):
+ outdir = path(tempdir)
+ (outdir / 'example.py').write_text('')
+ apidoc_main(['--no-headings', '-o', tempdir, tempdir])
+ assert (outdir / 'example.rst').exists()
+
+ content = (outdir / 'example.rst').text()
+ assert content == (".. automodule:: example\n"
+ " :members:\n"
+ " :undoc-members:\n"
+ " :show-inheritance:\n")
+
+
+def test_package_file(tempdir):
+ outdir = path(tempdir)
+ (outdir / 'testpkg').makedirs()
+ (outdir / 'testpkg' / '__init__.py').write_text('')
+ (outdir / 'testpkg' / 'example.py').write_text('')
+ (outdir / 'testpkg' / 'subpkg').makedirs()
+ (outdir / 'testpkg' / 'subpkg' / '__init__.py').write_text('')
+ apidoc_main(['-o', tempdir, tempdir / 'testpkg'])
+ assert (outdir / 'testpkg.rst').exists()
+ assert (outdir / 'testpkg.subpkg.rst').exists()
+
+ content = (outdir / 'testpkg.rst').text()
+ assert content == ("testpkg package\n"
+ "===============\n"
+ "\n"
+ "Subpackages\n"
+ "-----------\n"
+ "\n"
+ ".. toctree::\n"
+ "\n"
+ " testpkg.subpkg\n"
+ "\n"
+ "Submodules\n"
+ "----------\n"
+ "\n"
+ "testpkg.example module\n"
+ "----------------------\n"
+ "\n"
+ ".. automodule:: testpkg.example\n"
+ " :members:\n"
+ " :undoc-members:\n"
+ " :show-inheritance:\n"
+ "\n"
+ "\n"
+ "Module contents\n"
+ "---------------\n"
+ "\n"
+ ".. automodule:: testpkg\n"
+ " :members:\n"
+ " :undoc-members:\n"
+ " :show-inheritance:\n")
+
+ content = (outdir / 'testpkg.subpkg.rst').text()
+ assert content == ("testpkg.subpkg package\n"
+ "======================\n"
+ "\n"
+ "Module contents\n"
+ "---------------\n"
+ "\n"
+ ".. automodule:: testpkg.subpkg\n"
+ " :members:\n"
+ " :undoc-members:\n"
+ " :show-inheritance:\n")
+
+
+def test_package_file_separate(tempdir):
+ outdir = path(tempdir)
+ (outdir / 'testpkg').makedirs()
+ (outdir / 'testpkg' / '__init__.py').write_text('')
+ (outdir / 'testpkg' / 'example.py').write_text('')
+ apidoc_main(['--separate', '-o', tempdir, tempdir / 'testpkg'])
+ assert (outdir / 'testpkg.rst').exists()
+ assert (outdir / 'testpkg.example.rst').exists()
+
+ content = (outdir / 'testpkg.rst').text()
+ assert content == ("testpkg package\n"
+ "===============\n"
+ "\n"
+ "Submodules\n"
+ "----------\n"
+ "\n"
+ ".. toctree::\n"
+ "\n"
+ " testpkg.example\n"
+ "\n"
+ "Module contents\n"
+ "---------------\n"
+ "\n"
+ ".. automodule:: testpkg\n"
+ " :members:\n"
+ " :undoc-members:\n"
+ " :show-inheritance:\n")
+
+ content = (outdir / 'testpkg.example.rst').text()
+ assert content == ("testpkg.example module\n"
+ "======================\n"
+ "\n"
+ ".. automodule:: testpkg.example\n"
+ " :members:\n"
+ " :undoc-members:\n"
+ " :show-inheritance:\n")
+
+
+def test_package_file_module_first(tempdir):
+ outdir = path(tempdir)
+ (outdir / 'testpkg').makedirs()
+ (outdir / 'testpkg' / '__init__.py').write_text('')
+ (outdir / 'testpkg' / 'example.py').write_text('')
+ apidoc_main(['--module-first', '-o', tempdir, tempdir])
+
+ content = (outdir / 'testpkg.rst').text()
+ assert content == ("testpkg package\n"
+ "===============\n"
+ "\n"
+ ".. automodule:: testpkg\n"
+ " :members:\n"
+ " :undoc-members:\n"
+ " :show-inheritance:\n"
+ "\n"
+ "Submodules\n"
+ "----------\n"
+ "\n"
+ "testpkg.example module\n"
+ "----------------------\n"
+ "\n"
+ ".. automodule:: testpkg.example\n"
+ " :members:\n"
+ " :undoc-members:\n"
+ " :show-inheritance:\n"
+ "\n")
+
+
+def test_package_file_without_submodules(tempdir):
+ outdir = path(tempdir)
+ (outdir / 'testpkg').makedirs()
+ (outdir / 'testpkg' / '__init__.py').write_text('')
+ apidoc_main(['-o', tempdir, tempdir / 'testpkg'])
+ assert (outdir / 'testpkg.rst').exists()
+
+ content = (outdir / 'testpkg.rst').text()
+ assert content == ("testpkg package\n"
+ "===============\n"
+ "\n"
+ "Module contents\n"
+ "---------------\n"
+ "\n"
+ ".. automodule:: testpkg\n"
+ " :members:\n"
+ " :undoc-members:\n"
+ " :show-inheritance:\n")
+
+
+def test_namespace_package_file(tempdir):
+ outdir = path(tempdir)
+ (outdir / 'testpkg').makedirs()
+ (outdir / 'testpkg' / 'example.py').write_text('')
+ apidoc_main(['--implicit-namespace', '-o', tempdir, tempdir / 'testpkg'])
+ assert (outdir / 'testpkg.rst').exists()
+
+ content = (outdir / 'testpkg.rst').text()
+ assert content == ("testpkg namespace\n"
+ "=================\n"
+ "\n"
+ "Submodules\n"
+ "----------\n"
+ "\n"
+ "testpkg.example module\n"
+ "----------------------\n"
+ "\n"
+ ".. automodule:: testpkg.example\n"
+ " :members:\n"
+ " :undoc-members:\n"
+ " :show-inheritance:\n"
+ "\n")
diff --git a/tests/test_ext_autosummary.py b/tests/test_ext_autosummary.py
index 1da5b73a4..aabfc2441 100644
--- a/tests/test_ext_autosummary.py
+++ b/tests/test_ext_autosummary.py
@@ -379,3 +379,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_graphviz.py b/tests/test_ext_graphviz.py
index 6a3096c23..ec905aa5f 100644
--- a/tests/test_ext_graphviz.py
+++ b/tests/test_ext_graphviz.py
@@ -21,7 +21,7 @@ def test_graphviz_png_html(app, status, warning):
app.builder.build_all()
content = (app.outdir / 'index.html').text()
- html = (r'