summaryrefslogtreecommitdiffstats
path: root/tests/test_extensions
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-06-05 16:20:59 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-06-05 16:20:59 +0000
commit5de84c9242643f786eff03726286578726d7d390 (patch)
tree8e8eadab2b786c41d7b8a2cdafbb467588928ad0 /tests/test_extensions
parentReleasing progress-linux version 7.2.6-8~progress7.99u1. (diff)
downloadsphinx-5de84c9242643f786eff03726286578726d7d390.tar.xz
sphinx-5de84c9242643f786eff03726286578726d7d390.zip
Merging upstream version 7.3.7.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--tests/test_extensions/__init__.py0
-rw-r--r--tests/test_extensions/autodoc_util.py33
-rw-r--r--tests/test_extensions/ext_napoleon_pep526_data_google.py (renamed from tests/ext_napoleon_pep526_data_google.py)2
-rw-r--r--tests/test_extensions/ext_napoleon_pep526_data_numpy.py (renamed from tests/ext_napoleon_pep526_data_numpy.py)1
-rw-r--r--tests/test_extensions/test_ext_apidoc.py (renamed from tests/test_ext_apidoc.py)20
-rw-r--r--tests/test_extensions/test_ext_autodoc.py (renamed from tests/test_ext_autodoc.py)626
-rw-r--r--tests/test_extensions/test_ext_autodoc_autoattribute.py (renamed from tests/test_ext_autodoc_autoattribute.py)2
-rw-r--r--tests/test_extensions/test_ext_autodoc_autoclass.py (renamed from tests/test_ext_autodoc_autoclass.py)55
-rw-r--r--tests/test_extensions/test_ext_autodoc_autodata.py (renamed from tests/test_ext_autodoc_autodata.py)2
-rw-r--r--tests/test_extensions/test_ext_autodoc_autofunction.py (renamed from tests/test_ext_autodoc_autofunction.py)13
-rw-r--r--tests/test_extensions/test_ext_autodoc_automodule.py (renamed from tests/test_ext_autodoc_automodule.py)2
-rw-r--r--tests/test_extensions/test_ext_autodoc_autoproperty.py (renamed from tests/test_ext_autodoc_autoproperty.py)2
-rw-r--r--tests/test_extensions/test_ext_autodoc_configs.py (renamed from tests/test_ext_autodoc_configs.py)48
-rw-r--r--tests/test_extensions/test_ext_autodoc_events.py (renamed from tests/test_ext_autodoc_events.py)2
-rw-r--r--tests/test_extensions/test_ext_autodoc_mock.py (renamed from tests/test_ext_autodoc_mock.py)0
-rw-r--r--tests/test_extensions/test_ext_autodoc_preserve_defaults.py (renamed from tests/test_ext_autodoc_preserve_defaults.py)2
-rw-r--r--tests/test_extensions/test_ext_autodoc_private_members.py (renamed from tests/test_ext_autodoc_private_members.py)2
-rw-r--r--tests/test_extensions/test_ext_autosectionlabel.py (renamed from tests/test_ext_autosectionlabel.py)28
-rw-r--r--tests/test_extensions/test_ext_autosummary.py (renamed from tests/test_ext_autosummary.py)10
-rw-r--r--tests/test_extensions/test_ext_coverage.py (renamed from tests/test_ext_coverage.py)8
-rw-r--r--tests/test_extensions/test_ext_doctest.py (renamed from tests/test_ext_doctest.py)11
-rw-r--r--tests/test_extensions/test_ext_duration.py (renamed from tests/test_ext_duration.py)0
-rw-r--r--tests/test_extensions/test_ext_extlinks.py (renamed from tests/test_ext_extlinks.py)0
-rw-r--r--tests/test_extensions/test_ext_githubpages.py (renamed from tests/test_ext_githubpages.py)6
-rw-r--r--tests/test_extensions/test_ext_graphviz.py (renamed from tests/test_ext_graphviz.py)44
-rw-r--r--tests/test_extensions/test_ext_ifconfig.py (renamed from tests/test_ext_ifconfig.py)2
-rw-r--r--tests/test_extensions/test_ext_imgconverter.py (renamed from tests/test_ext_imgconverter.py)5
-rw-r--r--tests/test_extensions/test_ext_imgmockconverter.py (renamed from tests/test_ext_imgmockconverter.py)2
-rw-r--r--tests/test_extensions/test_ext_inheritance_diagram.py (renamed from tests/test_ext_inheritance_diagram.py)20
-rw-r--r--tests/test_extensions/test_ext_intersphinx.py (renamed from tests/test_ext_intersphinx.py)134
-rw-r--r--tests/test_extensions/test_ext_math.py (renamed from tests/test_ext_math.py)104
-rw-r--r--tests/test_extensions/test_ext_napoleon.py (renamed from tests/test_ext_napoleon.py)2
-rw-r--r--tests/test_extensions/test_ext_napoleon_docstring.py (renamed from tests/test_ext_napoleon_docstring.py)51
-rw-r--r--tests/test_extensions/test_ext_todo.py (renamed from tests/test_ext_todo.py)7
-rw-r--r--tests/test_extensions/test_ext_viewcode.py (renamed from tests/test_ext_viewcode.py)20
-rw-r--r--tests/test_extensions/test_extension.py (renamed from tests/test_extension.py)0
36 files changed, 931 insertions, 335 deletions
diff --git a/tests/test_extensions/__init__.py b/tests/test_extensions/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/test_extensions/__init__.py
diff --git a/tests/test_extensions/autodoc_util.py b/tests/test_extensions/autodoc_util.py
new file mode 100644
index 0000000..7c4da07
--- /dev/null
+++ b/tests/test_extensions/autodoc_util.py
@@ -0,0 +1,33 @@
+from __future__ import annotations
+
+from typing import TYPE_CHECKING
+from unittest.mock import Mock
+
+# NEVER import those objects from sphinx.ext.autodoc directly
+from sphinx.ext.autodoc.directive import DocumenterBridge, process_documenter_options
+from sphinx.util.docutils import LoggingReporter
+
+if TYPE_CHECKING:
+ from typing import Any
+
+ from docutils.statemachine import StringList
+
+ from sphinx.application import Sphinx
+
+
+def do_autodoc(
+ app: Sphinx,
+ objtype: str,
+ name: str,
+ options: dict[str, Any] | None = None,
+) -> StringList:
+ options = {} if options is None else options.copy()
+ app.env.temp_data.setdefault('docname', 'index') # set dummy docname
+ doccls = app.registry.documenters[objtype]
+ docoptions = process_documenter_options(doccls, app.config, options)
+ state = Mock()
+ state.document.settings.tab_width = 8
+ bridge = DocumenterBridge(app.env, LoggingReporter(''), docoptions, 1, state)
+ documenter = doccls(bridge, name)
+ documenter.generate()
+ return bridge.result
diff --git a/tests/ext_napoleon_pep526_data_google.py b/tests/test_extensions/ext_napoleon_pep526_data_google.py
index bb55b0f..d0692e0 100644
--- a/tests/ext_napoleon_pep526_data_google.py
+++ b/tests/test_extensions/ext_napoleon_pep526_data_google.py
@@ -5,7 +5,7 @@ module_level_var: int = 99
class PEP526GoogleClass:
- """Sample class with PEP 526 annotations and google docstring
+ """Sample class with PEP 526 annotations and google docstring.
Attributes:
attr1: Attr1 description.
diff --git a/tests/ext_napoleon_pep526_data_numpy.py b/tests/test_extensions/ext_napoleon_pep526_data_numpy.py
index b3093a7..eff7746 100644
--- a/tests/ext_napoleon_pep526_data_numpy.py
+++ b/tests/test_extensions/ext_napoleon_pep526_data_numpy.py
@@ -16,5 +16,6 @@ class PEP526NumpyClass:
attr2:
Attr2 description
"""
+
attr1: int
attr2: str
diff --git a/tests/test_ext_apidoc.py b/tests/test_extensions/test_ext_apidoc.py
index 1e089a3..c3c979f 100644
--- a/tests/test_ext_apidoc.py
+++ b/tests/test_extensions/test_ext_apidoc.py
@@ -15,7 +15,7 @@ def apidoc(rootdir, tmp_path, apidoc_params):
coderoot = rootdir / kwargs.get('coderoot', 'test-root')
outdir = tmp_path / 'out'
excludes = [str(coderoot / e) for e in kwargs.get('excludes', [])]
- args = ['-o', str(outdir), '-F', str(coderoot)] + excludes + kwargs.get('options', [])
+ args = ['-o', str(outdir), '-F', str(coderoot), *excludes, *kwargs.get('options', [])]
apidoc_main(args)
return namedtuple('apidoc', 'coderoot,outdir')(coderoot, outdir)
@@ -26,8 +26,7 @@ def apidoc_params(request):
kwargs = {}
for info in reversed(list(request.node.iter_markers("apidoc"))):
- for i, a in enumerate(info.args):
- pargs[i] = a
+ pargs |= dict(enumerate(info.args))
kwargs.update(info.kwargs)
args = [pargs[i] for i in sorted(pargs.keys())]
@@ -302,9 +301,9 @@ def test_extension_parsed(make_app, apidoc):
)
def test_toc_all_references_should_exist_pep420_enabled(make_app, apidoc):
"""All references in toc should exist. This test doesn't say if
- directories with empty __init__.py and and nothing else should be
- skipped, just ensures consistency between what's referenced in the toc
- and what is created. This is the variant with pep420 enabled.
+ directories with empty __init__.py and and nothing else should be
+ skipped, just ensures consistency between what's referenced in the toc
+ and what is created. This is the variant with pep420 enabled.
"""
outdir = apidoc.outdir
assert (outdir / 'conf.py').is_file()
@@ -332,9 +331,9 @@ def test_toc_all_references_should_exist_pep420_enabled(make_app, apidoc):
)
def test_toc_all_references_should_exist_pep420_disabled(make_app, apidoc):
"""All references in toc should exist. This test doesn't say if
- directories with empty __init__.py and and nothing else should be
- skipped, just ensures consistency between what's referenced in the toc
- and what is created. This is the variant with pep420 disabled.
+ directories with empty __init__.py and and nothing else should be
+ skipped, just ensures consistency between what's referenced in the toc
+ and what is created. This is the variant with pep420 disabled.
"""
outdir = apidoc.outdir
assert (outdir / 'conf.py').is_file()
@@ -379,7 +378,7 @@ def extract_toc(path):
)
def test_subpackage_in_toc(make_app, apidoc):
"""Make sure that empty subpackages with non-empty subpackages in them
- are not skipped (issue #4520)
+ are not skipped (issue #4520)
"""
outdir = apidoc.outdir
assert (outdir / 'conf.py').is_file()
@@ -643,7 +642,6 @@ def test_no_duplicates(rootdir, tmp_path):
We can't use pytest.mark.apidoc here as we use a different set of arguments
to apidoc_main
"""
-
original_suffixes = sphinx.ext.apidoc.PY_SUFFIXES
try:
# Ensure test works on Windows
diff --git a/tests/test_ext_autodoc.py b/tests/test_extensions/test_ext_autodoc.py
index 7062763..54f81f2 100644
--- a/tests/test_ext_autodoc.py
+++ b/tests/test_extensions/test_ext_autodoc.py
@@ -4,8 +4,14 @@ This tests mainly the Documenters; the auto directives are tested in a test
source file translated by test_build.
"""
+from __future__ import annotations
+
+import functools
+import itertools
+import operator
import sys
from types import SimpleNamespace
+from typing import TYPE_CHECKING
from unittest.mock import Mock
from warnings import catch_warnings
@@ -14,8 +20,8 @@ from docutils.statemachine import ViewList
from sphinx import addnodes
from sphinx.ext.autodoc import ALL, ModuleLevelDocumenter, Options
-from sphinx.ext.autodoc.directive import DocumenterBridge, process_documenter_options
-from sphinx.util.docutils import LoggingReporter
+
+from tests.test_extensions.autodoc_util import do_autodoc
try:
# Enable pyximport to test cython module
@@ -24,47 +30,35 @@ try:
except ImportError:
pyximport = None
-
-def do_autodoc(app, objtype, name, options=None):
- if options is None:
- options = {}
- app.env.temp_data.setdefault('docname', 'index') # set dummy docname
- doccls = app.registry.documenters[objtype]
- docoptions = process_documenter_options(doccls, app.config, options)
- state = Mock()
- state.document.settings.tab_width = 8
- bridge = DocumenterBridge(app.env, LoggingReporter(''), docoptions, 1, state)
- documenter = doccls(bridge, name)
- documenter.generate()
-
- return bridge.result
+if TYPE_CHECKING:
+ from typing import Any
def make_directive_bridge(env):
options = Options(
- inherited_members = False,
- undoc_members = False,
- private_members = False,
- special_members = False,
- imported_members = False,
- show_inheritance = False,
- no_index = False,
- annotation = None,
- synopsis = '',
- platform = '',
- deprecated = False,
- members = [],
- member_order = 'alphabetical',
- exclude_members = set(),
- ignore_module_all = False,
+ inherited_members=False,
+ undoc_members=False,
+ private_members=False,
+ special_members=False,
+ imported_members=False,
+ show_inheritance=False,
+ no_index=False,
+ annotation=None,
+ synopsis='',
+ platform='',
+ deprecated=False,
+ members=[],
+ member_order='alphabetical',
+ exclude_members=set(),
+ ignore_module_all=False,
)
directive = SimpleNamespace(
- env = env,
- genopt = options,
- result = ViewList(),
- record_dependencies = set(),
- state = Mock(),
+ env=env,
+ genopt=options,
+ result=ViewList(),
+ record_dependencies=set(),
+ state=Mock(),
)
directive.state.document.settings.tab_width = 8
@@ -74,23 +68,6 @@ def make_directive_bridge(env):
processed_signatures = []
-def process_signature(app, what, name, obj, options, args, retann):
- processed_signatures.append((what, name))
- if name == 'bar':
- return '42', None
- return None
-
-
-def skip_member(app, what, name, obj, skip, options):
- if name in ('__special1__', '__special2__'):
- return skip
- if name.startswith('__'):
- return True
- if name == 'skipmeth':
- return True
- return None
-
-
def test_parse_name(app):
def verify(objtype, name, result):
inst = app.registry.documenters[objtype](directive, name)
@@ -103,7 +80,7 @@ def test_parse_name(app):
verify('module', 'test_ext_autodoc', ('test_ext_autodoc', [], None, None))
verify('module', 'test.test_ext_autodoc', ('test.test_ext_autodoc', [], None, None))
verify('module', 'test(arg)', ('test', [], 'arg', None))
- assert 'signature arguments' in app._warning.getvalue()
+ assert 'signature arguments' in app.warning.getvalue()
# for functions/classes
verify('function', 'test_ext_autodoc.raises',
@@ -131,6 +108,21 @@ def test_parse_name(app):
def test_format_signature(app):
+ def process_signature(app, what, name, obj, options, args, retann):
+ processed_signatures.append((what, name))
+ if name == 'bar':
+ return '42', None
+ return None
+
+ def skip_member(app, what, name, obj, skip, options):
+ if name in ('__special1__', '__special2__'):
+ return skip
+ if name.startswith('__'):
+ return True
+ if name == 'skipmeth':
+ return True
+ return None
+
app.connect('autodoc-process-signature', process_signature)
app.connect('autodoc-skip-member', skip_member)
@@ -226,12 +218,14 @@ def test_format_signature(app):
class F2:
"""some docstring for F2."""
+
def __init__(self, *args, **kw):
"""
__init__(a1, a2, kw1=True, kw2=False)
some docstring for __init__.
"""
+
class G2(F2):
pass
@@ -330,7 +324,7 @@ def test_get_doc(app):
inst.format_signature() # handle docstring signatures!
ds = inst.get_doc()
# for testing purposes, concat them and strip the empty line at the end
- res = sum(ds, [])[:-1]
+ res = functools.reduce(operator.iadd, ds, [])[:-1]
print(res)
return res
@@ -342,6 +336,7 @@ def test_get_doc(app):
# standard function, diverse docstring styles...
def f():
"""Docstring"""
+
def g():
"""
Docstring
@@ -837,9 +832,13 @@ def test_autodoc_special_members(app):
]
# all special methods
- options = {"members": None,
- "undoc-members": None,
- "special-members": None}
+ options = {
+ "members": None,
+ "undoc-members": None,
+ "special-members": None,
+ }
+ if sys.version_info >= (3, 13, 0, 'alpha', 5):
+ options["exclude-members"] = "__static_attributes__"
actual = do_autodoc(app, 'class', 'target.Class', options)
assert list(filter(lambda l: '::' in l, actual)) == [
'.. py:class:: Class(arg)',
@@ -1402,73 +1401,411 @@ def test_slots(app):
]
+class _EnumFormatter:
+ def __init__(self, name: str, *, module: str = 'target.enums') -> None:
+ self.name = name
+ self.module = module
+
+ @property
+ def target(self) -> str:
+ """The autodoc target class."""
+ return f'{self.module}.{self.name}'
+
+ def subtarget(self, name: str) -> str:
+ """The autodoc sub-target (an attribute, method, etc)."""
+ return f'{self.target}.{name}'
+
+ def _node(
+ self, role: str, name: str, doc: str, *, args: str, indent: int, **options: Any,
+ ) -> list[str]:
+ prefix = indent * ' '
+ tab = ' ' * 3
+
+ def rst_option(name: str, value: Any) -> str:
+ value = '' if value in {1, True} else value
+ return f'{prefix}{tab}:{name}: {value!s}'.rstrip()
+
+ lines = [
+ '',
+ f'{prefix}.. py:{role}:: {name}{args}',
+ f'{prefix}{tab}:module: {self.module}',
+ *itertools.starmap(rst_option, options.items()),
+ ]
+ if doc:
+ lines.extend(['', f'{prefix}{tab}{doc}'])
+ lines.append('')
+ return lines
+
+ def entry(
+ self,
+ entry_name: str,
+ doc: str = '',
+ *,
+ role: str,
+ args: str = '',
+ indent: int = 3,
+ **rst_options: Any,
+ ) -> list[str]:
+ """Get the RST lines for a named attribute, method, etc."""
+ qualname = f'{self.name}.{entry_name}'
+ return self._node(role, qualname, doc, args=args, indent=indent, **rst_options)
+
+ def brief(self, doc: str, *, indent: int = 0, **options: Any) -> list[str]:
+ """Generate the brief part of the class being documented."""
+ assert doc, f'enumeration class {self.target!r} should have an explicit docstring'
+
+ if sys.version_info[:2] >= (3, 13) or sys.version_info[:3] >= (3, 12, 3):
+ args = ('(value, names=<not given>, *values, module=None, '
+ 'qualname=None, type=None, start=1, boundary=None)')
+ elif sys.version_info[:2] >= (3, 12):
+ args = ('(value, names=None, *values, module=None, '
+ 'qualname=None, type=None, start=1, boundary=None)')
+ elif sys.version_info[:2] >= (3, 11):
+ args = ('(value, names=None, *, module=None, qualname=None, '
+ 'type=None, start=1, boundary=None)')
+ else:
+ args = '(value)'
+
+ return self._node('class', self.name, doc, args=args, indent=indent, **options)
+
+ def method(
+ self, name: str, doc: str, *flags: str, args: str = '()', indent: int = 3,
+ ) -> list[str]:
+ rst_options = dict.fromkeys(flags, '')
+ return self.entry(name, doc, role='method', args=args, indent=indent, **rst_options)
+
+ def member(self, name: str, value: Any, doc: str, *, indent: int = 3) -> list[str]:
+ rst_options = {'value': repr(value)}
+ return self.entry(name, doc, role='attribute', indent=indent, **rst_options)
+
+
+@pytest.fixture()
+def autodoc_enum_options() -> dict[str, object]:
+ """Default autodoc options to use when testing enum's documentation."""
+ return {"members": None, "undoc-members": None}
+
+
+@pytest.mark.sphinx('html', testroot='ext-autodoc')
+def test_enum_class(app, autodoc_enum_options):
+ fmt = _EnumFormatter('EnumCls')
+ options = autodoc_enum_options | {'private-members': None}
+
+ actual = do_autodoc(app, 'class', fmt.target, options)
+ assert list(actual) == [
+ *fmt.brief('this is enum class'),
+ *fmt.method('say_goodbye', 'a classmethod says good-bye to you.', 'classmethod'),
+ *fmt.method('say_hello', 'a method says hello to you.'),
+ *fmt.member('val1', 12, 'doc for val1'),
+ *fmt.member('val2', 23, 'doc for val2'),
+ *fmt.member('val3', 34, 'doc for val3'),
+ *fmt.member('val4', 34, ''), # val4 is alias of val3
+ ]
+
+ # Inherited members exclude the native Enum API (in particular
+ # the 'name' and 'value' properties), unless they were explicitly
+ # redefined by the user in one of the bases.
+ actual = do_autodoc(app, 'class', fmt.target, options | {'inherited-members': None})
+ assert list(actual) == [
+ *fmt.brief('this is enum class'),
+ *fmt.method('say_goodbye', 'a classmethod says good-bye to you.', 'classmethod'),
+ *fmt.method('say_hello', 'a method says hello to you.'),
+ *fmt.member('val1', 12, 'doc for val1'),
+ *fmt.member('val2', 23, 'doc for val2'),
+ *fmt.member('val3', 34, 'doc for val3'),
+ *fmt.member('val4', 34, ''), # val4 is alias of val3
+ ]
+
+ # checks for an attribute of EnumCls
+ actual = do_autodoc(app, 'attribute', fmt.subtarget('val1'))
+ assert list(actual) == fmt.member('val1', 12, 'doc for val1', indent=0)
+
+
@pytest.mark.sphinx('html', testroot='ext-autodoc')
-def test_enum_class(app):
- options = {"members": None}
- actual = do_autodoc(app, 'class', 'target.enums.EnumCls', options)
-
- if sys.version_info[:2] >= (3, 12):
- args = ('(value, names=None, *values, module=None, '
- 'qualname=None, type=None, start=1, boundary=None)')
- elif sys.version_info[:2] >= (3, 11):
- args = ('(value, names=None, *, module=None, qualname=None, '
- 'type=None, start=1, boundary=None)')
- else:
- args = '(value)'
+def test_enum_class_with_data_type(app, autodoc_enum_options):
+ fmt = _EnumFormatter('EnumClassWithDataType')
+ actual = do_autodoc(app, 'class', fmt.target, autodoc_enum_options)
assert list(actual) == [
- '',
- '.. py:class:: EnumCls' + args,
- ' :module: target.enums',
- '',
- ' this is enum class',
- '',
- '',
- ' .. py:method:: EnumCls.say_goodbye()',
- ' :module: target.enums',
- ' :classmethod:',
- '',
- ' a classmethod says good-bye to you.',
- '',
- '',
- ' .. py:method:: EnumCls.say_hello()',
- ' :module: target.enums',
- '',
- ' a method says hello to you.',
- '',
- '',
- ' .. py:attribute:: EnumCls.val1',
- ' :module: target.enums',
- ' :value: 12',
- '',
- ' doc for val1',
- '',
- '',
- ' .. py:attribute:: EnumCls.val2',
- ' :module: target.enums',
- ' :value: 23',
- '',
- ' doc for val2',
- '',
- '',
- ' .. py:attribute:: EnumCls.val3',
- ' :module: target.enums',
- ' :value: 34',
- '',
- ' doc for val3',
- '',
+ *fmt.brief('this is enum class'),
+ *fmt.method('say_goodbye', 'docstring', 'classmethod'),
+ *fmt.method('say_hello', 'docstring'),
+ *fmt.member('x', 'x', ''),
]
- # checks for an attribute of EnumClass
- actual = do_autodoc(app, 'attribute', 'target.enums.EnumCls.val1')
+ options = autodoc_enum_options | {'inherited-members': None}
+ actual = do_autodoc(app, 'class', fmt.target, options)
assert list(actual) == [
- '',
- '.. py:attribute:: EnumCls.val1',
- ' :module: target.enums',
- ' :value: 12',
- '',
- ' doc for val1',
- '',
+ *fmt.brief('this is enum class'),
+ *fmt.entry('dtype', 'docstring', role='property'),
+ *fmt.method('isupper', 'inherited'),
+ *fmt.method('say_goodbye', 'docstring', 'classmethod'),
+ *fmt.method('say_hello', 'docstring'),
+ *fmt.member('x', 'x', ''),
+ ]
+
+
+@pytest.mark.sphinx('html', testroot='ext-autodoc')
+def test_enum_class_with_mixin_type(app, autodoc_enum_options):
+ fmt = _EnumFormatter('EnumClassWithMixinType')
+
+ actual = do_autodoc(app, 'class', fmt.target, autodoc_enum_options)
+ assert list(actual) == [
+ *fmt.brief('this is enum class'),
+ *fmt.method('say_goodbye', 'docstring', 'classmethod'),
+ *fmt.method('say_hello', 'docstring'),
+ *fmt.member('x', 'X', ''),
+ ]
+
+ options = autodoc_enum_options | {'inherited-members': None}
+ actual = do_autodoc(app, 'class', fmt.target, options)
+ assert list(actual) == [
+ *fmt.brief('this is enum class'),
+ *fmt.method('say_goodbye', 'docstring', 'classmethod'),
+ *fmt.method('say_hello', 'docstring'),
+ *fmt.entry('value', 'uppercased', role='property'),
+ *fmt.member('x', 'X', ''),
+ ]
+
+
+@pytest.mark.sphinx('html', testroot='ext-autodoc')
+def test_enum_class_with_mixin_type_and_inheritence(app, autodoc_enum_options):
+ fmt = _EnumFormatter('EnumClassWithMixinTypeInherit')
+
+ actual = do_autodoc(app, 'class', fmt.target, autodoc_enum_options)
+ assert list(actual) == [
+ *fmt.brief('this is enum class'),
+ *fmt.member('x', 'X', ''),
+ ]
+
+ options = autodoc_enum_options | {'inherited-members': None}
+ actual = do_autodoc(app, 'class', fmt.target, options)
+ assert list(actual) == [
+ *fmt.brief('this is enum class'),
+ *fmt.method('say_goodbye', 'inherited', 'classmethod'),
+ *fmt.method('say_hello', 'inherited'),
+ *fmt.entry('value', 'uppercased', role='property'),
+ *fmt.member('x', 'X', ''),
+ ]
+
+
+@pytest.mark.sphinx('html', testroot='ext-autodoc')
+def test_enum_class_with_mixin_enum_type(app, autodoc_enum_options):
+ fmt = _EnumFormatter('EnumClassWithMixinEnumType')
+
+ actual = do_autodoc(app, 'class', fmt.target, autodoc_enum_options)
+ assert list(actual) == [
+ *fmt.brief('this is enum class'),
+ # override() is overridden at the class level so it should be rendered
+ *fmt.method('override', 'overridden'),
+ # say_goodbye() and say_hello() are not rendered since they are inherited
+ *fmt.member('x', 'x', ''),
+ ]
+
+ options = autodoc_enum_options | {'inherited-members': None}
+ actual = do_autodoc(app, 'class', fmt.target, options)
+ assert list(actual) == [
+ *fmt.brief('this is enum class'),
+ *fmt.method('override', 'overridden'),
+ *fmt.method('say_goodbye', 'inherited', 'classmethod'),
+ *fmt.method('say_hello', 'inherited'),
+ *fmt.member('x', 'x', ''),
+ ]
+
+
+@pytest.mark.sphinx('html', testroot='ext-autodoc')
+def test_enum_class_with_mixin_and_data_type(app, autodoc_enum_options):
+ fmt = _EnumFormatter('EnumClassWithMixinAndDataType')
+
+ actual = do_autodoc(app, 'class', fmt.target, autodoc_enum_options)
+ assert list(actual) == [
+ *fmt.brief('this is enum class'),
+ *fmt.method('isupper', 'overridden'),
+ *fmt.method('say_goodbye', 'overridden', 'classmethod'),
+ *fmt.method('say_hello', 'overridden'),
+ *fmt.member('x', 'X', ''),
+ ]
+
+ # add the special member __str__ (but not the inherited members)
+ options = autodoc_enum_options | {'special-members': '__str__'}
+ actual = do_autodoc(app, 'class', fmt.target, options)
+ assert list(actual) == [
+ *fmt.brief('this is enum class'),
+ *fmt.method('__str__', 'overridden'),
+ *fmt.method('isupper', 'overridden'),
+ *fmt.method('say_goodbye', 'overridden', 'classmethod'),
+ *fmt.method('say_hello', 'overridden'),
+ *fmt.member('x', 'X', ''),
+ ]
+
+ options = autodoc_enum_options | {'inherited-members': None}
+ actual = do_autodoc(app, 'class', fmt.target, options)
+ assert list(actual) == [
+ *fmt.brief('this is enum class'),
+ *fmt.entry('dtype', 'docstring', role='property'),
+ *fmt.method('isupper', 'overridden'),
+ *fmt.method('say_goodbye', 'overridden', 'classmethod'),
+ *fmt.method('say_hello', 'overridden'),
+ *fmt.entry('value', 'uppercased', role='property'),
+ *fmt.member('x', 'X', ''),
+ ]
+
+
+@pytest.mark.sphinx('html', testroot='ext-autodoc')
+def test_enum_with_parent_enum(app, autodoc_enum_options):
+ fmt = _EnumFormatter('EnumClassWithParentEnum')
+
+ actual = do_autodoc(app, 'class', fmt.target, autodoc_enum_options)
+ assert list(actual) == [
+ *fmt.brief('this is enum class'),
+ *fmt.method('isupper', 'overridden'),
+ *fmt.member('x', 'X', ''),
+ ]
+
+ # add the special member __str__ (but not the inherited members)
+ options = autodoc_enum_options | {'special-members': '__str__'}
+ actual = do_autodoc(app, 'class', fmt.target, options)
+ assert list(actual) == [
+ *fmt.brief('this is enum class'),
+ *fmt.method('__str__', 'overridden'),
+ *fmt.method('isupper', 'overridden'),
+ *fmt.member('x', 'X', ''),
+ ]
+
+ options = autodoc_enum_options | {'inherited-members': None}
+ actual = do_autodoc(app, 'class', fmt.target, options)
+ assert list(actual) == [
+ *fmt.brief('this is enum class'),
+ *fmt.entry('dtype', 'docstring', role='property'),
+ *fmt.method('isupper', 'overridden'),
+ *fmt.method('override', 'inherited'),
+ *fmt.method('say_goodbye', 'inherited', 'classmethod'),
+ *fmt.method('say_hello', 'inherited'),
+ *fmt.entry('value', 'uppercased', role='property'),
+ *fmt.member('x', 'X', ''),
+ ]
+
+
+@pytest.mark.sphinx('html', testroot='ext-autodoc')
+def test_enum_sunder_method(app, autodoc_enum_options):
+ PRIVATE = {'private-members': None} # sunder methods are recognized as private
+
+ fmt = _EnumFormatter('EnumSunderMissingInNonEnumMixin')
+ actual = do_autodoc(app, 'class', fmt.target, autodoc_enum_options)
+ assert list(actual) == [*fmt.brief('this is enum class')]
+ actual = do_autodoc(app, 'class', fmt.target, autodoc_enum_options | PRIVATE)
+ assert list(actual) == [*fmt.brief('this is enum class')]
+
+ fmt = _EnumFormatter('EnumSunderMissingInEnumMixin')
+ actual = do_autodoc(app, 'class', fmt.target, autodoc_enum_options)
+ assert list(actual) == [*fmt.brief('this is enum class')]
+ actual = do_autodoc(app, 'class', fmt.target, autodoc_enum_options | PRIVATE)
+ assert list(actual) == [*fmt.brief('this is enum class')]
+
+ fmt = _EnumFormatter('EnumSunderMissingInDataType')
+ actual = do_autodoc(app, 'class', fmt.target, autodoc_enum_options)
+ assert list(actual) == [*fmt.brief('this is enum class')]
+ actual = do_autodoc(app, 'class', fmt.target, autodoc_enum_options | PRIVATE)
+ assert list(actual) == [*fmt.brief('this is enum class')]
+
+ fmt = _EnumFormatter('EnumSunderMissingInClass')
+ actual = do_autodoc(app, 'class', fmt.target, autodoc_enum_options)
+ assert list(actual) == [*fmt.brief('this is enum class')]
+ actual = do_autodoc(app, 'class', fmt.target, autodoc_enum_options | PRIVATE)
+ assert list(actual) == [
+ *fmt.brief('this is enum class'),
+ *fmt.method('_missing_', 'docstring', 'classmethod', args='(value)'),
+ ]
+
+
+@pytest.mark.sphinx('html', testroot='ext-autodoc')
+def test_enum_inherited_sunder_method(app, autodoc_enum_options):
+ options = autodoc_enum_options | {'private-members': None, 'inherited-members': None}
+
+ fmt = _EnumFormatter('EnumSunderMissingInNonEnumMixin')
+ actual = do_autodoc(app, 'class', fmt.target, options)
+ assert list(actual) == [
+ *fmt.brief('this is enum class'),
+ *fmt.method('_missing_', 'inherited', 'classmethod', args='(value)'),
+ ]
+
+ fmt = _EnumFormatter('EnumSunderMissingInEnumMixin')
+ actual = do_autodoc(app, 'class', fmt.target, options)
+ assert list(actual) == [
+ *fmt.brief('this is enum class'),
+ *fmt.method('_missing_', 'inherited', 'classmethod', args='(value)'),
+ ]
+
+ fmt = _EnumFormatter('EnumSunderMissingInDataType')
+ actual = do_autodoc(app, 'class', fmt.target, options)
+ assert list(actual) == [
+ *fmt.brief('this is enum class'),
+ *fmt.method('_missing_', 'inherited', 'classmethod', args='(value)'),
+ *fmt.entry('dtype', 'docstring', role='property'),
+ *fmt.method('isupper', 'inherited'),
+ ]
+
+ fmt = _EnumFormatter('EnumSunderMissingInClass')
+ actual = do_autodoc(app, 'class', fmt.target, options)
+ assert list(actual) == [
+ *fmt.brief('this is enum class'),
+ *fmt.method('_missing_', 'docstring', 'classmethod', args='(value)'),
+ ]
+
+
+@pytest.mark.sphinx('html', testroot='ext-autodoc')
+def test_enum_custom_name_property(app, autodoc_enum_options):
+ fmt = _EnumFormatter('EnumNamePropertyInNonEnumMixin')
+ actual = do_autodoc(app, 'class', fmt.target, autodoc_enum_options)
+ assert list(actual) == [*fmt.brief('this is enum class')]
+
+ fmt = _EnumFormatter('EnumNamePropertyInEnumMixin')
+ actual = do_autodoc(app, 'class', fmt.target, autodoc_enum_options)
+ assert list(actual) == [*fmt.brief('this is enum class')]
+
+ fmt = _EnumFormatter('EnumNamePropertyInDataType')
+ actual = do_autodoc(app, 'class', fmt.target, autodoc_enum_options)
+ assert list(actual) == [*fmt.brief('this is enum class')]
+
+ fmt = _EnumFormatter('EnumNamePropertyInClass')
+ actual = do_autodoc(app, 'class', fmt.target, autodoc_enum_options)
+ assert list(actual) == [
+ *fmt.brief('this is enum class'),
+ *fmt.entry('name', 'docstring', role='property'),
+ ]
+
+
+@pytest.mark.sphinx('html', testroot='ext-autodoc')
+def test_enum_inherited_custom_name_property(app, autodoc_enum_options):
+ options = autodoc_enum_options | {"inherited-members": None}
+
+ fmt = _EnumFormatter('EnumNamePropertyInNonEnumMixin')
+ actual = do_autodoc(app, 'class', fmt.target, options)
+ assert list(actual) == [
+ *fmt.brief('this is enum class'),
+ *fmt.entry('name', 'inherited', role='property'),
+ ]
+
+ fmt = _EnumFormatter('EnumNamePropertyInEnumMixin')
+ actual = do_autodoc(app, 'class', fmt.target, options)
+ assert list(actual) == [
+ *fmt.brief('this is enum class'),
+ *fmt.entry('name', 'inherited', role='property'),
+ ]
+
+ fmt = _EnumFormatter('EnumNamePropertyInDataType')
+ actual = do_autodoc(app, 'class', fmt.target, options)
+ assert list(actual) == [
+ *fmt.brief('this is enum class'),
+ *fmt.entry('dtype', 'docstring', role='property'),
+ *fmt.method('isupper', 'inherited'),
+ *fmt.entry('name', 'inherited', role='property'),
+ ]
+
+ fmt = _EnumFormatter('EnumNamePropertyInClass')
+ actual = do_autodoc(app, 'class', fmt.target, options)
+ assert list(actual) == [
+ *fmt.brief('this is enum class'),
+ *fmt.entry('name', 'docstring', role='property'),
]
@@ -2103,6 +2440,55 @@ def test_singledispatchmethod_automethod(app):
]
+@pytest.mark.sphinx('html', testroot='ext-autodoc')
+def test_singledispatchmethod_classmethod(app):
+ options = {"members": None}
+ actual = do_autodoc(app, 'module', 'target.singledispatchmethod_classmethod', options)
+
+ assert list(actual) == [
+ '',
+ '.. py:module:: target.singledispatchmethod_classmethod',
+ '',
+ '',
+ '.. py:class:: Foo()',
+ ' :module: target.singledispatchmethod_classmethod',
+ '',
+ ' docstring',
+ '',
+ '',
+ ' .. py:method:: Foo.class_meth(arg, kwarg=None)',
+ ' Foo.class_meth(arg: float, kwarg=None)',
+ ' Foo.class_meth(arg: int, kwarg=None)',
+ ' Foo.class_meth(arg: str, kwarg=None)',
+ ' Foo.class_meth(arg: dict, kwarg=None)',
+ ' :module: target.singledispatchmethod_classmethod',
+ ' :classmethod:',
+ '',
+ ' A class method for general use.',
+ '',
+ ]
+
+
+@pytest.mark.sphinx('html', testroot='ext-autodoc')
+def test_singledispatchmethod_classmethod_automethod(app):
+ options = {}
+ actual = do_autodoc(app, 'method', 'target.singledispatchmethod_classmethod.Foo.class_meth', options)
+
+ assert list(actual) == [
+ '',
+ '.. py:method:: Foo.class_meth(arg, kwarg=None)',
+ ' Foo.class_meth(arg: float, kwarg=None)',
+ ' Foo.class_meth(arg: int, kwarg=None)',
+ ' Foo.class_meth(arg: str, kwarg=None)',
+ ' Foo.class_meth(arg: dict, kwarg=None)',
+ ' :module: target.singledispatchmethod_classmethod',
+ ' :classmethod:',
+ '',
+ ' A class method for general use.',
+ '',
+ ]
+
+
@pytest.mark.skipif(sys.version_info[:2] >= (3, 13),
reason='Cython does not support Python 3.13 yet.')
@pytest.mark.skipif(pyximport is None, reason='cython is not installed')
@@ -2276,7 +2662,7 @@ def test_pyclass_for_ClassLevelDocumenter(app):
@pytest.mark.sphinx('dummy', testroot='ext-autodoc')
def test_autodoc(app, status, warning):
- app.builder.build_all()
+ app.build(force_all=True)
content = app.env.get_doctree('index')
assert isinstance(content[3], addnodes.desc)
diff --git a/tests/test_ext_autodoc_autoattribute.py b/tests/test_extensions/test_ext_autodoc_autoattribute.py
index 0424af0..41fcc99 100644
--- a/tests/test_ext_autodoc_autoattribute.py
+++ b/tests/test_extensions/test_ext_autodoc_autoattribute.py
@@ -6,7 +6,7 @@ source file translated by test_build.
import pytest
-from .test_ext_autodoc import do_autodoc
+from tests.test_extensions.autodoc_util import do_autodoc
@pytest.mark.sphinx('html', testroot='ext-autodoc')
diff --git a/tests/test_ext_autodoc_autoclass.py b/tests/test_extensions/test_ext_autodoc_autoclass.py
index 92c259a..3e68d60 100644
--- a/tests/test_ext_autodoc_autoclass.py
+++ b/tests/test_extensions/test_ext_autodoc_autoclass.py
@@ -11,7 +11,7 @@ from typing import Union
import pytest
-from .test_ext_autodoc import do_autodoc
+from tests.test_extensions.autodoc_util import do_autodoc
@pytest.mark.sphinx('html', testroot='ext-autodoc')
@@ -169,6 +169,7 @@ def test_undocumented_uninitialized_attributes(app):
]
+@pytest.mark.sphinx('html', testroot='ext-autodoc')
def test_decorators(app):
actual = do_autodoc(app, 'class', 'target.decorator.Baz')
assert list(actual) == [
@@ -275,8 +276,7 @@ def test_show_inheritance_for_subclass_of_generic_type(app):
'.. py:class:: Quux(iterable=(), /)',
' :module: target.classes',
'',
- ' Bases: :py:class:`~typing.List`\\ '
- '[:py:obj:`~typing.Union`\\ [:py:class:`int`, :py:class:`float`]]',
+ ' Bases: :py:class:`~typing.List`\\ [:py:class:`int` | :py:class:`float`]',
'',
' A subclass of List[Union[int, float]]',
'',
@@ -373,6 +373,7 @@ def test_class_doc_from_both(app):
]
+@pytest.mark.sphinx('html', testroot='ext-autodoc')
def test_class_alias(app):
def autodoc_process_docstring(*args):
"""A handler always raises an error.
@@ -391,6 +392,7 @@ def test_class_alias(app):
]
+@pytest.mark.sphinx('html', testroot='ext-autodoc')
def test_class_alias_having_doccomment(app):
actual = do_autodoc(app, 'class', 'target.classes.OtherAlias')
assert list(actual) == [
@@ -403,6 +405,7 @@ def test_class_alias_having_doccomment(app):
]
+@pytest.mark.sphinx('html', testroot='ext-autodoc')
def test_class_alias_for_imported_object_having_doccomment(app):
actual = do_autodoc(app, 'class', 'target.classes.IntAlias')
assert list(actual) == [
@@ -515,3 +518,49 @@ def test_autoattribute_TypeVar_module_level(app):
" alias of TypeVar('T1')",
'',
]
+
+
+@pytest.mark.sphinx('html', testroot='ext-autodoc')
+def test_inherited_instance_variable_with_annotations(app):
+ options = {'members': None,
+ 'inherited-members': None}
+ actual = do_autodoc(app, 'class', 'target.inherited_annotations.NoTypeAnnotation', options)
+ assert list(actual) == [
+ '',
+ '.. py:class:: NoTypeAnnotation()',
+ ' :module: target.inherited_annotations',
+ '',
+ '',
+ ' .. py:attribute:: NoTypeAnnotation.a',
+ ' :module: target.inherited_annotations',
+ ' :value: 1',
+ '',
+ ' Local',
+ '',
+ '',
+ ' .. py:attribute:: NoTypeAnnotation.inherit_me',
+ ' :module: target.inherited_annotations',
+ ' :type: int',
+ '',
+ ' Inherited',
+ '',
+ ]
+
+
+@pytest.mark.sphinx('html', testroot='ext-autodoc')
+def test_no_inherited_instance_variable_with_annotations(app):
+ options = {'members': None}
+ actual = do_autodoc(app, 'class', 'target.inherited_annotations.NoTypeAnnotation2', options)
+ assert list(actual) == [
+ '',
+ '.. py:class:: NoTypeAnnotation2()',
+ ' :module: target.inherited_annotations',
+ '',
+ '',
+ ' .. py:attribute:: NoTypeAnnotation2.a',
+ ' :module: target.inherited_annotations',
+ ' :value: 1',
+ '',
+ ' Local',
+ '',
+ ]
diff --git a/tests/test_ext_autodoc_autodata.py b/tests/test_extensions/test_ext_autodoc_autodata.py
index 83647d9..b794666 100644
--- a/tests/test_ext_autodoc_autodata.py
+++ b/tests/test_extensions/test_ext_autodoc_autodata.py
@@ -6,7 +6,7 @@ source file translated by test_build.
import pytest
-from .test_ext_autodoc import do_autodoc
+from tests.test_extensions.autodoc_util import do_autodoc
@pytest.mark.sphinx('html', testroot='ext-autodoc')
diff --git a/tests/test_ext_autodoc_autofunction.py b/tests/test_extensions/test_ext_autodoc_autofunction.py
index b0cd7d9..5dfa42d 100644
--- a/tests/test_ext_autodoc_autofunction.py
+++ b/tests/test_extensions/test_ext_autodoc_autofunction.py
@@ -6,7 +6,7 @@ source file translated by test_build.
import pytest
-from .test_ext_autodoc import do_autodoc
+from tests.test_extensions.autodoc_util import do_autodoc
@pytest.mark.sphinx('html', testroot='ext-autodoc')
@@ -199,3 +199,14 @@ def test_async_generator(app):
' :async:',
'',
]
+
+
+@pytest.mark.sphinx('html', testroot='ext-autodoc')
+def test_slice_function_arg(app):
+ actual = do_autodoc(app, 'function', 'target.functions.slice_arg_func')
+ assert list(actual) == [
+ '',
+ '.. py:function:: slice_arg_func(arg: float64[:, :])',
+ ' :module: target.functions',
+ '',
+ ]
diff --git a/tests/test_ext_autodoc_automodule.py b/tests/test_extensions/test_ext_autodoc_automodule.py
index 2855020..92565ae 100644
--- a/tests/test_ext_autodoc_automodule.py
+++ b/tests/test_extensions/test_ext_autodoc_automodule.py
@@ -8,7 +8,7 @@ import sys
import pytest
-from .test_ext_autodoc import do_autodoc
+from tests.test_extensions.autodoc_util import do_autodoc
@pytest.mark.sphinx('html', testroot='ext-autodoc')
diff --git a/tests/test_ext_autodoc_autoproperty.py b/tests/test_extensions/test_ext_autodoc_autoproperty.py
index ca8b981..de33117 100644
--- a/tests/test_ext_autodoc_autoproperty.py
+++ b/tests/test_extensions/test_ext_autodoc_autoproperty.py
@@ -6,7 +6,7 @@ source file translated by test_build.
import pytest
-from .test_ext_autodoc import do_autodoc
+from tests.test_extensions.autodoc_util import do_autodoc
@pytest.mark.sphinx('html', testroot='ext-autodoc')
diff --git a/tests/test_ext_autodoc_configs.py b/tests/test_extensions/test_ext_autodoc_configs.py
index 45bc729..6c2af5a 100644
--- a/tests/test_ext_autodoc_configs.py
+++ b/tests/test_extensions/test_ext_autodoc_configs.py
@@ -8,7 +8,7 @@ import pytest
from sphinx.testing import restructuredtext
-from .test_ext_autodoc import do_autodoc
+from tests.test_extensions.autodoc_util import do_autodoc
IS_PYPY = platform.python_implementation() == 'PyPy'
@@ -969,9 +969,9 @@ def test_autodoc_typehints_description(app):
assert ('target.typehints.incr(a, b=1)\n'
'\n'
' Parameters:\n'
- ' * **a** (*int*) --\n'
+ ' * **a** (*int*)\n'
'\n'
- ' * **b** (*int*) --\n'
+ ' * **b** (*int*)\n'
'\n'
' Return type:\n'
' int\n'
@@ -979,7 +979,7 @@ def test_autodoc_typehints_description(app):
assert ('target.typehints.tuple_args(x)\n'
'\n'
' Parameters:\n'
- ' **x** (*tuple**[**int**, **int** | **str**]*) --\n'
+ ' **x** (*tuple**[**int**, **int** | **str**]*)\n'
'\n'
' Return type:\n'
' tuple[int, int]\n'
@@ -1117,11 +1117,11 @@ def test_autodoc_typehints_description_with_documented_init(app):
' Class docstring.\n'
'\n'
' Parameters:\n'
- ' * **x** (*int*) --\n'
+ ' * **x** (*int*)\n'
'\n'
- ' * **args** (*int*) --\n'
+ ' * **args** (*int*)\n'
'\n'
- ' * **kwargs** (*int*) --\n'
+ ' * **kwargs** (*int*)\n'
'\n'
' __init__(x, *args, **kwargs)\n'
'\n'
@@ -1217,9 +1217,9 @@ def test_autodoc_typehints_both(app):
assert ('target.typehints.incr(a: int, b: int = 1) -> int\n'
'\n'
' Parameters:\n'
- ' * **a** (*int*) --\n'
+ ' * **a** (*int*)\n'
'\n'
- ' * **b** (*int*) --\n'
+ ' * **b** (*int*)\n'
'\n'
' Return type:\n'
' int\n'
@@ -1227,7 +1227,7 @@ def test_autodoc_typehints_both(app):
assert ('target.typehints.tuple_args(x: tuple[int, int | str]) -> tuple[int, int]\n'
'\n'
' Parameters:\n'
- ' **x** (*tuple**[**int**, **int** | **str**]*) --\n'
+ ' **x** (*tuple**[**int**, **int** | **str**]*)\n'
'\n'
' Return type:\n'
' tuple[int, int]\n'
@@ -1401,9 +1401,9 @@ def test_autodoc_typehints_description_and_type_aliases(app):
' docstring\n'
'\n'
' Parameters:\n'
- ' * **x** (*myint*) --\n'
+ ' * **x** (*myint*)\n'
'\n'
- ' * **y** (*myint*) --\n'
+ ' * **y** (*myint*)\n'
'\n'
' Return type:\n'
' myint\n'
@@ -1584,6 +1584,14 @@ def test_autodoc_typehints_format_fully_qualified_for_newtype_alias(app):
@pytest.mark.sphinx('html', testroot='ext-autodoc')
def test_autodoc_default_options(app):
+ if (
+ (3, 11, 7) <= sys.version_info < (3, 12)
+ or sys.version_info >= (3, 12, 1)
+ ):
+ list_of_weak_references = " list of weak references to the object"
+ else:
+ list_of_weak_references = " list of weak references to the object (if defined)"
+
# no settings
actual = do_autodoc(app, 'class', 'target.enums.EnumCls')
assert ' .. py:attribute:: EnumCls.val1' not in actual
@@ -1627,7 +1635,7 @@ def test_autodoc_default_options(app):
assert ' Iterate squares of each value.' in actual
if not IS_PYPY:
assert ' .. py:attribute:: CustomIter.__weakref__' in actual
- assert ' list of weak references to the object (if defined)' in actual
+ assert list_of_weak_references in actual
# :exclude-members: None - has no effect. Unlike :members:,
# :special-members:, etc. where None == "include all", here None means
@@ -1651,13 +1659,21 @@ def test_autodoc_default_options(app):
assert ' Iterate squares of each value.' in actual
if not IS_PYPY:
assert ' .. py:attribute:: CustomIter.__weakref__' in actual
- assert ' list of weak references to the object (if defined)' in actual
+ assert list_of_weak_references in actual
assert ' .. py:method:: CustomIter.snafucate()' in actual
assert ' Makes this snafucated.' in actual
@pytest.mark.sphinx('html', testroot='ext-autodoc')
def test_autodoc_default_options_with_values(app):
+ if (
+ (3, 11, 7) <= sys.version_info < (3, 12)
+ or sys.version_info >= (3, 12, 1)
+ ):
+ list_of_weak_references = " list of weak references to the object"
+ else:
+ list_of_weak_references = " list of weak references to the object (if defined)"
+
# with :members:
app.config.autodoc_default_options = {'members': 'val1,val2'}
actual = do_autodoc(app, 'class', 'target.enums.EnumCls')
@@ -1698,7 +1714,7 @@ def test_autodoc_default_options_with_values(app):
assert ' Iterate squares of each value.' in actual
if not IS_PYPY:
assert ' .. py:attribute:: CustomIter.__weakref__' not in actual
- assert ' list of weak references to the object (if defined)' not in actual
+ assert list_of_weak_references not in actual
# with :exclude-members:
app.config.autodoc_default_options = {
@@ -1722,6 +1738,6 @@ def test_autodoc_default_options_with_values(app):
assert ' Iterate squares of each value.' in actual
if not IS_PYPY:
assert ' .. py:attribute:: CustomIter.__weakref__' not in actual
- assert ' list of weak references to the object (if defined)' not in actual
+ assert list_of_weak_references not in actual
assert ' .. py:method:: CustomIter.snafucate()' not in actual
assert ' Makes this snafucated.' not in actual
diff --git a/tests/test_ext_autodoc_events.py b/tests/test_extensions/test_ext_autodoc_events.py
index d821f4c..c0af254 100644
--- a/tests/test_ext_autodoc_events.py
+++ b/tests/test_extensions/test_ext_autodoc_events.py
@@ -4,7 +4,7 @@ import pytest
from sphinx.ext.autodoc import between, cut_lines
-from .test_ext_autodoc import do_autodoc
+from tests.test_extensions.autodoc_util import do_autodoc
@pytest.mark.sphinx('html', testroot='ext-autodoc')
diff --git a/tests/test_ext_autodoc_mock.py b/tests/test_extensions/test_ext_autodoc_mock.py
index 3b90693..3b90693 100644
--- a/tests/test_ext_autodoc_mock.py
+++ b/tests/test_extensions/test_ext_autodoc_mock.py
diff --git a/tests/test_ext_autodoc_preserve_defaults.py b/tests/test_extensions/test_ext_autodoc_preserve_defaults.py
index 70b6146..c1a00ab 100644
--- a/tests/test_ext_autodoc_preserve_defaults.py
+++ b/tests/test_extensions/test_ext_autodoc_preserve_defaults.py
@@ -2,7 +2,7 @@
import pytest
-from .test_ext_autodoc import do_autodoc
+from tests.test_extensions.autodoc_util import do_autodoc
@pytest.mark.sphinx('html', testroot='ext-autodoc',
diff --git a/tests/test_ext_autodoc_private_members.py b/tests/test_extensions/test_ext_autodoc_private_members.py
index bf707bf..bf14414 100644
--- a/tests/test_ext_autodoc_private_members.py
+++ b/tests/test_extensions/test_ext_autodoc_private_members.py
@@ -3,7 +3,7 @@
import pytest
-from .test_ext_autodoc import do_autodoc
+from tests.test_extensions.autodoc_util import do_autodoc
@pytest.mark.sphinx('html', testroot='ext-autodoc')
diff --git a/tests/test_ext_autosectionlabel.py b/tests/test_extensions/test_ext_autosectionlabel.py
index f99a6d3..f854ecf 100644
--- a/tests/test_ext_autosectionlabel.py
+++ b/tests/test_extensions/test_ext_autosectionlabel.py
@@ -7,42 +7,42 @@ import pytest
@pytest.mark.sphinx('html', testroot='ext-autosectionlabel')
def test_autosectionlabel_html(app, status, warning, skipped_labels=False):
- app.builder.build_all()
+ app.build(force_all=True)
content = (app.outdir / 'index.html').read_text(encoding='utf8')
html = ('<li><p><a class="reference internal" href="#introduce-of-sphinx">'
'<span class=".*?">Introduce of Sphinx</span></a></p></li>')
- assert re.search(html, content, re.S)
+ assert re.search(html, content, re.DOTALL)
html = ('<li><p><a class="reference internal" href="#installation">'
'<span class="std std-ref">Installation</span></a></p></li>')
- assert re.search(html, content, re.S)
+ assert re.search(html, content, re.DOTALL)
html = ('<li><p><a class="reference internal" href="#for-windows-users">'
'<span class="std std-ref">For Windows users</span></a></p></li>')
- assert re.search(html, content, re.S)
+ assert re.search(html, content, re.DOTALL)
html = ('<li><p><a class="reference internal" href="#for-unix-users">'
'<span class="std std-ref">For UNIX users</span></a></p></li>')
- assert re.search(html, content, re.S)
+ assert re.search(html, content, re.DOTALL)
html = ('<li><p><a class="reference internal" href="#linux">'
'<span class="std std-ref">Linux</span></a></p></li>')
- assert re.search(html, content, re.S)
+ assert re.search(html, content, re.DOTALL)
html = ('<li><p><a class="reference internal" href="#freebsd">'
'<span class="std std-ref">FreeBSD</span></a></p></li>')
- assert re.search(html, content, re.S)
+ assert re.search(html, content, re.DOTALL)
# for smart_quotes (refs: #4027)
html = ('<li><p><a class="reference internal" '
'href="#this-one-s-got-an-apostrophe">'
'<span class="std std-ref">This one’s got an apostrophe'
'</span></a></p></li>')
- assert re.search(html, content, re.S)
+ assert re.search(html, content, re.DOTALL)
-# Re-use test definition from above, just change the test root directory
+# Reuse test definition from above, just change the test root directory
@pytest.mark.sphinx('html', testroot='ext-autosectionlabel-prefix-document')
def test_autosectionlabel_prefix_document_html(app, status, warning):
test_autosectionlabel_html(app, status, warning)
@@ -51,27 +51,27 @@ def test_autosectionlabel_prefix_document_html(app, status, warning):
@pytest.mark.sphinx('html', testroot='ext-autosectionlabel',
confoverrides={'autosectionlabel_maxdepth': 3})
def test_autosectionlabel_maxdepth(app, status, warning):
- app.builder.build_all()
+ app.build(force_all=True)
content = (app.outdir / 'index.html').read_text(encoding='utf8')
# depth: 1
html = ('<li><p><a class="reference internal" href="#test-ext-autosectionlabel">'
'<span class=".*?">test-ext-autosectionlabel</span></a></p></li>')
- assert re.search(html, content, re.S)
+ assert re.search(html, content, re.DOTALL)
# depth: 2
html = ('<li><p><a class="reference internal" href="#installation">'
'<span class="std std-ref">Installation</span></a></p></li>')
- assert re.search(html, content, re.S)
+ assert re.search(html, content, re.DOTALL)
# depth: 3
html = ('<li><p><a class="reference internal" href="#for-windows-users">'
'<span class="std std-ref">For Windows users</span></a></p></li>')
- assert re.search(html, content, re.S)
+ assert re.search(html, content, re.DOTALL)
# depth: 4
html = '<li><p><span class="xref std std-ref">Linux</span></p></li>'
- assert re.search(html, content, re.S)
+ assert re.search(html, content, re.DOTALL)
assert "WARNING: undefined label: 'linux'" in warning.getvalue()
diff --git a/tests/test_ext_autosummary.py b/tests/test_extensions/test_ext_autosummary.py
index 43f3ae0..d761978 100644
--- a/tests/test_ext_autosummary.py
+++ b/tests/test_extensions/test_ext_autosummary.py
@@ -154,7 +154,7 @@ def test_get_items_summary(make_app, app_params):
def new_get_items(self, names, *args, **kwargs):
results = orig_get_items(self, names, *args, **kwargs)
for name, result in zip(names, results):
- autosummary_items[name] = result
+ autosummary_items[name] = result # NoQA: PERF403
return results
def handler(app, what, name, obj, options, lines):
@@ -167,7 +167,7 @@ def test_get_items_summary(make_app, app_params):
sphinx.ext.autosummary.Autosummary.get_items = new_get_items
try:
- app.builder.build_all()
+ app.build(force_all=True)
finally:
sphinx.ext.autosummary.Autosummary.get_items = orig_get_items
@@ -207,7 +207,7 @@ def str_content(elem):
@pytest.mark.sphinx('xml', **default_kw)
def test_escaping(app, status, warning):
- app.builder.build_all()
+ app.build(force_all=True)
outdir = Path(app.builder.outdir)
@@ -358,7 +358,7 @@ def test_autosummary_generate_content_for_module_imported_members_inherited_modu
@pytest.mark.sphinx('dummy', testroot='ext-autosummary')
def test_autosummary_generate(app, status, warning):
- app.builder.build_all()
+ app.build(force_all=True)
doctree = app.env.get_doctree('index')
assert_node(doctree, (nodes.paragraph,
@@ -544,7 +544,7 @@ def test_autosummary_filename_map(app, status, warning):
@pytest.mark.sphinx('latex', **default_kw)
def test_autosummary_latex_table_colspec(app, status, warning):
- app.builder.build_all()
+ app.build(force_all=True)
result = (app.outdir / 'python.tex').read_text(encoding='utf8')
print(status.getvalue())
print(warning.getvalue())
diff --git a/tests/test_ext_coverage.py b/tests/test_extensions/test_ext_coverage.py
index af8cf53..c9e9ba9 100644
--- a/tests/test_ext_coverage.py
+++ b/tests/test_extensions/test_ext_coverage.py
@@ -7,7 +7,7 @@ import pytest
@pytest.mark.sphinx('coverage')
def test_build(app, status, warning):
- app.builder.build_all()
+ app.build(force_all=True)
py_undoc = (app.outdir / 'python.txt').read_text(encoding='utf8')
assert py_undoc.startswith('Undocumented Python objects\n'
@@ -45,7 +45,7 @@ def test_build(app, status, warning):
@pytest.mark.sphinx('coverage', testroot='ext-coverage')
def test_coverage_ignore_pyobjects(app, status, warning):
- app.builder.build_all()
+ app.build(force_all=True)
actual = (app.outdir / 'python.txt').read_text(encoding='utf8')
expected = '''\
Undocumented Python objects
@@ -78,7 +78,7 @@ Classes:
@pytest.mark.sphinx('coverage', confoverrides={'coverage_show_missing_items': True})
def test_show_missing_items(app, status, warning):
- app.builder.build_all()
+ app.build(force_all=True)
assert "undocumented" in status.getvalue()
@@ -92,7 +92,7 @@ def test_show_missing_items(app, status, warning):
@pytest.mark.sphinx('coverage', confoverrides={'coverage_show_missing_items': True})
def test_show_missing_items_quiet(app, status, warning):
app.quiet = True
- app.builder.build_all()
+ app.build(force_all=True)
assert "undocumented python function: autodoc_target :: raises" in warning.getvalue()
assert "undocumented python class: autodoc_target :: Base" in warning.getvalue()
diff --git a/tests/test_ext_doctest.py b/tests/test_extensions/test_ext_doctest.py
index c83e582..ab0dd62 100644
--- a/tests/test_ext_doctest.py
+++ b/tests/test_extensions/test_ext_doctest.py
@@ -16,9 +16,8 @@ cleanup_called = 0
def test_build(app, status, warning):
global cleanup_called
cleanup_called = 0
- app.builder.build_all()
- if app.statuscode != 0:
- raise AssertionError('failures in doctests:' + status.getvalue())
+ app.build(force_all=True)
+ assert app.statuscode == 0, f'failures in doctests:\n{status.getvalue()}'
# in doctest.txt, there are two named groups and the default group,
# so the cleanup function must be called three times
assert cleanup_called == 3, 'testcleanup did not get executed enough times'
@@ -80,13 +79,13 @@ def test_skipif(app, status, warning):
The tests are separated into a different test root directory since the
``app`` object only evaluates options once in its lifetime. If these tests
were combined with the other doctest tests, the ``:skipif:`` evaluations
- would be recorded only on the first ``app.builder.build_all()`` run, i.e.
+ would be recorded only on the first ``app.build(force_all=True)`` run, i.e.
in ``test_build`` above, and the assertion below would fail.
"""
global recorded_calls
recorded_calls = Counter()
- app.builder.build_all()
+ app.build(force_all=True)
if app.statuscode != 0:
raise AssertionError('failures in doctests:' + status.getvalue())
# The `:skipif:` expressions are always run.
@@ -124,7 +123,7 @@ def test_reporting_with_autodoc(app, status, warning, capfd):
# Patch builder to get a copy of the output
written = []
app.builder._warn_out = written.append
- app.builder.build_all()
+ app.build(force_all=True)
failures = [line.replace(os.sep, '/')
for line in '\n'.join(written).splitlines()
diff --git a/tests/test_ext_duration.py b/tests/test_extensions/test_ext_duration.py
index 4fa4dfc..4fa4dfc 100644
--- a/tests/test_ext_duration.py
+++ b/tests/test_extensions/test_ext_duration.py
diff --git a/tests/test_ext_extlinks.py b/tests/test_extensions/test_ext_extlinks.py
index 7634db6..7634db6 100644
--- a/tests/test_ext_extlinks.py
+++ b/tests/test_extensions/test_ext_extlinks.py
diff --git a/tests/test_ext_githubpages.py b/tests/test_extensions/test_ext_githubpages.py
index 8e41537..879b6d1 100644
--- a/tests/test_ext_githubpages.py
+++ b/tests/test_extensions/test_ext_githubpages.py
@@ -5,7 +5,7 @@ import pytest
@pytest.mark.sphinx('html', testroot='ext-githubpages')
def test_githubpages(app, status, warning):
- app.builder.build_all()
+ app.build(force_all=True)
assert (app.outdir / '.nojekyll').exists()
assert not (app.outdir / 'CNAME').exists()
@@ -13,7 +13,7 @@ def test_githubpages(app, status, warning):
@pytest.mark.sphinx('html', testroot='ext-githubpages',
confoverrides={'html_baseurl': 'https://sphinx-doc.github.io'})
def test_no_cname_for_github_io_domain(app, status, warning):
- app.builder.build_all()
+ app.build(force_all=True)
assert (app.outdir / '.nojekyll').exists()
assert not (app.outdir / 'CNAME').exists()
@@ -21,6 +21,6 @@ def test_no_cname_for_github_io_domain(app, status, warning):
@pytest.mark.sphinx('html', testroot='ext-githubpages',
confoverrides={'html_baseurl': 'https://sphinx-doc.org'})
def test_cname_for_custom_domain(app, status, warning):
- app.builder.build_all()
+ app.build(force_all=True)
assert (app.outdir / '.nojekyll').exists()
assert (app.outdir / 'CNAME').read_text(encoding='utf8') == 'sphinx-doc.org'
diff --git a/tests/test_ext_graphviz.py b/tests/test_extensions/test_ext_graphviz.py
index d63dc2a..866a92a 100644
--- a/tests/test_ext_graphviz.py
+++ b/tests/test_extensions/test_ext_graphviz.py
@@ -11,40 +11,40 @@ from sphinx.ext.graphviz import ClickableMapDefinition
@pytest.mark.sphinx('html', testroot='ext-graphviz')
@pytest.mark.usefixtures('if_graphviz_found')
def test_graphviz_png_html(app, status, warning):
- app.builder.build_all()
+ app.build(force_all=True)
content = (app.outdir / 'index.html').read_text(encoding='utf8')
html = (r'<figure class="align-default" .*?>\s*'
r'<div class="graphviz"><img .*?/></div>\s*<figcaption>\s*'
r'<p><span class="caption-text">caption of graph</span>.*</p>\s*'
r'</figcaption>\s*</figure>')
- assert re.search(html, content, re.S)
+ assert re.search(html, content, re.DOTALL)
html = 'Hello <div class="graphviz"><img .*?/></div>\n graphviz world'
- assert re.search(html, content, re.S)
+ assert re.search(html, content, re.DOTALL)
html = ('<img src=".*?" alt="digraph foo {\nbaz -&gt; qux\n}" '
'class="graphviz neato-graph" />')
- assert re.search(html, content, re.S)
+ assert re.search(html, content, re.DOTALL)
html = (r'<figure class="align-right" .*?>\s*'
r'<div class="graphviz"><img .*?/></div>\s*<figcaption>\s*'
r'<p><span class="caption-text">on <em>right</em></span>.*</p>\s*'
r'</figcaption>\s*</figure>')
- assert re.search(html, content, re.S)
+ assert re.search(html, content, re.DOTALL)
html = (r'<div align=\"center\" class=\"align-center\">'
r'<div class="graphviz"><img src=\".*\.png\" alt=\"digraph foo {\n'
r'centered\n'
r'}\" class="graphviz" /></div>\n</div>')
- assert re.search(html, content, re.S)
+ assert re.search(html, content, re.DOTALL)
@pytest.mark.sphinx('html', testroot='ext-graphviz',
confoverrides={'graphviz_output_format': 'svg'})
@pytest.mark.usefixtures('if_graphviz_found')
def test_graphviz_svg_html(app, status, warning):
- app.builder.build_all()
+ app.build(force_all=True)
content = (app.outdir / 'index.html').read_text(encoding='utf8')
@@ -57,12 +57,12 @@ def test_graphviz_svg_html(app, status, warning):
r'<p><span class=\"caption-text\">caption of graph</span>.*</p>\n'
r'</figcaption>\n'
r'</figure>')
- assert re.search(html, content, re.S)
+ assert re.search(html, content, re.DOTALL)
html = (r'Hello <div class="graphviz"><object.*>\n'
r'\s*<p class=\"warning\">graph</p></object></div>\n'
r' graphviz world')
- assert re.search(html, content, re.S)
+ assert re.search(html, content, re.DOTALL)
html = (r'<figure class=\"align-right\" .*\>\n'
r'<div class="graphviz"><object data=\".*\.svg\".*>\n'
@@ -73,7 +73,7 @@ def test_graphviz_svg_html(app, status, warning):
r'<p><span class=\"caption-text\">on <em>right</em></span>.*</p>\n'
r'</figcaption>\n'
r'</figure>')
- assert re.search(html, content, re.S)
+ assert re.search(html, content, re.DOTALL)
html = (r'<div align=\"center\" class=\"align-center\">'
r'<div class="graphviz"><object data=\".*\.svg\".*>\n'
@@ -81,10 +81,10 @@ def test_graphviz_svg_html(app, status, warning):
r'centered\n'
r'}</p></object></div>\n'
r'</div>')
- assert re.search(html, content, re.S)
+ assert re.search(html, content, re.DOTALL)
image_re = r'.*data="([^"]+)".*?digraph test'
- image_path_match = re.search(image_re, content, re.S)
+ image_path_match = re.search(image_re, content, re.DOTALL)
assert image_path_match
image_path = image_path_match.group(1)
@@ -103,37 +103,37 @@ def test_graphviz_svg_html(app, status, warning):
@pytest.mark.sphinx('latex', testroot='ext-graphviz')
@pytest.mark.usefixtures('if_graphviz_found')
def test_graphviz_latex(app, status, warning):
- app.builder.build_all()
+ app.build(force_all=True)
content = (app.outdir / 'python.tex').read_text(encoding='utf8')
macro = ('\\\\begin{figure}\\[htbp\\]\n\\\\centering\n\\\\capstart\n\n'
'\\\\sphinxincludegraphics\\[\\]{graphviz-\\w+.pdf}\n'
'\\\\caption{caption of graph}\\\\label{.*}\\\\end{figure}')
- assert re.search(macro, content, re.S)
+ assert re.search(macro, content, re.DOTALL)
macro = 'Hello \\\\sphinxincludegraphics\\[\\]{graphviz-\\w+.pdf} graphviz world'
- assert re.search(macro, content, re.S)
+ assert re.search(macro, content, re.DOTALL)
macro = ('\\\\begin{wrapfigure}{r}{0pt}\n\\\\centering\n'
'\\\\sphinxincludegraphics\\[\\]{graphviz-\\w+.pdf}\n'
'\\\\caption{on \\\\sphinxstyleemphasis{right}}'
'\\\\label{.*}\\\\end{wrapfigure}')
- assert re.search(macro, content, re.S)
+ assert re.search(macro, content, re.DOTALL)
macro = (r'\{\\hfill'
r'\\sphinxincludegraphics\[\]{graphviz-.*}'
r'\\hspace\*{\\fill}}')
- assert re.search(macro, content, re.S)
+ assert re.search(macro, content, re.DOTALL)
@pytest.mark.sphinx('html', testroot='ext-graphviz', confoverrides={'language': 'xx'})
@pytest.mark.usefixtures('if_graphviz_found')
def test_graphviz_i18n(app, status, warning):
- app.builder.build_all()
+ app.build(force_all=True)
content = (app.outdir / 'index.html').read_text(encoding='utf8')
html = '<img src=".*?" alt="digraph {\n BAR -&gt; BAZ\n}" class="graphviz" />'
- assert re.search(html, content, re.M)
+ assert re.search(html, content, re.MULTILINE)
def test_graphviz_parse_mapfile():
@@ -150,17 +150,17 @@ def test_graphviz_parse_mapfile():
# normal graph
code = ('digraph {\n'
- ' foo [href="http://www.google.com/"];\n'
+ ' foo [href="https://www.google.com/"];\n'
' foo -> bar;\n'
'}\n')
content = ('<map id="%3" name="%3">\n'
- '<area shape="poly" id="node1" href="http://www.google.com/" title="foo" alt=""'
+ '<area shape="poly" id="node1" href="https://www.google.com/" title="foo" alt=""'
' coords="77,29,76,22,70,15,62,10,52,7,41,5,30,7,20,10,12,15,7,22,5,29,7,37,12,'
'43,20,49,30,52,41,53,52,52,62,49,70,43,76,37"/>\n'
'</map>')
cmap = ClickableMapDefinition('dummy.map', content, code)
assert cmap.filename == 'dummy.map'
- assert cmap.id == 'grapviza4ccdd48ce'
+ assert cmap.id == 'grapvizff087ab863'
assert len(cmap.clickable) == 1
assert cmap.generate_clickable_map() == content.replace('%3', cmap.id)
diff --git a/tests/test_ext_ifconfig.py b/tests/test_extensions/test_ext_ifconfig.py
index 0292699..3e46b1e 100644
--- a/tests/test_ext_ifconfig.py
+++ b/tests/test_extensions/test_ext_ifconfig.py
@@ -9,7 +9,7 @@ from sphinx.testing import restructuredtext
@pytest.mark.sphinx('text', testroot='ext-ifconfig')
def test_ifconfig(app, status, warning):
- app.builder.build_all()
+ app.build(force_all=True)
result = (app.outdir / 'index.txt').read_text(encoding='utf8')
assert 'spam' in result
assert 'ham' not in result
diff --git a/tests/test_ext_imgconverter.py b/tests/test_extensions/test_ext_imgconverter.py
index 18be700..c1d2061 100644
--- a/tests/test_ext_imgconverter.py
+++ b/tests/test_extensions/test_ext_imgconverter.py
@@ -10,7 +10,8 @@ def _if_converter_found(app):
image_converter = getattr(app.config, 'image_converter', '')
try:
if image_converter:
- subprocess.run([image_converter, '-version'], capture_output=True) # show version
+ # print the image_converter version, to check that the command is available
+ subprocess.run([image_converter, '-version'], capture_output=True, check=False)
return
except OSError: # No such file or directory
pass
@@ -21,7 +22,7 @@ def _if_converter_found(app):
@pytest.mark.usefixtures('_if_converter_found')
@pytest.mark.sphinx('latex', testroot='ext-imgconverter')
def test_ext_imgconverter(app, status, warning):
- app.builder.build_all()
+ app.build(force_all=True)
content = (app.outdir / 'python.tex').read_text(encoding='utf8')
diff --git a/tests/test_ext_imgmockconverter.py b/tests/test_extensions/test_ext_imgmockconverter.py
index b5d4e79..4c3c64e 100644
--- a/tests/test_ext_imgmockconverter.py
+++ b/tests/test_extensions/test_ext_imgmockconverter.py
@@ -5,7 +5,7 @@ import pytest
@pytest.mark.sphinx('latex', testroot='ext-imgmockconverter')
def test_ext_imgmockconverter(app, status, warning):
- app.builder.build_all()
+ app.build(force_all=True)
content = (app.outdir / 'python.tex').read_text(encoding='utf8')
diff --git a/tests/test_ext_inheritance_diagram.py b/tests/test_extensions/test_ext_inheritance_diagram.py
index 9ace5ad..c13ccea 100644
--- a/tests/test_ext_inheritance_diagram.py
+++ b/tests/test_extensions/test_ext_inheritance_diagram.py
@@ -33,7 +33,7 @@ def test_inheritance_diagram(app, status, warning):
InheritanceDiagram.run = new_run
try:
- app.builder.build_all()
+ app.build(force_all=True)
finally:
InheritanceDiagram.run = orig_run
@@ -160,7 +160,7 @@ def test_inheritance_diagram_png_html(tmp_path, app):
normalize_intersphinx_mapping(app, app.config)
load_mappings(app)
- app.builder.build_all()
+ app.build(force_all=True)
content = (app.outdir / 'index.html').read_text(encoding='utf8')
base_maps = re.findall('<map .+\n.+\n</map>', content)
@@ -171,7 +171,7 @@ def test_inheritance_diagram_png_html(tmp_path, app):
'class="inheritance graphviz" /></div>\n<figcaption>\n<p>'
'<span class="caption-text">Test Foo!</span><a class="headerlink" href="#id1" '
'title="Link to this image">\xb6</a></p>\n</figcaption>\n</figure>\n')
- assert re.search(pattern, content, re.M)
+ assert re.search(pattern, content, re.MULTILINE)
subdir_content = (app.outdir / 'subdir/page1.html').read_text(encoding='utf8')
subdir_maps = re.findall('<map .+\n.+\n</map>', subdir_content)
@@ -207,7 +207,7 @@ def test_inheritance_diagram_svg_html(tmp_path, app):
normalize_intersphinx_mapping(app, app.config)
load_mappings(app)
- app.builder.build_all()
+ app.build(force_all=True)
content = (app.outdir / 'index.html').read_text(encoding='utf8')
base_svgs = re.findall('<object data="(_images/inheritance-\\w+.svg?)"', content)
@@ -216,12 +216,12 @@ def test_inheritance_diagram_svg_html(tmp_path, app):
'<div class="graphviz">'
'<object data="_images/inheritance-\\w+.svg" '
'type="image/svg\\+xml" class="inheritance graphviz">\n'
- '<p class=\"warning\">Inheritance diagram of test.Foo</p>'
+ '<p class="warning">Inheritance diagram of test.Foo</p>'
'</object></div>\n<figcaption>\n<p><span class="caption-text">'
'Test Foo!</span><a class="headerlink" href="#id1" '
'title="Link to this image">\xb6</a></p>\n</figcaption>\n</figure>\n')
- assert re.search(pattern, content, re.M)
+ assert re.search(pattern, content, re.MULTILINE)
subdir_content = (app.outdir / 'subdir/page1.html').read_text(encoding='utf8')
subdir_svgs = re.findall('<object data="../(_images/inheritance-\\w+.svg?)"', subdir_content)
@@ -249,14 +249,14 @@ def test_inheritance_diagram_svg_html(tmp_path, app):
@pytest.mark.sphinx('latex', testroot='ext-inheritance_diagram')
@pytest.mark.usefixtures('if_graphviz_found')
def test_inheritance_diagram_latex(app, status, warning):
- app.builder.build_all()
+ app.build(force_all=True)
content = (app.outdir / 'python.tex').read_text(encoding='utf8')
pattern = ('\\\\begin{figure}\\[htbp]\n\\\\centering\n\\\\capstart\n\n'
'\\\\sphinxincludegraphics\\[\\]{inheritance-\\w+.pdf}\n'
'\\\\caption{Test Foo!}\\\\label{\\\\detokenize{index:id1}}\\\\end{figure}')
- assert re.search(pattern, content, re.M)
+ assert re.search(pattern, content, re.MULTILINE)
@pytest.mark.sphinx('html', testroot='ext-inheritance_diagram',
@@ -264,7 +264,7 @@ def test_inheritance_diagram_latex(app, status, warning):
@pytest.mark.usefixtures('if_graphviz_found')
def test_inheritance_diagram_latex_alias(app, status, warning):
app.config.inheritance_alias = {'test.Foo': 'alias.Foo'}
- app.builder.build_all()
+ app.build(force_all=True)
doc = app.env.get_and_resolve_doctree('index', app)
aliased_graph = doc.children[0].children[3]['graph'].class_info
@@ -282,7 +282,7 @@ def test_inheritance_diagram_latex_alias(app, status, warning):
'class="inheritance graphviz" /></div>\n<figcaption>\n<p>'
'<span class="caption-text">Test Foo!</span><a class="headerlink" href="#id1" '
'title="Link to this image">\xb6</a></p>\n</figcaption>\n</figure>\n')
- assert re.search(pattern, content, re.M)
+ assert re.search(pattern, content, re.MULTILINE)
def test_import_classes(rootdir):
diff --git a/tests/test_ext_intersphinx.py b/tests/test_extensions/test_ext_intersphinx.py
index 82bec9e..ef5a9b1 100644
--- a/tests/test_ext_intersphinx.py
+++ b/tests/test_extensions/test_ext_intersphinx.py
@@ -18,9 +18,10 @@ from sphinx.ext.intersphinx import (
normalize_intersphinx_mapping,
)
from sphinx.ext.intersphinx import setup as intersphinx_setup
+from sphinx.util.console import strip_colors
-from .test_util_inventory import inventory_v2, inventory_v2_not_having_version
-from .utils import http_server
+from tests.test_util.intersphinx_data import INVENTORY_V2, INVENTORY_V2_NO_VERSION
+from tests.utils import http_server
def fake_node(domain, type, target, content, **attrs):
@@ -52,46 +53,46 @@ def test_fetch_inventory_redirection(_read_from_url, InventoryFile, app, status,
_read_from_url().readline.return_value = b'# Sphinx inventory version 2'
# same uri and inv, not redirected
- _read_from_url().url = 'http://hostname/' + INVENTORY_FILENAME
- fetch_inventory(app, 'http://hostname/', 'http://hostname/' + INVENTORY_FILENAME)
+ _read_from_url().url = 'https://hostname/' + INVENTORY_FILENAME
+ fetch_inventory(app, 'https://hostname/', 'https://hostname/' + INVENTORY_FILENAME)
assert 'intersphinx inventory has moved' not in status.getvalue()
- assert InventoryFile.load.call_args[0][1] == 'http://hostname/'
+ assert InventoryFile.load.call_args[0][1] == 'https://hostname/'
# same uri and inv, redirected
status.seek(0)
status.truncate(0)
- _read_from_url().url = 'http://hostname/new/' + INVENTORY_FILENAME
+ _read_from_url().url = 'https://hostname/new/' + INVENTORY_FILENAME
- fetch_inventory(app, 'http://hostname/', 'http://hostname/' + INVENTORY_FILENAME)
+ fetch_inventory(app, 'https://hostname/', 'https://hostname/' + INVENTORY_FILENAME)
assert status.getvalue() == ('intersphinx inventory has moved: '
- 'http://hostname/%s -> http://hostname/new/%s\n' %
+ 'https://hostname/%s -> https://hostname/new/%s\n' %
(INVENTORY_FILENAME, INVENTORY_FILENAME))
- assert InventoryFile.load.call_args[0][1] == 'http://hostname/new'
+ assert InventoryFile.load.call_args[0][1] == 'https://hostname/new'
# different uri and inv, not redirected
status.seek(0)
status.truncate(0)
- _read_from_url().url = 'http://hostname/new/' + INVENTORY_FILENAME
+ _read_from_url().url = 'https://hostname/new/' + INVENTORY_FILENAME
- fetch_inventory(app, 'http://hostname/', 'http://hostname/new/' + INVENTORY_FILENAME)
+ fetch_inventory(app, 'https://hostname/', 'https://hostname/new/' + INVENTORY_FILENAME)
assert 'intersphinx inventory has moved' not in status.getvalue()
- assert InventoryFile.load.call_args[0][1] == 'http://hostname/'
+ assert InventoryFile.load.call_args[0][1] == 'https://hostname/'
# different uri and inv, redirected
status.seek(0)
status.truncate(0)
- _read_from_url().url = 'http://hostname/other/' + INVENTORY_FILENAME
+ _read_from_url().url = 'https://hostname/other/' + INVENTORY_FILENAME
- fetch_inventory(app, 'http://hostname/', 'http://hostname/new/' + INVENTORY_FILENAME)
+ fetch_inventory(app, 'https://hostname/', 'https://hostname/new/' + INVENTORY_FILENAME)
assert status.getvalue() == ('intersphinx inventory has moved: '
- 'http://hostname/new/%s -> http://hostname/other/%s\n' %
+ 'https://hostname/new/%s -> https://hostname/other/%s\n' %
(INVENTORY_FILENAME, INVENTORY_FILENAME))
- assert InventoryFile.load.call_args[0][1] == 'http://hostname/'
+ assert InventoryFile.load.call_args[0][1] == 'https://hostname/'
def test_missing_reference(tmp_path, app, status, warning):
inv_file = tmp_path / 'inventory'
- inv_file.write_bytes(inventory_v2)
+ inv_file.write_bytes(INVENTORY_V2)
set_config(app, {
'https://docs.python.org/': str(inv_file),
'py3k': ('https://docs.python.org/py3k/', str(inv_file)),
@@ -169,7 +170,7 @@ def test_missing_reference(tmp_path, app, status, warning):
def test_missing_reference_pydomain(tmp_path, app, status, warning):
inv_file = tmp_path / 'inventory'
- inv_file.write_bytes(inventory_v2)
+ inv_file.write_bytes(INVENTORY_V2)
set_config(app, {
'https://docs.python.org/': str(inv_file),
})
@@ -196,20 +197,10 @@ def test_missing_reference_pydomain(tmp_path, app, status, warning):
rn = missing_reference(app, app.env, node, contnode)
assert rn.astext() == 'Foo.bar'
- # term reference (normal)
- node, contnode = fake_node('std', 'term', 'a term', 'a term')
- rn = missing_reference(app, app.env, node, contnode)
- assert rn.astext() == 'a term'
-
- # term reference (case insensitive)
- node, contnode = fake_node('std', 'term', 'A TERM', 'A TERM')
- rn = missing_reference(app, app.env, node, contnode)
- assert rn.astext() == 'A TERM'
-
def test_missing_reference_stddomain(tmp_path, app, status, warning):
inv_file = tmp_path / 'inventory'
- inv_file.write_bytes(inventory_v2)
+ inv_file.write_bytes(INVENTORY_V2)
set_config(app, {
'cmd': ('https://docs.python.org/', str(inv_file)),
})
@@ -236,11 +227,31 @@ def test_missing_reference_stddomain(tmp_path, app, status, warning):
rn = missing_reference(app, app.env, node, contnode)
assert rn.astext() == '-l'
+ # term reference (normal)
+ node, contnode = fake_node('std', 'term', 'a term', 'a term')
+ rn = missing_reference(app, app.env, node, contnode)
+ assert rn.astext() == 'a term'
+
+ # term reference (case insensitive)
+ node, contnode = fake_node('std', 'term', 'A TERM', 'A TERM')
+ rn = missing_reference(app, app.env, node, contnode)
+ assert rn.astext() == 'A TERM'
+
+ # label reference (normal)
+ node, contnode = fake_node('std', 'ref', 'The-Julia-Domain', 'The-Julia-Domain')
+ rn = missing_reference(app, app.env, node, contnode)
+ assert rn.astext() == 'The Julia Domain'
+
+ # label reference (case insensitive)
+ node, contnode = fake_node('std', 'ref', 'the-julia-domain', 'the-julia-domain')
+ rn = missing_reference(app, app.env, node, contnode)
+ assert rn.astext() == 'The Julia Domain'
+
@pytest.mark.sphinx('html', testroot='ext-intersphinx-cppdomain')
def test_missing_reference_cppdomain(tmp_path, app, status, warning):
inv_file = tmp_path / 'inventory'
- inv_file.write_bytes(inventory_v2)
+ inv_file.write_bytes(INVENTORY_V2)
set_config(app, {
'https://docs.python.org/': str(inv_file),
})
@@ -266,7 +277,7 @@ def test_missing_reference_cppdomain(tmp_path, app, status, warning):
def test_missing_reference_jsdomain(tmp_path, app, status, warning):
inv_file = tmp_path / 'inventory'
- inv_file.write_bytes(inventory_v2)
+ inv_file.write_bytes(INVENTORY_V2)
set_config(app, {
'https://docs.python.org/': str(inv_file),
})
@@ -290,7 +301,7 @@ def test_missing_reference_jsdomain(tmp_path, app, status, warning):
def test_missing_reference_disabled_domain(tmp_path, app, status, warning):
inv_file = tmp_path / 'inventory'
- inv_file.write_bytes(inventory_v2)
+ inv_file.write_bytes(INVENTORY_V2)
set_config(app, {
'inv': ('https://docs.python.org/', str(inv_file)),
})
@@ -352,7 +363,7 @@ def test_missing_reference_disabled_domain(tmp_path, app, status, warning):
def test_inventory_not_having_version(tmp_path, app, status, warning):
inv_file = tmp_path / 'inventory'
- inv_file.write_bytes(inventory_v2_not_having_version)
+ inv_file.write_bytes(INVENTORY_V2_NO_VERSION)
set_config(app, {
'https://docs.python.org/': str(inv_file),
})
@@ -374,14 +385,14 @@ def test_load_mappings_warnings(tmp_path, app, status, warning):
identifiers are not string
"""
inv_file = tmp_path / 'inventory'
- inv_file.write_bytes(inventory_v2)
+ inv_file.write_bytes(INVENTORY_V2)
set_config(app, {
'https://docs.python.org/': str(inv_file),
'py3k': ('https://docs.python.org/py3k/', str(inv_file)),
- 'repoze.workflow': ('http://docs.repoze.org/workflow/', str(inv_file)),
- 'django-taggit': ('http://django-taggit.readthedocs.org/en/latest/',
+ 'repoze.workflow': ('https://docs.repoze.org/workflow/', str(inv_file)),
+ 'django-taggit': ('https://django-taggit.readthedocs.org/en/latest/',
str(inv_file)),
- 12345: ('http://www.sphinx-doc.org/en/stable/', str(inv_file)),
+ 12345: ('https://www.sphinx-doc.org/en/stable/', str(inv_file)),
})
# load the inventory and check if it's done correctly
@@ -395,7 +406,7 @@ def test_load_mappings_warnings(tmp_path, app, status, warning):
def test_load_mappings_fallback(tmp_path, app, status, warning):
inv_file = tmp_path / 'inventory'
- inv_file.write_bytes(inventory_v2)
+ inv_file.write_bytes(INVENTORY_V2)
set_config(app, {})
# connect to invalid path
@@ -429,23 +440,25 @@ def test_load_mappings_fallback(tmp_path, app, status, warning):
class TestStripBasicAuth:
"""Tests for sphinx.ext.intersphinx._strip_basic_auth()"""
+
def test_auth_stripped(self):
- """basic auth creds stripped from URL containing creds"""
+ """Basic auth creds stripped from URL containing creds"""
url = 'https://user:12345@domain.com/project/objects.inv'
expected = 'https://domain.com/project/objects.inv'
actual = _strip_basic_auth(url)
assert expected == actual
def test_no_auth(self):
- """url unchanged if param doesn't contain basic auth creds"""
+ """Url unchanged if param doesn't contain basic auth creds"""
url = 'https://domain.com/project/objects.inv'
expected = 'https://domain.com/project/objects.inv'
actual = _strip_basic_auth(url)
assert expected == actual
def test_having_port(self):
- """basic auth creds correctly stripped from URL containing creds even if URL
- contains port"""
+ """Basic auth creds correctly stripped from URL containing creds even if URL
+ contains port
+ """
url = 'https://user:12345@domain.com:8080/project/objects.inv'
expected = 'https://domain.com:8080/project/objects.inv'
actual = _strip_basic_auth(url)
@@ -492,7 +505,7 @@ def test_inspect_main_noargs(capsys):
def test_inspect_main_file(capsys, tmp_path):
"""inspect_main interface, with file argument"""
inv_file = tmp_path / 'inventory'
- inv_file.write_bytes(inventory_v2)
+ inv_file.write_bytes(INVENTORY_V2)
inspect_main([str(inv_file)])
@@ -507,15 +520,14 @@ def test_inspect_main_url(capsys):
def do_GET(self):
self.send_response(200, "OK")
self.end_headers()
- self.wfile.write(inventory_v2)
+ self.wfile.write(INVENTORY_V2)
def log_message(*args, **kwargs):
# Silenced.
pass
- url = 'http://localhost:7777/' + INVENTORY_FILENAME
-
- with http_server(InventoryHandler):
+ with http_server(InventoryHandler) as server:
+ url = f'http://localhost:{server.server_port}/{INVENTORY_FILENAME}'
inspect_main([url])
stdout, stderr = capsys.readouterr()
@@ -526,9 +538,9 @@ def test_inspect_main_url(capsys):
@pytest.mark.sphinx('html', testroot='ext-intersphinx-role')
def test_intersphinx_role(app, warning):
inv_file = app.srcdir / 'inventory'
- inv_file.write_bytes(inventory_v2)
+ inv_file.write_bytes(INVENTORY_V2)
app.config.intersphinx_mapping = {
- 'inv': ('http://example.org/', str(inv_file)),
+ 'inv': ('https://example.org/', str(inv_file)),
}
app.config.intersphinx_cache_limit = 0
app.config.nitpicky = True
@@ -539,22 +551,27 @@ def test_intersphinx_role(app, warning):
app.build()
content = (app.outdir / 'index.html').read_text(encoding='utf8')
- wStr = warning.getvalue()
-
- html = '<a class="reference external" href="http://example.org/{}" title="(in foo v2.0)">'
+ warnings = strip_colors(warning.getvalue()).splitlines()
+ index_path = app.srcdir / 'index.rst'
+ assert warnings == [
+ f"{index_path}:21: WARNING: role for external cross-reference not found in domain 'py': 'nope'",
+ f"{index_path}:28: WARNING: role for external cross-reference not found in domains 'cpp', 'std': 'nope'",
+ f"{index_path}:39: WARNING: inventory for external cross-reference not found: 'invNope'",
+ f"{index_path}:44: WARNING: role for external cross-reference not found in domain 'c': 'function' (perhaps you meant one of: 'func', 'identifier', 'type')",
+ f"{index_path}:45: WARNING: role for external cross-reference not found in domains 'cpp', 'std': 'function' (perhaps you meant one of: 'cpp:func', 'cpp:identifier', 'cpp:type')",
+ f'{index_path}:9: WARNING: external py:mod reference target not found: module3',
+ f'{index_path}:14: WARNING: external py:mod reference target not found: module10',
+ f'{index_path}:19: WARNING: external py:meth reference target not found: inv:Foo.bar',
+ ]
+
+ html = '<a class="reference external" href="https://example.org/{}" title="(in foo v2.0)">'
assert html.format('foo.html#module-module1') in content
assert html.format('foo.html#module-module2') in content
- assert "WARNING: external py:mod reference target not found: module3" in wStr
- assert "WARNING: external py:mod reference target not found: module10" in wStr
assert html.format('sub/foo.html#module1.func') in content
- assert "WARNING: external py:meth reference target not found: inv:Foo.bar" in wStr
-
- assert "WARNING: role for external cross-reference not found: py:nope" in wStr
# default domain
assert html.format('index.html#std_uint8_t') in content
- assert "WARNING: role for external cross-reference not found: nope" in wStr
# std roles without domain prefix
assert html.format('docname.html') in content
@@ -562,7 +579,6 @@ def test_intersphinx_role(app, warning):
# explicit inventory
assert html.format('cfunc.html#CFunc') in content
- assert "WARNING: inventory for external cross-reference not found: invNope" in wStr
# explicit title
assert html.format('index.html#foons') in content
diff --git a/tests/test_ext_math.py b/tests/test_extensions/test_ext_math.py
index d5331f8..b673f83 100644
--- a/tests/test_ext_math.py
+++ b/tests/test_extensions/test_ext_math.py
@@ -25,9 +25,9 @@ def has_binary(binary):
@pytest.mark.skipif(not has_binary('dvipng'),
reason='Requires dvipng" binary')
@pytest.mark.sphinx('html', testroot='ext-math-simple',
- confoverrides = {'extensions': ['sphinx.ext.imgmath']})
+ confoverrides={'extensions': ['sphinx.ext.imgmath']})
def test_imgmath_png(app, status, warning):
- app.builder.build_all()
+ app.build(force_all=True)
if "LaTeX command 'latex' cannot be run" in warning.getvalue():
msg = 'LaTeX command "latex" is not available'
raise pytest.skip.Exception(msg)
@@ -39,7 +39,7 @@ def test_imgmath_png(app, status, warning):
shutil.rmtree(app.outdir)
html = (r'<div class="math">\s*<p>\s*<img src="_images/math/\w+.png"'
r'\s*alt="a\^2\+b\^2=c\^2"/>\s*</p>\s*</div>')
- assert re.search(html, content, re.S)
+ assert re.search(html, content, re.DOTALL)
@pytest.mark.skipif(not has_binary('dvisvgm'),
@@ -48,7 +48,7 @@ def test_imgmath_png(app, status, warning):
confoverrides={'extensions': ['sphinx.ext.imgmath'],
'imgmath_image_format': 'svg'})
def test_imgmath_svg(app, status, warning):
- app.builder.build_all()
+ app.build(force_all=True)
if "LaTeX command 'latex' cannot be run" in warning.getvalue():
msg = 'LaTeX command "latex" is not available'
raise pytest.skip.Exception(msg)
@@ -60,7 +60,7 @@ def test_imgmath_svg(app, status, warning):
shutil.rmtree(app.outdir)
html = (r'<div class="math">\s*<p>\s*<img src="_images/math/\w+.svg"'
r'\s*alt="a\^2\+b\^2=c\^2"/>\s*</p>\s*</div>')
- assert re.search(html, content, re.S)
+ assert re.search(html, content, re.DOTALL)
@pytest.mark.skipif(not has_binary('dvisvgm'),
@@ -70,7 +70,7 @@ def test_imgmath_svg(app, status, warning):
'imgmath_image_format': 'svg',
'imgmath_embed': True})
def test_imgmath_svg_embed(app, status, warning):
- app.builder.build_all()
+ app.build(force_all=True)
if "LaTeX command 'latex' cannot be run" in warning.getvalue():
msg = 'LaTeX command "latex" is not available'
raise pytest.skip.Exception(msg)
@@ -88,7 +88,7 @@ def test_imgmath_svg_embed(app, status, warning):
confoverrides={'extensions': ['sphinx.ext.mathjax'],
'mathjax_options': {'integrity': 'sha384-0123456789'}})
def test_mathjax_options(app, status, warning):
- app.builder.build_all()
+ app.build(force_all=True)
content = (app.outdir / 'index.html').read_text(encoding='utf8')
shutil.rmtree(app.outdir)
@@ -100,14 +100,14 @@ def test_mathjax_options(app, status, warning):
@pytest.mark.sphinx('html', testroot='ext-math',
confoverrides={'extensions': ['sphinx.ext.mathjax']})
def test_mathjax_align(app, status, warning):
- app.builder.build_all()
+ app.build(force_all=True)
content = (app.outdir / 'index.html').read_text(encoding='utf8')
shutil.rmtree(app.outdir)
html = (r'<div class="math notranslate nohighlight">\s*'
r'\\\[ \\begin\{align\}\\begin\{aligned\}S \&amp;= \\pi r\^2\\\\'
r'V \&amp;= \\frac\{4\}\{3\} \\pi r\^3\\end\{aligned\}\\end\{align\} \\\]</div>')
- assert re.search(html, content, re.S)
+ assert re.search(html, content, re.DOTALL)
@pytest.mark.sphinx('html', testroot='ext-math',
@@ -119,7 +119,7 @@ def test_math_number_all_mathjax(app, status, warning):
content = (app.outdir / 'index.html').read_text(encoding='utf8')
html = (r'<div class="math notranslate nohighlight" id="equation-index-0">\s*'
r'<span class="eqno">\(1\)<a .*>\xb6</a></span>\\\[a\^2\+b\^2=c\^2\\\]</div>')
- assert re.search(html, content, re.S)
+ assert re.search(html, content, re.DOTALL)
@pytest.mark.sphinx('latex', testroot='ext-math',
@@ -131,31 +131,31 @@ def test_math_number_all_latex(app, status, warning):
macro = (r'\\begin{equation\*}\s*'
r'\\begin{split}a\^2\+b\^2=c\^2\\end{split}\s*'
r'\\end{equation\*}')
- assert re.search(macro, content, re.S)
+ assert re.search(macro, content, re.DOTALL)
macro = r'Inline \\\(E=mc\^2\\\)'
- assert re.search(macro, content, re.S)
+ assert re.search(macro, content, re.DOTALL)
macro = (r'\\begin{equation\*}\s*'
r'\\begin{split}e\^{i\\pi}\+1=0\\end{split}\s+'
r'\\end{equation\*}')
- assert re.search(macro, content, re.S)
+ assert re.search(macro, content, re.DOTALL)
macro = (r'\\begin{align\*}\\!\\begin{aligned}\s*'
r'S &= \\pi r\^2\\\\\s*'
r'V &= \\frac\{4}\{3} \\pi r\^3\\\\\s*'
r'\\end{aligned}\\end{align\*}')
- assert re.search(macro, content, re.S)
+ assert re.search(macro, content, re.DOTALL)
macro = r'Referencing equation \\eqref{equation:math:foo}.'
- assert re.search(macro, content, re.S)
+ assert re.search(macro, content, re.DOTALL)
@pytest.mark.sphinx('html', testroot='ext-math',
confoverrides={'extensions': ['sphinx.ext.mathjax'],
'math_eqref_format': 'Eq.{number}'})
def test_math_eqref_format_html(app, status, warning):
- app.builder.build_all()
+ app.build(force_all=True)
content = (app.outdir / 'math.html').read_text(encoding='utf8')
html = ('<p>Referencing equation <a class="reference internal" '
@@ -168,12 +168,12 @@ def test_math_eqref_format_html(app, status, warning):
confoverrides={'extensions': ['sphinx.ext.mathjax'],
'math_eqref_format': 'Eq.{number}'})
def test_math_eqref_format_latex(app, status, warning):
- app.builder.build_all()
+ app.build(force_all=True)
content = (app.outdir / 'python.tex').read_text(encoding='utf8')
macro = (r'Referencing equation Eq.\\ref{equation:math:foo} and '
r'Eq.\\ref{equation:math:foo}.')
- assert re.search(macro, content, re.S)
+ assert re.search(macro, content, re.DOTALL)
@pytest.mark.sphinx('html', testroot='ext-math',
@@ -181,7 +181,7 @@ def test_math_eqref_format_latex(app, status, warning):
'numfig': True,
'math_numfig': True})
def test_mathjax_numfig_html(app, status, warning):
- app.builder.build_all()
+ app.build(force_all=True)
content = (app.outdir / 'math.html').read_text(encoding='utf8')
html = ('<div class="math notranslate nohighlight" id="equation-math-0">\n'
@@ -199,7 +199,7 @@ def test_mathjax_numfig_html(app, status, warning):
'numfig_secnum_depth': 0,
'math_numfig': True})
def test_imgmath_numfig_html(app, status, warning):
- app.builder.build_all()
+ app.build(force_all=True)
content = (app.outdir / 'page.html').read_text(encoding='utf8')
html = '<span class="eqno">(3)<a class="headerlink" href="#equation-bar"'
@@ -213,7 +213,7 @@ def test_imgmath_numfig_html(app, status, warning):
@pytest.mark.sphinx('dummy', testroot='ext-math-compat')
def test_math_compat(app, status, warning):
with warnings.catch_warnings(record=True):
- app.builder.build_all()
+ app.build(force_all=True)
doctree = app.env.get_and_resolve_doctree('index', app.builder)
assert_node(doctree,
@@ -239,7 +239,7 @@ def test_math_compat(app, status, warning):
confoverrides={'extensions': ['sphinx.ext.mathjax'],
'mathjax3_config': {'extensions': ['tex2jax.js']}})
def test_mathjax3_config(app, status, warning):
- app.builder.build_all()
+ app.build(force_all=True)
content = (app.outdir / 'index.html').read_text(encoding='utf8')
assert MATHJAX_URL in content
@@ -251,7 +251,7 @@ def test_mathjax3_config(app, status, warning):
confoverrides={'extensions': ['sphinx.ext.mathjax'],
'mathjax2_config': {'extensions': ['tex2jax.js']}})
def test_mathjax2_config(app, status, warning):
- app.builder.build_all()
+ app.build(force_all=True)
content = (app.outdir / 'index.html').read_text(encoding='utf8')
assert ('<script async="async" src="%s">' % MATHJAX_URL in content)
@@ -265,7 +265,7 @@ def test_mathjax2_config(app, status, warning):
'mathjax_options': {'async': 'async'},
'mathjax3_config': {'extensions': ['tex2jax.js']}})
def test_mathjax_options_async_for_mathjax3(app, status, warning):
- app.builder.build_all()
+ app.build(force_all=True)
content = (app.outdir / 'index.html').read_text(encoding='utf8')
assert MATHJAX_URL in content
@@ -277,7 +277,7 @@ def test_mathjax_options_async_for_mathjax3(app, status, warning):
'mathjax_options': {'defer': 'defer'},
'mathjax2_config': {'extensions': ['tex2jax.js']}})
def test_mathjax_options_defer_for_mathjax2(app, status, warning):
- app.builder.build_all()
+ app.build(force_all=True)
content = (app.outdir / 'index.html').read_text(encoding='utf8')
assert ('<script defer="defer" src="%s">' % MATHJAX_URL in content)
@@ -291,7 +291,7 @@ def test_mathjax_options_defer_for_mathjax2(app, status, warning):
},
)
def test_mathjax_path(app):
- app.builder.build_all()
+ app.build(force_all=True)
content = (app.outdir / 'index.html').read_text(encoding='utf8')
assert '<script async="async" src="_static/MathJax.js"></script>' in content
@@ -305,7 +305,7 @@ def test_mathjax_path(app):
},
)
def test_mathjax_path_config(app):
- app.builder.build_all()
+ app.build(force_all=True)
content = (app.outdir / 'index.html').read_text(encoding='utf8')
assert '<script async="async" src="_static/MathJax.js?config=scipy-mathjax"></script>' in content
@@ -314,7 +314,7 @@ def test_mathjax_path_config(app):
@pytest.mark.sphinx('html', testroot='ext-math',
confoverrides={'extensions': ['sphinx.ext.mathjax']})
def test_mathjax_is_installed_only_if_document_having_math(app, status, warning):
- app.builder.build_all()
+ app.build(force_all=True)
content = (app.outdir / 'index.html').read_text(encoding='utf8')
assert MATHJAX_URL in content
@@ -326,7 +326,7 @@ def test_mathjax_is_installed_only_if_document_having_math(app, status, warning)
@pytest.mark.sphinx('html', testroot='basic',
confoverrides={'extensions': ['sphinx.ext.mathjax']})
def test_mathjax_is_not_installed_if_no_equations(app, status, warning):
- app.builder.build_all()
+ app.build(force_all=True)
content = (app.outdir / 'index.html').read_text(encoding='utf8')
assert 'MathJax.js' not in content
@@ -336,10 +336,54 @@ def test_mathjax_is_not_installed_if_no_equations(app, status, warning):
confoverrides={'extensions': ['sphinx.ext.mathjax']})
def test_mathjax_is_installed_if_no_equations_when_forced(app, status, warning):
app.set_html_assets_policy('always')
- app.builder.build_all()
+ app.build(force_all=True)
content = (app.outdir / 'index.html').read_text(encoding='utf8')
assert MATHJAX_URL in content
content = (app.outdir / 'nomath.html').read_text(encoding='utf8')
assert MATHJAX_URL in content
+
+
+@pytest.mark.sphinx('html', testroot='ext-math-include',
+ confoverrides={'extensions': ['sphinx.ext.mathjax']})
+def test_mathjax_is_installed_if_included_file_has_equations(app):
+ app.build(force_all=True)
+
+ # no real equations at the rst level, but includes "included"
+ content = (app.outdir / 'index.html').read_text(encoding='utf8')
+ assert MATHJAX_URL in content
+
+ # no real equations at the rst level, but includes "math.rst"
+ content = (app.outdir / 'included.html').read_text(encoding='utf8')
+ assert MATHJAX_URL in content
+
+ content = (app.outdir / 'math.html').read_text(encoding='utf8')
+ assert MATHJAX_URL in content
+
+
+@pytest.mark.sphinx('singlehtml', testroot='ext-math',
+ confoverrides={'extensions': ['sphinx.ext.mathjax']})
+def test_mathjax_is_installed_only_if_document_having_math_singlehtml(app):
+ app.build(force_all=True)
+
+ content = (app.outdir / 'index.html').read_text(encoding='utf8')
+ assert MATHJAX_URL in content
+
+
+@pytest.mark.sphinx('singlehtml', testroot='basic',
+ confoverrides={'extensions': ['sphinx.ext.mathjax']})
+def test_mathjax_is_not_installed_if_no_equations_singlehtml(app):
+ app.build(force_all=True)
+
+ content = (app.outdir / 'index.html').read_text(encoding='utf8')
+ assert 'MathJax.js' not in content
+
+
+@pytest.mark.sphinx('singlehtml', testroot='ext-math-include',
+ confoverrides={'extensions': ['sphinx.ext.mathjax']})
+def test_mathjax_is_installed_if_included_file_has_equations_singlehtml(app):
+ app.build(force_all=True)
+
+ content = (app.outdir / 'index.html').read_text(encoding='utf8')
+ assert MATHJAX_URL in content
diff --git a/tests/test_ext_napoleon.py b/tests/test_extensions/test_ext_napoleon.py
index 00b7ac1..466bd49 100644
--- a/tests/test_ext_napoleon.py
+++ b/tests/test_extensions/test_ext_napoleon.py
@@ -55,7 +55,7 @@ class SampleClass:
@simple_decorator
def __decorated_func__(self):
- """doc"""
+ """Doc"""
pass
diff --git a/tests/test_ext_napoleon_docstring.py b/tests/test_extensions/test_ext_napoleon_docstring.py
index 87fad61..d7ef489 100644
--- a/tests/test_ext_napoleon_docstring.py
+++ b/tests/test_extensions/test_ext_napoleon_docstring.py
@@ -1,13 +1,16 @@
"""Tests for :mod:`sphinx.ext.napoleon.docstring` module."""
import re
+import zlib
from collections import namedtuple
from inspect import cleandoc
+from itertools import product
from textwrap import dedent
from unittest import mock
import pytest
+from sphinx.ext.intersphinx import load_mappings, normalize_intersphinx_mapping
from sphinx.ext.napoleon import Config
from sphinx.ext.napoleon.docstring import (
GoogleDocstring,
@@ -17,9 +20,10 @@ from sphinx.ext.napoleon.docstring import (
_token_type,
_tokenize_type_spec,
)
+from sphinx.testing.util import etree_parse
-from .ext_napoleon_pep526_data_google import PEP526GoogleClass
-from .ext_napoleon_pep526_data_numpy import PEP526NumpyClass
+from tests.test_extensions.ext_napoleon_pep526_data_google import PEP526GoogleClass
+from tests.test_extensions.ext_napoleon_pep526_data_numpy import PEP526NumpyClass
class NamedtupleSubclass(namedtuple('NamedtupleSubclass', ('attr1', 'attr2'))):
@@ -36,6 +40,7 @@ class NamedtupleSubclass(namedtuple('NamedtupleSubclass', ('attr1', 'attr2'))):
Adds a newline after the type
"""
+
# To avoid creating a dict, as a namedtuple doesn't have it:
__slots__ = ()
@@ -1156,7 +1161,7 @@ Methods:
description
-""" # noqa: W293
+""" # NoQA: W293
config = Config()
actual = str(GoogleDocstring(docstring, config=config, app=None, what='module',
options={'no-index': True}))
@@ -1186,7 +1191,7 @@ Do as you please
actual = str(GoogleDocstring(cleandoc(PEP526GoogleClass.__doc__), config, app=None, what="class",
obj=PEP526GoogleClass))
expected = """\
-Sample class with PEP 526 annotations and google docstring
+Sample class with PEP 526 annotations and google docstring.
.. attribute:: attr1
@@ -2658,3 +2663,41 @@ def test_napoleon_and_autodoc_typehints_description_documented_params(app, statu
'\n'
' * ****kwargs** (*int*) -- Extra arguments.\n'
)
+
+
+@pytest.mark.sphinx('html', testroot='ext-napoleon-paramtype', freshenv=True)
+def test_napoleon_keyword_and_paramtype(app, tmp_path):
+ inv_file = tmp_path / 'objects.inv'
+ inv_file.write_bytes(b'''\
+# Sphinx inventory version 2
+# Project: Intersphinx Test
+# Version: 42
+# The remainder of this file is compressed using zlib.
+''' + zlib.compress(b'''\
+None py:data 1 none.html -
+list py:class 1 list.html -
+int py:class 1 int.html -
+''')) # NoQA: W291
+ app.config.intersphinx_mapping = {'python': ('127.0.0.1:5555', str(inv_file))}
+ normalize_intersphinx_mapping(app, app.config)
+ load_mappings(app)
+
+ app.build(force_all=True)
+
+ etree = etree_parse(app.outdir / 'index.html')
+
+ for name, typename in product(('keyword', 'kwarg', 'kwparam'), ('paramtype', 'kwtype')):
+ param = f'{name}_{typename}'
+ li_ = list(etree.findall(f'.//li/p/strong[.="{param}"]/../..'))
+ assert len(li_) == 1
+ li = li_[0]
+
+ text = li.text or ''.join(li.itertext())
+ assert text == f'{param} (list[int]) \u2013 some param'
+
+ a_ = list(li.findall('.//a[@class="reference external"]'))
+
+ assert len(a_) == 2
+ for a, uri in zip(a_, ('list.html', 'int.html')):
+ assert a.attrib['href'] == f'127.0.0.1:5555/{uri}'
+ assert a.attrib['title'] == '(in Intersphinx Test v42)'
diff --git a/tests/test_ext_todo.py b/tests/test_extensions/test_ext_todo.py
index 7d39495..1903f9f 100644
--- a/tests/test_ext_todo.py
+++ b/tests/test_extensions/test_ext_todo.py
@@ -14,7 +14,7 @@ def test_todo(app, status, warning):
todos.append(node)
app.connect('todo-defined', on_todo_defined)
- app.builder.build_all()
+ app.build(force_all=True)
# check todolist
content = (app.outdir / 'index.html').read_text(encoding='utf8')
@@ -52,7 +52,7 @@ def test_todo_not_included(app, status, warning):
todos.append(node)
app.connect('todo-defined', on_todo_defined)
- app.builder.build_all()
+ app.build(force_all=True)
# check todolist
content = (app.outdir / 'index.html').read_text(encoding='utf8')
@@ -86,9 +86,8 @@ def test_todo_valid_link(app, status, warning):
that exists in the LaTeX output. The target was previously incorrectly
omitted (GitHub issue #1020).
"""
-
# Ensure the LaTeX output is built.
- app.builder.build_all()
+ app.build(force_all=True)
content = (app.outdir / 'python.tex').read_text(encoding='utf8')
diff --git a/tests/test_ext_viewcode.py b/tests/test_extensions/test_ext_viewcode.py
index a1a0a6d..b2c6fc0 100644
--- a/tests/test_ext_viewcode.py
+++ b/tests/test_extensions/test_ext_viewcode.py
@@ -44,7 +44,7 @@ def check_viewcode_output(app, warning):
confoverrides={"viewcode_line_numbers": True})
def test_viewcode_linenos(app, warning):
shutil.rmtree(app.outdir / '_modules', ignore_errors=True)
- app.builder.build_all()
+ app.build(force_all=True)
result = check_viewcode_output(app, warning)
assert '<span class="linenos"> 1</span>' in result
@@ -54,7 +54,7 @@ def test_viewcode_linenos(app, warning):
confoverrides={"viewcode_line_numbers": False})
def test_viewcode(app, warning):
shutil.rmtree(app.outdir / '_modules', ignore_errors=True)
- app.builder.build_all()
+ app.build(force_all=True)
result = check_viewcode_output(app, warning)
assert 'class="linenos">' not in result
@@ -63,7 +63,7 @@ def test_viewcode(app, warning):
@pytest.mark.sphinx('epub', testroot='ext-viewcode')
def test_viewcode_epub_default(app, status, warning):
shutil.rmtree(app.outdir)
- app.builder.build_all()
+ app.build(force_all=True)
assert not (app.outdir / '_modules/spam/mod1.xhtml').exists()
@@ -74,7 +74,7 @@ def test_viewcode_epub_default(app, status, warning):
@pytest.mark.sphinx('epub', testroot='ext-viewcode',
confoverrides={'viewcode_enable_epub': True})
def test_viewcode_epub_enabled(app, status, warning):
- app.builder.build_all()
+ app.build(force_all=True)
assert (app.outdir / '_modules/spam/mod1.xhtml').exists()
@@ -84,14 +84,14 @@ def test_viewcode_epub_enabled(app, status, warning):
@pytest.mark.sphinx(testroot='ext-viewcode', tags=['test_linkcode'])
def test_linkcode(app, status, warning):
- app.builder.build(['objects'])
+ app.build(filenames=[app.srcdir / 'objects.rst'])
stuff = (app.outdir / 'objects.html').read_text(encoding='utf8')
- assert 'http://foobar/source/foolib.py' in stuff
- assert 'http://foobar/js/' in stuff
- assert 'http://foobar/c/' in stuff
- assert 'http://foobar/cpp/' in stuff
+ assert 'https://foobar/source/foolib.py' in stuff
+ assert 'https://foobar/js/' in stuff
+ assert 'https://foobar/c/' in stuff
+ assert 'https://foobar/cpp/' in stuff
@pytest.mark.sphinx(testroot='ext-viewcode-find', freshenv=True)
@@ -117,7 +117,7 @@ def test_local_source_files(app, status, warning):
return (source, tags)
app.connect('viewcode-find-source', find_source)
- app.builder.build_all()
+ app.build(force_all=True)
warnings = re.sub(r'\\+', '/', warning.getvalue())
assert re.findall(
diff --git a/tests/test_extension.py b/tests/test_extensions/test_extension.py
index d74743c..d74743c 100644
--- a/tests/test_extension.py
+++ b/tests/test_extensions/test_extension.py