diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-06-05 16:20:59 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-06-05 16:20:59 +0000 |
commit | 5de84c9242643f786eff03726286578726d7d390 (patch) | |
tree | 8e8eadab2b786c41d7b8a2cdafbb467588928ad0 /tests/test_extensions | |
parent | Releasing progress-linux version 7.2.6-8~progress7.99u1. (diff) | |
download | sphinx-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__.py | 0 | ||||
-rw-r--r-- | tests/test_extensions/autodoc_util.py | 33 | ||||
-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 -> 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 -> 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 \&= \\pi r\^2\\\\' r'V \&= \\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 |