mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
[tests] move utilities and static data into dedicated modules and remove `html5lib
` (#12173)
Since #12168, HTML files are now XML compliant, hence ``html5lib`` is no more needed as a testing dependencies.
This commit is contained in:
parent
9e239729d4
commit
885818bb7f
@ -91,7 +91,6 @@ lint = [
|
|||||||
]
|
]
|
||||||
test = [
|
test = [
|
||||||
"pytest>=6.0",
|
"pytest>=6.0",
|
||||||
"html5lib",
|
|
||||||
"defusedxml>=0.7.1", # for secure XML/HTML parsing
|
"defusedxml>=0.7.1", # for secure XML/HTML parsing
|
||||||
"cython>=3.0",
|
"cython>=3.0",
|
||||||
"setuptools>=67.0", # for Cython compilation
|
"setuptools>=67.0", # for Cython compilation
|
||||||
|
@ -7,12 +7,11 @@ __all__ = ('SphinxTestApp', 'SphinxTestAppWrapperForSkipBuilding')
|
|||||||
import contextlib
|
import contextlib
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import warnings
|
|
||||||
from io import StringIO
|
from io import StringIO
|
||||||
from types import MappingProxyType
|
from types import MappingProxyType
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
from xml.etree import ElementTree
|
|
||||||
|
|
||||||
|
from defusedxml.ElementTree import parse as xml_parse
|
||||||
from docutils import nodes
|
from docutils import nodes
|
||||||
from docutils.parsers.rst import directives, roles
|
from docutils.parsers.rst import directives, roles
|
||||||
|
|
||||||
@ -26,6 +25,7 @@ if TYPE_CHECKING:
|
|||||||
from collections.abc import Mapping
|
from collections.abc import Mapping
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any, Final
|
from typing import Any, Final
|
||||||
|
from xml.etree.ElementTree import ElementTree
|
||||||
|
|
||||||
from docutils.nodes import Node
|
from docutils.nodes import Node
|
||||||
|
|
||||||
@ -70,10 +70,10 @@ def assert_node(node: Node, cls: Any = None, xpath: str = "", **kwargs: Any) ->
|
|||||||
f'The node{xpath}[{key}] is not {value!r}: {node[key]!r}'
|
f'The node{xpath}[{key}] is not {value!r}: {node[key]!r}'
|
||||||
|
|
||||||
|
|
||||||
def etree_parse(path: str) -> Any:
|
# keep this to restrict the API usage and to have a correct return type
|
||||||
with warnings.catch_warnings(record=False):
|
def etree_parse(path: str | os.PathLike[str]) -> ElementTree:
|
||||||
warnings.filterwarnings("ignore", category=DeprecationWarning)
|
"""Parse a file into a (safe) XML element tree."""
|
||||||
return ElementTree.parse(path) # NoQA: S314 # using known data in tests
|
return xml_parse(path)
|
||||||
|
|
||||||
|
|
||||||
class SphinxTestApp(sphinx.application.Sphinx):
|
class SphinxTestApp(sphinx.application.Sphinx):
|
||||||
|
@ -3,26 +3,26 @@ from __future__ import annotations
|
|||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from html5lib import HTMLParser
|
|
||||||
|
from sphinx.testing.util import etree_parse
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from collections.abc import Callable, Generator
|
from collections.abc import Callable, Generator
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from xml.etree.ElementTree import Element
|
from xml.etree.ElementTree import ElementTree
|
||||||
|
|
||||||
etree_cache: dict[Path, Element] = {}
|
_etree_cache: dict[Path, ElementTree] = {}
|
||||||
|
|
||||||
|
|
||||||
def _parse(fname: Path) -> Element:
|
def _parse(path: Path) -> ElementTree:
|
||||||
if fname in etree_cache:
|
if path in _etree_cache:
|
||||||
return etree_cache[fname]
|
return _etree_cache[path]
|
||||||
with fname.open('rb') as fp:
|
|
||||||
etree = HTMLParser(namespaceHTMLElements=False).parse(fp)
|
_etree_cache[path] = tree = etree_parse(path)
|
||||||
etree_cache[fname] = etree
|
return tree
|
||||||
return etree
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope='package')
|
@pytest.fixture(scope='package')
|
||||||
def cached_etree_parse() -> Generator[Callable[[Path], Element], None, None]:
|
def cached_etree_parse() -> Generator[Callable[[Path], ElementTree], None, None]:
|
||||||
yield _parse
|
yield _parse
|
||||||
etree_cache.clear()
|
_etree_cache.clear()
|
||||||
|
@ -12,43 +12,8 @@ from sphinx.errors import ConfigError
|
|||||||
from sphinx.util.console import strip_colors
|
from sphinx.util.console import strip_colors
|
||||||
from sphinx.util.inventory import InventoryFile
|
from sphinx.util.inventory import InventoryFile
|
||||||
|
|
||||||
FIGURE_CAPTION = ".//figure/figcaption/p"
|
from tests.test_builders.xpath_data import FIGURE_CAPTION
|
||||||
|
from tests.test_builders.xpath_util import check_xpath
|
||||||
|
|
||||||
def check_xpath(etree, fname, path, check, be_found=True):
|
|
||||||
nodes = list(etree.findall(path))
|
|
||||||
if check is None:
|
|
||||||
assert nodes == [], ('found any nodes matching xpath '
|
|
||||||
f'{path!r} in file {fname}')
|
|
||||||
return
|
|
||||||
else:
|
|
||||||
assert nodes != [], ('did not find any node matching xpath '
|
|
||||||
f'{path!r} in file {fname}')
|
|
||||||
if callable(check):
|
|
||||||
check(nodes)
|
|
||||||
elif not check:
|
|
||||||
# only check for node presence
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
def get_text(node):
|
|
||||||
if node.text is not None:
|
|
||||||
# the node has only one text
|
|
||||||
return node.text
|
|
||||||
else:
|
|
||||||
# the node has tags and text; gather texts just under the node
|
|
||||||
return ''.join(n.tail or '' for n in node)
|
|
||||||
|
|
||||||
rex = re.compile(check)
|
|
||||||
if be_found:
|
|
||||||
if any(rex.search(get_text(node)) for node in nodes):
|
|
||||||
return
|
|
||||||
else:
|
|
||||||
if all(not rex.search(get_text(node)) for node in nodes):
|
|
||||||
return
|
|
||||||
|
|
||||||
msg = (f'{check!r} not found in any node matching '
|
|
||||||
f'{path!r} in file {fname}: {[node.text for node in nodes]!r}')
|
|
||||||
raise AssertionError(msg)
|
|
||||||
|
|
||||||
|
|
||||||
def test_html4_error(make_app, tmp_path):
|
def test_html4_error(make_app, tmp_path):
|
||||||
|
@ -4,7 +4,7 @@ import re
|
|||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from tests.test_builders.test_build_html import check_xpath
|
from tests.test_builders.xpath_util import check_xpath
|
||||||
|
|
||||||
|
|
||||||
def tail_check(check):
|
def tail_check(check):
|
||||||
@ -128,7 +128,7 @@ def tail_check(check):
|
|||||||
# ``seealso`` directive
|
# ``seealso`` directive
|
||||||
('markup.html', ".//div/p[@class='admonition-title']", 'See also'),
|
('markup.html', ".//div/p[@class='admonition-title']", 'See also'),
|
||||||
# a ``hlist`` directive
|
# a ``hlist`` directive
|
||||||
('markup.html', ".//table[@class='hlist']/tbody/tr/td/ul/li/p", '^This$'),
|
('markup.html', ".//table[@class='hlist']/tr/td/ul/li/p", '^This$'),
|
||||||
# a ``centered`` directive
|
# a ``centered`` directive
|
||||||
('markup.html', ".//p[@class='centered']/strong", 'LICENSE'),
|
('markup.html', ".//p[@class='centered']/strong", 'LICENSE'),
|
||||||
# a glossary
|
# a glossary
|
||||||
|
@ -5,7 +5,8 @@ import re
|
|||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from tests.test_builders.test_build_html import FIGURE_CAPTION, check_xpath
|
from tests.test_builders.xpath_data import FIGURE_CAPTION
|
||||||
|
from tests.test_builders.xpath_util import check_xpath
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.sphinx('html', testroot='numfig')
|
@pytest.mark.sphinx('html', testroot='numfig')
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from tests.test_builders.test_build_html import check_xpath
|
from tests.test_builders.xpath_util import check_xpath
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(("fname", "path", "check", "be_found"), [
|
@pytest.mark.parametrize(("fname", "path", "check", "be_found"), [
|
||||||
|
8
tests/test_builders/xpath_data.py
Normal file
8
tests/test_builders/xpath_data.py
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from typing import Final
|
||||||
|
|
||||||
|
FIGURE_CAPTION: Final[str] = ".//figure/figcaption/p"
|
79
tests/test_builders/xpath_util.py
Normal file
79
tests/test_builders/xpath_util.py
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import re
|
||||||
|
import textwrap
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
from xml.etree.ElementTree import tostring
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
import os
|
||||||
|
from collections.abc import Callable, Iterable, Sequence
|
||||||
|
from xml.etree.ElementTree import Element, ElementTree
|
||||||
|
|
||||||
|
|
||||||
|
def _get_text(node: Element) -> str:
|
||||||
|
if node.text is not None:
|
||||||
|
# the node has only one text
|
||||||
|
return node.text
|
||||||
|
|
||||||
|
# the node has tags and text; gather texts just under the node
|
||||||
|
return ''.join(n.tail or '' for n in node)
|
||||||
|
|
||||||
|
|
||||||
|
def _prettify(nodes: Iterable[Element]) -> str:
|
||||||
|
def pformat(node: Element) -> str:
|
||||||
|
return tostring(node, encoding='unicode', method='html')
|
||||||
|
|
||||||
|
return ''.join(f'(i={index}) {pformat(node)}\n' for index, node in enumerate(nodes))
|
||||||
|
|
||||||
|
|
||||||
|
def check_xpath(
|
||||||
|
etree: ElementTree,
|
||||||
|
filename: str | os.PathLike[str],
|
||||||
|
xpath: str,
|
||||||
|
check: str | re.Pattern[str] | Callable[[Sequence[Element]], None] | None,
|
||||||
|
be_found: bool = True,
|
||||||
|
*,
|
||||||
|
min_count: int = 1,
|
||||||
|
) -> None:
|
||||||
|
"""Check that one or more nodes satisfy a predicate.
|
||||||
|
|
||||||
|
:param etree: The element tree.
|
||||||
|
:param filename: The element tree source name (for errors only).
|
||||||
|
:param xpath: An XPath expression to use.
|
||||||
|
:param check: Optional regular expression or a predicate the nodes must validate.
|
||||||
|
:param be_found: If false, negate the predicate.
|
||||||
|
:param min_count: Minimum number of nodes expected to satisfy the predicate.
|
||||||
|
|
||||||
|
* If *check* is empty (``''``), only the minimum count is checked.
|
||||||
|
* If *check* is ``None``, no node should satisfy the XPath expression.
|
||||||
|
"""
|
||||||
|
nodes = etree.findall(xpath)
|
||||||
|
assert isinstance(nodes, list)
|
||||||
|
|
||||||
|
if check is None:
|
||||||
|
# use == to have a nice pytest diff
|
||||||
|
assert nodes == [], f'found nodes matching xpath {xpath!r} in file {filename}'
|
||||||
|
return
|
||||||
|
|
||||||
|
assert len(nodes) >= min_count, (f'expecting at least {min_count} node(s) '
|
||||||
|
f'to satisfy {xpath!r} in file {filename}')
|
||||||
|
|
||||||
|
if check == '':
|
||||||
|
return
|
||||||
|
|
||||||
|
if callable(check):
|
||||||
|
check(nodes)
|
||||||
|
return
|
||||||
|
|
||||||
|
rex = re.compile(check)
|
||||||
|
if be_found:
|
||||||
|
if any(rex.search(_get_text(node)) for node in nodes):
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
if all(not rex.search(_get_text(node)) for node in nodes):
|
||||||
|
return
|
||||||
|
|
||||||
|
ctx = textwrap.indent(_prettify(nodes), ' ' * 2)
|
||||||
|
msg = f'{check!r} not found in any node matching {xpath!r} in file {filename}:\n{ctx}'
|
||||||
|
raise AssertionError(msg)
|
@ -5,7 +5,6 @@ from unittest import mock
|
|||||||
import pytest
|
import pytest
|
||||||
from docutils import nodes
|
from docutils import nodes
|
||||||
from docutils.nodes import definition, definition_list, definition_list_item, term
|
from docutils.nodes import definition, definition_list, definition_list_item, term
|
||||||
from html5lib import HTMLParser
|
|
||||||
|
|
||||||
from sphinx import addnodes
|
from sphinx import addnodes
|
||||||
from sphinx.addnodes import (
|
from sphinx.addnodes import (
|
||||||
@ -20,7 +19,7 @@ from sphinx.addnodes import (
|
|||||||
)
|
)
|
||||||
from sphinx.domains.std import StandardDomain
|
from sphinx.domains.std import StandardDomain
|
||||||
from sphinx.testing import restructuredtext
|
from sphinx.testing import restructuredtext
|
||||||
from sphinx.testing.util import assert_node
|
from sphinx.testing.util import assert_node, etree_parse
|
||||||
|
|
||||||
|
|
||||||
def test_process_doc_handle_figure_caption():
|
def test_process_doc_handle_figure_caption():
|
||||||
@ -375,9 +374,11 @@ def test_productionlist(app, status, warning):
|
|||||||
assert warnings[-1] == ''
|
assert warnings[-1] == ''
|
||||||
assert "Dup2.rst:4: WARNING: duplicate token description of Dup, other instance in Dup1" in warnings[0]
|
assert "Dup2.rst:4: WARNING: duplicate token description of Dup, other instance in Dup1" in warnings[0]
|
||||||
|
|
||||||
with (app.outdir / 'index.html').open('rb') as f:
|
etree = etree_parse(app.outdir / 'index.html')
|
||||||
etree = HTMLParser(namespaceHTMLElements=False).parse(f)
|
nodes = list(etree.iter('ul'))
|
||||||
ul = list(etree.iter('ul'))[1]
|
assert len(nodes) >= 2
|
||||||
|
|
||||||
|
ul = nodes[1]
|
||||||
cases = []
|
cases = []
|
||||||
for li in list(ul):
|
for li in list(ul):
|
||||||
assert len(list(li)) == 1
|
assert len(list(li)) == 1
|
||||||
|
33
tests/test_extensions/autodoc_util.py
Normal file
33
tests/test_extensions/autodoc_util.py
Normal file
@ -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
|
@ -20,8 +20,8 @@ from docutils.statemachine import ViewList
|
|||||||
|
|
||||||
from sphinx import addnodes
|
from sphinx import addnodes
|
||||||
from sphinx.ext.autodoc import ALL, ModuleLevelDocumenter, Options
|
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:
|
try:
|
||||||
# Enable pyximport to test cython module
|
# Enable pyximport to test cython module
|
||||||
@ -34,20 +34,6 @@ if TYPE_CHECKING:
|
|||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
|
|
||||||
def do_autodoc(app, objtype, name, options=None):
|
|
||||||
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
|
|
||||||
|
|
||||||
|
|
||||||
def make_directive_bridge(env):
|
def make_directive_bridge(env):
|
||||||
options = Options(
|
options = Options(
|
||||||
inherited_members=False,
|
inherited_members=False,
|
||||||
@ -82,23 +68,6 @@ def make_directive_bridge(env):
|
|||||||
processed_signatures = []
|
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 test_parse_name(app):
|
||||||
def verify(objtype, name, result):
|
def verify(objtype, name, result):
|
||||||
inst = app.registry.documenters[objtype](directive, name)
|
inst = app.registry.documenters[objtype](directive, name)
|
||||||
@ -139,6 +108,21 @@ def test_parse_name(app):
|
|||||||
|
|
||||||
|
|
||||||
def test_format_signature(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-process-signature', process_signature)
|
||||||
app.connect('autodoc-skip-member', skip_member)
|
app.connect('autodoc-skip-member', skip_member)
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@ source file translated by test_build.
|
|||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from tests.test_extensions.test_ext_autodoc import do_autodoc
|
from tests.test_extensions.autodoc_util import do_autodoc
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.sphinx('html', testroot='ext-autodoc')
|
@pytest.mark.sphinx('html', testroot='ext-autodoc')
|
||||||
|
@ -11,7 +11,7 @@ from typing import Union
|
|||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from tests.test_extensions.test_ext_autodoc import do_autodoc
|
from tests.test_extensions.autodoc_util import do_autodoc
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.sphinx('html', testroot='ext-autodoc')
|
@pytest.mark.sphinx('html', testroot='ext-autodoc')
|
||||||
|
@ -6,7 +6,7 @@ source file translated by test_build.
|
|||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from tests.test_extensions.test_ext_autodoc import do_autodoc
|
from tests.test_extensions.autodoc_util import do_autodoc
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.sphinx('html', testroot='ext-autodoc')
|
@pytest.mark.sphinx('html', testroot='ext-autodoc')
|
||||||
|
@ -6,7 +6,7 @@ source file translated by test_build.
|
|||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from tests.test_extensions.test_ext_autodoc import do_autodoc
|
from tests.test_extensions.autodoc_util import do_autodoc
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.sphinx('html', testroot='ext-autodoc')
|
@pytest.mark.sphinx('html', testroot='ext-autodoc')
|
||||||
|
@ -8,7 +8,7 @@ import sys
|
|||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from tests.test_extensions.test_ext_autodoc import do_autodoc
|
from tests.test_extensions.autodoc_util import do_autodoc
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.sphinx('html', testroot='ext-autodoc')
|
@pytest.mark.sphinx('html', testroot='ext-autodoc')
|
||||||
|
@ -6,7 +6,7 @@ source file translated by test_build.
|
|||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from tests.test_extensions.test_ext_autodoc import do_autodoc
|
from tests.test_extensions.autodoc_util import do_autodoc
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.sphinx('html', testroot='ext-autodoc')
|
@pytest.mark.sphinx('html', testroot='ext-autodoc')
|
||||||
|
@ -8,7 +8,7 @@ import pytest
|
|||||||
|
|
||||||
from sphinx.testing import restructuredtext
|
from sphinx.testing import restructuredtext
|
||||||
|
|
||||||
from tests.test_extensions.test_ext_autodoc import do_autodoc
|
from tests.test_extensions.autodoc_util import do_autodoc
|
||||||
|
|
||||||
IS_PYPY = platform.python_implementation() == 'PyPy'
|
IS_PYPY = platform.python_implementation() == 'PyPy'
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@ import pytest
|
|||||||
|
|
||||||
from sphinx.ext.autodoc import between, cut_lines
|
from sphinx.ext.autodoc import between, cut_lines
|
||||||
|
|
||||||
from tests.test_extensions.test_ext_autodoc import do_autodoc
|
from tests.test_extensions.autodoc_util import do_autodoc
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.sphinx('html', testroot='ext-autodoc')
|
@pytest.mark.sphinx('html', testroot='ext-autodoc')
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from tests.test_extensions.test_ext_autodoc import do_autodoc
|
from tests.test_extensions.autodoc_util import do_autodoc
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.sphinx('html', testroot='ext-autodoc',
|
@pytest.mark.sphinx('html', testroot='ext-autodoc',
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from tests.test_extensions.test_ext_autodoc import do_autodoc
|
from tests.test_extensions.autodoc_util import do_autodoc
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.sphinx('html', testroot='ext-autodoc')
|
@pytest.mark.sphinx('html', testroot='ext-autodoc')
|
||||||
|
@ -20,7 +20,7 @@ from sphinx.ext.intersphinx import (
|
|||||||
from sphinx.ext.intersphinx import setup as intersphinx_setup
|
from sphinx.ext.intersphinx import setup as intersphinx_setup
|
||||||
from sphinx.util.console import strip_colors
|
from sphinx.util.console import strip_colors
|
||||||
|
|
||||||
from tests.test_util.test_util_inventory import inventory_v2, inventory_v2_not_having_version
|
from tests.test_util.intersphinx_data import INVENTORY_V2, INVENTORY_V2_NO_VERSION
|
||||||
from tests.utils import http_server
|
from tests.utils import http_server
|
||||||
|
|
||||||
|
|
||||||
@ -92,7 +92,7 @@ def test_fetch_inventory_redirection(_read_from_url, InventoryFile, app, status,
|
|||||||
|
|
||||||
def test_missing_reference(tmp_path, app, status, warning):
|
def test_missing_reference(tmp_path, app, status, warning):
|
||||||
inv_file = tmp_path / 'inventory'
|
inv_file = tmp_path / 'inventory'
|
||||||
inv_file.write_bytes(inventory_v2)
|
inv_file.write_bytes(INVENTORY_V2)
|
||||||
set_config(app, {
|
set_config(app, {
|
||||||
'https://docs.python.org/': str(inv_file),
|
'https://docs.python.org/': str(inv_file),
|
||||||
'py3k': ('https://docs.python.org/py3k/', str(inv_file)),
|
'py3k': ('https://docs.python.org/py3k/', str(inv_file)),
|
||||||
@ -170,7 +170,7 @@ def test_missing_reference(tmp_path, app, status, warning):
|
|||||||
|
|
||||||
def test_missing_reference_pydomain(tmp_path, app, status, warning):
|
def test_missing_reference_pydomain(tmp_path, app, status, warning):
|
||||||
inv_file = tmp_path / 'inventory'
|
inv_file = tmp_path / 'inventory'
|
||||||
inv_file.write_bytes(inventory_v2)
|
inv_file.write_bytes(INVENTORY_V2)
|
||||||
set_config(app, {
|
set_config(app, {
|
||||||
'https://docs.python.org/': str(inv_file),
|
'https://docs.python.org/': str(inv_file),
|
||||||
})
|
})
|
||||||
@ -200,7 +200,7 @@ def test_missing_reference_pydomain(tmp_path, app, status, warning):
|
|||||||
|
|
||||||
def test_missing_reference_stddomain(tmp_path, app, status, warning):
|
def test_missing_reference_stddomain(tmp_path, app, status, warning):
|
||||||
inv_file = tmp_path / 'inventory'
|
inv_file = tmp_path / 'inventory'
|
||||||
inv_file.write_bytes(inventory_v2)
|
inv_file.write_bytes(INVENTORY_V2)
|
||||||
set_config(app, {
|
set_config(app, {
|
||||||
'cmd': ('https://docs.python.org/', str(inv_file)),
|
'cmd': ('https://docs.python.org/', str(inv_file)),
|
||||||
})
|
})
|
||||||
@ -251,7 +251,7 @@ def test_missing_reference_stddomain(tmp_path, app, status, warning):
|
|||||||
@pytest.mark.sphinx('html', testroot='ext-intersphinx-cppdomain')
|
@pytest.mark.sphinx('html', testroot='ext-intersphinx-cppdomain')
|
||||||
def test_missing_reference_cppdomain(tmp_path, app, status, warning):
|
def test_missing_reference_cppdomain(tmp_path, app, status, warning):
|
||||||
inv_file = tmp_path / 'inventory'
|
inv_file = tmp_path / 'inventory'
|
||||||
inv_file.write_bytes(inventory_v2)
|
inv_file.write_bytes(INVENTORY_V2)
|
||||||
set_config(app, {
|
set_config(app, {
|
||||||
'https://docs.python.org/': str(inv_file),
|
'https://docs.python.org/': str(inv_file),
|
||||||
})
|
})
|
||||||
@ -277,7 +277,7 @@ def test_missing_reference_cppdomain(tmp_path, app, status, warning):
|
|||||||
|
|
||||||
def test_missing_reference_jsdomain(tmp_path, app, status, warning):
|
def test_missing_reference_jsdomain(tmp_path, app, status, warning):
|
||||||
inv_file = tmp_path / 'inventory'
|
inv_file = tmp_path / 'inventory'
|
||||||
inv_file.write_bytes(inventory_v2)
|
inv_file.write_bytes(INVENTORY_V2)
|
||||||
set_config(app, {
|
set_config(app, {
|
||||||
'https://docs.python.org/': str(inv_file),
|
'https://docs.python.org/': str(inv_file),
|
||||||
})
|
})
|
||||||
@ -301,7 +301,7 @@ def test_missing_reference_jsdomain(tmp_path, app, status, warning):
|
|||||||
|
|
||||||
def test_missing_reference_disabled_domain(tmp_path, app, status, warning):
|
def test_missing_reference_disabled_domain(tmp_path, app, status, warning):
|
||||||
inv_file = tmp_path / 'inventory'
|
inv_file = tmp_path / 'inventory'
|
||||||
inv_file.write_bytes(inventory_v2)
|
inv_file.write_bytes(INVENTORY_V2)
|
||||||
set_config(app, {
|
set_config(app, {
|
||||||
'inv': ('https://docs.python.org/', str(inv_file)),
|
'inv': ('https://docs.python.org/', str(inv_file)),
|
||||||
})
|
})
|
||||||
@ -363,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):
|
def test_inventory_not_having_version(tmp_path, app, status, warning):
|
||||||
inv_file = tmp_path / 'inventory'
|
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, {
|
set_config(app, {
|
||||||
'https://docs.python.org/': str(inv_file),
|
'https://docs.python.org/': str(inv_file),
|
||||||
})
|
})
|
||||||
@ -385,7 +385,7 @@ def test_load_mappings_warnings(tmp_path, app, status, warning):
|
|||||||
identifiers are not string
|
identifiers are not string
|
||||||
"""
|
"""
|
||||||
inv_file = tmp_path / 'inventory'
|
inv_file = tmp_path / 'inventory'
|
||||||
inv_file.write_bytes(inventory_v2)
|
inv_file.write_bytes(INVENTORY_V2)
|
||||||
set_config(app, {
|
set_config(app, {
|
||||||
'https://docs.python.org/': str(inv_file),
|
'https://docs.python.org/': str(inv_file),
|
||||||
'py3k': ('https://docs.python.org/py3k/', str(inv_file)),
|
'py3k': ('https://docs.python.org/py3k/', str(inv_file)),
|
||||||
@ -406,7 +406,7 @@ def test_load_mappings_warnings(tmp_path, app, status, warning):
|
|||||||
|
|
||||||
def test_load_mappings_fallback(tmp_path, app, status, warning):
|
def test_load_mappings_fallback(tmp_path, app, status, warning):
|
||||||
inv_file = tmp_path / 'inventory'
|
inv_file = tmp_path / 'inventory'
|
||||||
inv_file.write_bytes(inventory_v2)
|
inv_file.write_bytes(INVENTORY_V2)
|
||||||
set_config(app, {})
|
set_config(app, {})
|
||||||
|
|
||||||
# connect to invalid path
|
# connect to invalid path
|
||||||
@ -505,7 +505,7 @@ def test_inspect_main_noargs(capsys):
|
|||||||
def test_inspect_main_file(capsys, tmp_path):
|
def test_inspect_main_file(capsys, tmp_path):
|
||||||
"""inspect_main interface, with file argument"""
|
"""inspect_main interface, with file argument"""
|
||||||
inv_file = tmp_path / 'inventory'
|
inv_file = tmp_path / 'inventory'
|
||||||
inv_file.write_bytes(inventory_v2)
|
inv_file.write_bytes(INVENTORY_V2)
|
||||||
|
|
||||||
inspect_main([str(inv_file)])
|
inspect_main([str(inv_file)])
|
||||||
|
|
||||||
@ -520,7 +520,7 @@ def test_inspect_main_url(capsys):
|
|||||||
def do_GET(self):
|
def do_GET(self):
|
||||||
self.send_response(200, "OK")
|
self.send_response(200, "OK")
|
||||||
self.end_headers()
|
self.end_headers()
|
||||||
self.wfile.write(inventory_v2)
|
self.wfile.write(INVENTORY_V2)
|
||||||
|
|
||||||
def log_message(*args, **kwargs):
|
def log_message(*args, **kwargs):
|
||||||
# Silenced.
|
# Silenced.
|
||||||
@ -539,7 +539,7 @@ def test_inspect_main_url(capsys):
|
|||||||
@pytest.mark.sphinx('html', testroot='ext-intersphinx-role')
|
@pytest.mark.sphinx('html', testroot='ext-intersphinx-role')
|
||||||
def test_intersphinx_role(app, warning):
|
def test_intersphinx_role(app, warning):
|
||||||
inv_file = app.srcdir / 'inventory'
|
inv_file = app.srcdir / 'inventory'
|
||||||
inv_file.write_bytes(inventory_v2)
|
inv_file.write_bytes(INVENTORY_V2)
|
||||||
app.config.intersphinx_mapping = {
|
app.config.intersphinx_mapping = {
|
||||||
'inv': ('https://example.org/', str(inv_file)),
|
'inv': ('https://example.org/', str(inv_file)),
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,6 @@ from textwrap import dedent
|
|||||||
from unittest import mock
|
from unittest import mock
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from html5lib import HTMLParser
|
|
||||||
|
|
||||||
from sphinx.ext.intersphinx import load_mappings, normalize_intersphinx_mapping
|
from sphinx.ext.intersphinx import load_mappings, normalize_intersphinx_mapping
|
||||||
from sphinx.ext.napoleon import Config
|
from sphinx.ext.napoleon import Config
|
||||||
@ -21,6 +20,7 @@ from sphinx.ext.napoleon.docstring import (
|
|||||||
_token_type,
|
_token_type,
|
||||||
_tokenize_type_spec,
|
_tokenize_type_spec,
|
||||||
)
|
)
|
||||||
|
from sphinx.testing.util import etree_parse
|
||||||
|
|
||||||
from tests.test_extensions.ext_napoleon_pep526_data_google import PEP526GoogleClass
|
from tests.test_extensions.ext_napoleon_pep526_data_google import PEP526GoogleClass
|
||||||
from tests.test_extensions.ext_napoleon_pep526_data_numpy import PEP526NumpyClass
|
from tests.test_extensions.ext_napoleon_pep526_data_numpy import PEP526NumpyClass
|
||||||
@ -2684,8 +2684,7 @@ int py:class 1 int.html -
|
|||||||
|
|
||||||
app.build(force_all=True)
|
app.build(force_all=True)
|
||||||
|
|
||||||
buffer = (app.outdir / 'index.html').read_bytes()
|
etree = etree_parse(app.outdir / 'index.html')
|
||||||
etree = HTMLParser(namespaceHTMLElements=False).parse(buffer)
|
|
||||||
|
|
||||||
for name, typename in product(('keyword', 'kwarg', 'kwparam'), ('paramtype', 'kwtype')):
|
for name, typename in product(('keyword', 'kwarg', 'kwparam'), ('paramtype', 'kwtype')):
|
||||||
param = f'{name}_{typename}'
|
param = f'{name}_{typename}'
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
"""Test smart quotes."""
|
"""Test smart quotes."""
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from html5lib import HTMLParser
|
|
||||||
|
from sphinx.testing.util import etree_parse
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.sphinx(buildername='html', testroot='smartquotes', freshenv=True)
|
@pytest.mark.sphinx(buildername='html', testroot='smartquotes', freshenv=True)
|
||||||
@ -16,9 +17,7 @@ def test_basic(app, status, warning):
|
|||||||
def test_literals(app, status, warning):
|
def test_literals(app, status, warning):
|
||||||
app.build()
|
app.build()
|
||||||
|
|
||||||
with (app.outdir / 'literals.html').open(encoding='utf-8') as html_file:
|
etree = etree_parse(app.outdir / 'literals.html')
|
||||||
etree = HTMLParser(namespaceHTMLElements=False).parse(html_file)
|
|
||||||
|
|
||||||
for code_element in etree.iter('code'):
|
for code_element in etree.iter('code'):
|
||||||
code_text = ''.join(code_element.itertext())
|
code_text = ''.join(code_element.itertext())
|
||||||
|
|
||||||
|
52
tests/test_util/intersphinx_data.py
Normal file
52
tests/test_util/intersphinx_data.py
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import zlib
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from typing import Final
|
||||||
|
|
||||||
|
INVENTORY_V1: Final[bytes] = b'''\
|
||||||
|
# Sphinx inventory version 1
|
||||||
|
# Project: foo
|
||||||
|
# Version: 1.0
|
||||||
|
module mod foo.html
|
||||||
|
module.cls class foo.html
|
||||||
|
'''
|
||||||
|
|
||||||
|
INVENTORY_V2: Final[bytes] = b'''\
|
||||||
|
# Sphinx inventory version 2
|
||||||
|
# Project: foo
|
||||||
|
# Version: 2.0
|
||||||
|
# The remainder of this file is compressed with zlib.
|
||||||
|
''' + zlib.compress(b'''\
|
||||||
|
module1 py:module 0 foo.html#module-module1 Long Module desc
|
||||||
|
module2 py:module 0 foo.html#module-$ -
|
||||||
|
module1.func py:function 1 sub/foo.html#$ -
|
||||||
|
module1.Foo.bar py:method 1 index.html#foo.Bar.baz -
|
||||||
|
CFunc c:function 2 cfunc.html#CFunc -
|
||||||
|
std cpp:type 1 index.html#std -
|
||||||
|
std::uint8_t cpp:type 1 index.html#std_uint8_t -
|
||||||
|
foo::Bar cpp:class 1 index.html#cpp_foo_bar -
|
||||||
|
foo::Bar::baz cpp:function 1 index.html#cpp_foo_bar_baz -
|
||||||
|
foons cpp:type 1 index.html#foons -
|
||||||
|
foons::bartype cpp:type 1 index.html#foons_bartype -
|
||||||
|
a term std:term -1 glossary.html#term-a-term -
|
||||||
|
ls.-l std:cmdoption 1 index.html#cmdoption-ls-l -
|
||||||
|
docname std:doc -1 docname.html -
|
||||||
|
foo js:module 1 index.html#foo -
|
||||||
|
foo.bar js:class 1 index.html#foo.bar -
|
||||||
|
foo.bar.baz js:method 1 index.html#foo.bar.baz -
|
||||||
|
foo.bar.qux js:data 1 index.html#foo.bar.qux -
|
||||||
|
a term including:colon std:term -1 glossary.html#term-a-term-including-colon -
|
||||||
|
The-Julia-Domain std:label -1 write_inventory/#$ The Julia Domain
|
||||||
|
''')
|
||||||
|
|
||||||
|
INVENTORY_V2_NO_VERSION: Final[bytes] = b'''\
|
||||||
|
# Sphinx inventory version 2
|
||||||
|
# Project: foo
|
||||||
|
# Version:
|
||||||
|
# The remainder of this file is compressed with zlib.
|
||||||
|
''' + zlib.compress(b'''\
|
||||||
|
module1 py:module 0 foo.html#module-module1 Long Module desc
|
||||||
|
''')
|
@ -223,168 +223,140 @@ def test_signature_partialmethod():
|
|||||||
|
|
||||||
|
|
||||||
def test_signature_annotations():
|
def test_signature_annotations():
|
||||||
from tests.test_util.typing_test_data import (
|
import tests.test_util.typing_test_data as mod
|
||||||
Node,
|
|
||||||
f0,
|
|
||||||
f1,
|
|
||||||
f2,
|
|
||||||
f3,
|
|
||||||
f4,
|
|
||||||
f5,
|
|
||||||
f6,
|
|
||||||
f7,
|
|
||||||
f8,
|
|
||||||
f9,
|
|
||||||
f10,
|
|
||||||
f11,
|
|
||||||
f12,
|
|
||||||
f13,
|
|
||||||
f14,
|
|
||||||
f15,
|
|
||||||
f16,
|
|
||||||
f17,
|
|
||||||
f18,
|
|
||||||
f19,
|
|
||||||
f20,
|
|
||||||
f21,
|
|
||||||
f22,
|
|
||||||
f23,
|
|
||||||
f24,
|
|
||||||
f25,
|
|
||||||
)
|
|
||||||
|
|
||||||
# Class annotations
|
# Class annotations
|
||||||
sig = inspect.signature(f0)
|
sig = inspect.signature(mod.f0)
|
||||||
assert stringify_signature(sig) == '(x: int, y: numbers.Integral) -> None'
|
assert stringify_signature(sig) == '(x: int, y: numbers.Integral) -> None'
|
||||||
|
|
||||||
# Generic types with concrete parameters
|
# Generic types with concrete parameters
|
||||||
sig = inspect.signature(f1)
|
sig = inspect.signature(mod.f1)
|
||||||
assert stringify_signature(sig) == '(x: list[int]) -> typing.List[int]'
|
assert stringify_signature(sig) == '(x: list[int]) -> typing.List[int]'
|
||||||
|
|
||||||
# TypeVars and generic types with TypeVars
|
# TypeVars and generic types with TypeVars
|
||||||
sig = inspect.signature(f2)
|
sig = inspect.signature(mod.f2)
|
||||||
assert stringify_signature(sig) == ('(x: typing.List[tests.test_util.typing_test_data.T],'
|
assert stringify_signature(sig) == ('(x: typing.List[tests.test_util.typing_test_data.T],'
|
||||||
' y: typing.List[tests.test_util.typing_test_data.T_co],'
|
' y: typing.List[tests.test_util.typing_test_data.T_co],'
|
||||||
' z: tests.test_util.typing_test_data.T'
|
' z: tests.test_util.typing_test_data.T'
|
||||||
') -> typing.List[tests.test_util.typing_test_data.T_contra]')
|
') -> typing.List[tests.test_util.typing_test_data.T_contra]')
|
||||||
|
|
||||||
# Union types
|
# Union types
|
||||||
sig = inspect.signature(f3)
|
sig = inspect.signature(mod.f3)
|
||||||
assert stringify_signature(sig) == '(x: str | numbers.Integral) -> None'
|
assert stringify_signature(sig) == '(x: str | numbers.Integral) -> None'
|
||||||
|
|
||||||
# Quoted annotations
|
# Quoted annotations
|
||||||
sig = inspect.signature(f4)
|
sig = inspect.signature(mod.f4)
|
||||||
assert stringify_signature(sig) == '(x: str, y: str) -> None'
|
assert stringify_signature(sig) == '(x: str, y: str) -> None'
|
||||||
|
|
||||||
# Keyword-only arguments
|
# Keyword-only arguments
|
||||||
sig = inspect.signature(f5)
|
sig = inspect.signature(mod.f5)
|
||||||
assert stringify_signature(sig) == '(x: int, *, y: str, z: str) -> None'
|
assert stringify_signature(sig) == '(x: int, *, y: str, z: str) -> None'
|
||||||
|
|
||||||
# Keyword-only arguments with varargs
|
# Keyword-only arguments with varargs
|
||||||
sig = inspect.signature(f6)
|
sig = inspect.signature(mod.f6)
|
||||||
assert stringify_signature(sig) == '(x: int, *args, y: str, z: str) -> None'
|
assert stringify_signature(sig) == '(x: int, *args, y: str, z: str) -> None'
|
||||||
|
|
||||||
# Space around '=' for defaults
|
# Space around '=' for defaults
|
||||||
sig = inspect.signature(f7)
|
sig = inspect.signature(mod.f7)
|
||||||
if sys.version_info[:2] <= (3, 10):
|
if sys.version_info[:2] <= (3, 10):
|
||||||
assert stringify_signature(sig) == '(x: int | None = None, y: dict = {}) -> None'
|
assert stringify_signature(sig) == '(x: int | None = None, y: dict = {}) -> None'
|
||||||
else:
|
else:
|
||||||
assert stringify_signature(sig) == '(x: int = None, y: dict = {}) -> None'
|
assert stringify_signature(sig) == '(x: int = None, y: dict = {}) -> None'
|
||||||
|
|
||||||
# Callable types
|
# Callable types
|
||||||
sig = inspect.signature(f8)
|
sig = inspect.signature(mod.f8)
|
||||||
assert stringify_signature(sig) == '(x: typing.Callable[[int, str], int]) -> None'
|
assert stringify_signature(sig) == '(x: typing.Callable[[int, str], int]) -> None'
|
||||||
|
|
||||||
sig = inspect.signature(f9)
|
sig = inspect.signature(mod.f9)
|
||||||
assert stringify_signature(sig) == '(x: typing.Callable) -> None'
|
assert stringify_signature(sig) == '(x: typing.Callable) -> None'
|
||||||
|
|
||||||
# Tuple types
|
# Tuple types
|
||||||
sig = inspect.signature(f10)
|
sig = inspect.signature(mod.f10)
|
||||||
assert stringify_signature(sig) == '(x: typing.Tuple[int, str], y: typing.Tuple[int, ...]) -> None'
|
assert stringify_signature(sig) == '(x: typing.Tuple[int, str], y: typing.Tuple[int, ...]) -> None'
|
||||||
|
|
||||||
# Instance annotations
|
# Instance annotations
|
||||||
sig = inspect.signature(f11)
|
sig = inspect.signature(mod.f11)
|
||||||
assert stringify_signature(sig) == '(x: CustomAnnotation, y: 123) -> None'
|
assert stringify_signature(sig) == '(x: CustomAnnotation, y: 123) -> None'
|
||||||
|
|
||||||
# tuple with more than two items
|
# tuple with more than two items
|
||||||
sig = inspect.signature(f12)
|
sig = inspect.signature(mod.f12)
|
||||||
assert stringify_signature(sig) == '() -> typing.Tuple[int, str, int]'
|
assert stringify_signature(sig) == '() -> typing.Tuple[int, str, int]'
|
||||||
|
|
||||||
# optional
|
# optional
|
||||||
sig = inspect.signature(f13)
|
sig = inspect.signature(mod.f13)
|
||||||
assert stringify_signature(sig) == '() -> str | None'
|
assert stringify_signature(sig) == '() -> str | None'
|
||||||
|
|
||||||
# optional union
|
# optional union
|
||||||
sig = inspect.signature(f20)
|
sig = inspect.signature(mod.f20)
|
||||||
assert stringify_signature(sig) in ('() -> int | str | None',
|
assert stringify_signature(sig) in ('() -> int | str | None',
|
||||||
'() -> str | int | None')
|
'() -> str | int | None')
|
||||||
|
|
||||||
# Any
|
# Any
|
||||||
sig = inspect.signature(f14)
|
sig = inspect.signature(mod.f14)
|
||||||
assert stringify_signature(sig) == '() -> typing.Any'
|
assert stringify_signature(sig) == '() -> typing.Any'
|
||||||
|
|
||||||
# ForwardRef
|
# ForwardRef
|
||||||
sig = inspect.signature(f15)
|
sig = inspect.signature(mod.f15)
|
||||||
assert stringify_signature(sig) == '(x: Unknown, y: int) -> typing.Any'
|
assert stringify_signature(sig) == '(x: Unknown, y: int) -> typing.Any'
|
||||||
|
|
||||||
# keyword only arguments (1)
|
# keyword only arguments (1)
|
||||||
sig = inspect.signature(f16)
|
sig = inspect.signature(mod.f16)
|
||||||
assert stringify_signature(sig) == '(arg1, arg2, *, arg3=None, arg4=None)'
|
assert stringify_signature(sig) == '(arg1, arg2, *, arg3=None, arg4=None)'
|
||||||
|
|
||||||
# keyword only arguments (2)
|
# keyword only arguments (2)
|
||||||
sig = inspect.signature(f17)
|
sig = inspect.signature(mod.f17)
|
||||||
assert stringify_signature(sig) == '(*, arg3, arg4)'
|
assert stringify_signature(sig) == '(*, arg3, arg4)'
|
||||||
|
|
||||||
sig = inspect.signature(f18)
|
sig = inspect.signature(mod.f18)
|
||||||
assert stringify_signature(sig) == ('(self, arg1: int | typing.Tuple = 10) -> '
|
assert stringify_signature(sig) == ('(self, arg1: int | typing.Tuple = 10) -> '
|
||||||
'typing.List[typing.Dict]')
|
'typing.List[typing.Dict]')
|
||||||
|
|
||||||
# annotations for variadic and keyword parameters
|
# annotations for variadic and keyword parameters
|
||||||
sig = inspect.signature(f19)
|
sig = inspect.signature(mod.f19)
|
||||||
assert stringify_signature(sig) == '(*args: int, **kwargs: str)'
|
assert stringify_signature(sig) == '(*args: int, **kwargs: str)'
|
||||||
|
|
||||||
# default value is inspect.Signature.empty
|
# default value is inspect.Signature.empty
|
||||||
sig = inspect.signature(f21)
|
sig = inspect.signature(mod.f21)
|
||||||
assert stringify_signature(sig) == "(arg1='whatever', arg2)"
|
assert stringify_signature(sig) == "(arg1='whatever', arg2)"
|
||||||
|
|
||||||
# type hints by string
|
# type hints by string
|
||||||
sig = inspect.signature(Node.children)
|
sig = inspect.signature(mod.Node.children)
|
||||||
assert stringify_signature(sig) == '(self) -> typing.List[tests.test_util.typing_test_data.Node]'
|
assert stringify_signature(sig) == '(self) -> typing.List[tests.test_util.typing_test_data.Node]'
|
||||||
|
|
||||||
sig = inspect.signature(Node.__init__)
|
sig = inspect.signature(mod.Node.__init__)
|
||||||
assert stringify_signature(sig) == '(self, parent: tests.test_util.typing_test_data.Node | None) -> None'
|
assert stringify_signature(sig) == '(self, parent: tests.test_util.typing_test_data.Node | None) -> None'
|
||||||
|
|
||||||
# show_annotation is False
|
# show_annotation is False
|
||||||
sig = inspect.signature(f7)
|
sig = inspect.signature(mod.f7)
|
||||||
assert stringify_signature(sig, show_annotation=False) == '(x=None, y={})'
|
assert stringify_signature(sig, show_annotation=False) == '(x=None, y={})'
|
||||||
|
|
||||||
# show_return_annotation is False
|
# show_return_annotation is False
|
||||||
sig = inspect.signature(f7)
|
sig = inspect.signature(mod.f7)
|
||||||
if sys.version_info[:2] <= (3, 10):
|
if sys.version_info[:2] <= (3, 10):
|
||||||
assert stringify_signature(sig, show_return_annotation=False) == '(x: int | None = None, y: dict = {})'
|
assert stringify_signature(sig, show_return_annotation=False) == '(x: int | None = None, y: dict = {})'
|
||||||
else:
|
else:
|
||||||
assert stringify_signature(sig, show_return_annotation=False) == '(x: int = None, y: dict = {})'
|
assert stringify_signature(sig, show_return_annotation=False) == '(x: int = None, y: dict = {})'
|
||||||
|
|
||||||
# unqualified_typehints is True
|
# unqualified_typehints is True
|
||||||
sig = inspect.signature(f7)
|
sig = inspect.signature(mod.f7)
|
||||||
if sys.version_info[:2] <= (3, 10):
|
if sys.version_info[:2] <= (3, 10):
|
||||||
assert stringify_signature(sig, unqualified_typehints=True) == '(x: int | None = None, y: dict = {}) -> None'
|
assert stringify_signature(sig, unqualified_typehints=True) == '(x: int | None = None, y: dict = {}) -> None'
|
||||||
else:
|
else:
|
||||||
assert stringify_signature(sig, unqualified_typehints=True) == '(x: int = None, y: dict = {}) -> None'
|
assert stringify_signature(sig, unqualified_typehints=True) == '(x: int = None, y: dict = {}) -> None'
|
||||||
|
|
||||||
# case: separator at head
|
# case: separator at head
|
||||||
sig = inspect.signature(f22)
|
sig = inspect.signature(mod.f22)
|
||||||
assert stringify_signature(sig) == '(*, a, b)'
|
assert stringify_signature(sig) == '(*, a, b)'
|
||||||
|
|
||||||
# case: separator in the middle
|
# case: separator in the middle
|
||||||
sig = inspect.signature(f23)
|
sig = inspect.signature(mod.f23)
|
||||||
assert stringify_signature(sig) == '(a, b, /, c, d)'
|
assert stringify_signature(sig) == '(a, b, /, c, d)'
|
||||||
|
|
||||||
sig = inspect.signature(f24)
|
sig = inspect.signature(mod.f24)
|
||||||
assert stringify_signature(sig) == '(a, /, *, b)'
|
assert stringify_signature(sig) == '(a, /, *, b)'
|
||||||
|
|
||||||
# case: separator at tail
|
# case: separator at tail
|
||||||
sig = inspect.signature(f25)
|
sig = inspect.signature(mod.f25)
|
||||||
assert stringify_signature(sig) == '(a, b, /)'
|
assert stringify_signature(sig) == '(a, b, /)'
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,61 +1,21 @@
|
|||||||
"""Test inventory util functions."""
|
"""Test inventory util functions."""
|
||||||
import os
|
import os
|
||||||
import posixpath
|
import posixpath
|
||||||
import zlib
|
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
|
|
||||||
import sphinx.locale
|
import sphinx.locale
|
||||||
from sphinx.testing.util import SphinxTestApp
|
from sphinx.testing.util import SphinxTestApp
|
||||||
from sphinx.util.inventory import InventoryFile
|
from sphinx.util.inventory import InventoryFile
|
||||||
|
|
||||||
inventory_v1 = b'''\
|
from tests.test_util.intersphinx_data import (
|
||||||
# Sphinx inventory version 1
|
INVENTORY_V1,
|
||||||
# Project: foo
|
INVENTORY_V2,
|
||||||
# Version: 1.0
|
INVENTORY_V2_NO_VERSION,
|
||||||
module mod foo.html
|
)
|
||||||
module.cls class foo.html
|
|
||||||
'''
|
|
||||||
|
|
||||||
inventory_v2 = b'''\
|
|
||||||
# Sphinx inventory version 2
|
|
||||||
# Project: foo
|
|
||||||
# Version: 2.0
|
|
||||||
# The remainder of this file is compressed with zlib.
|
|
||||||
''' + zlib.compress(b'''\
|
|
||||||
module1 py:module 0 foo.html#module-module1 Long Module desc
|
|
||||||
module2 py:module 0 foo.html#module-$ -
|
|
||||||
module1.func py:function 1 sub/foo.html#$ -
|
|
||||||
module1.Foo.bar py:method 1 index.html#foo.Bar.baz -
|
|
||||||
CFunc c:function 2 cfunc.html#CFunc -
|
|
||||||
std cpp:type 1 index.html#std -
|
|
||||||
std::uint8_t cpp:type 1 index.html#std_uint8_t -
|
|
||||||
foo::Bar cpp:class 1 index.html#cpp_foo_bar -
|
|
||||||
foo::Bar::baz cpp:function 1 index.html#cpp_foo_bar_baz -
|
|
||||||
foons cpp:type 1 index.html#foons -
|
|
||||||
foons::bartype cpp:type 1 index.html#foons_bartype -
|
|
||||||
a term std:term -1 glossary.html#term-a-term -
|
|
||||||
ls.-l std:cmdoption 1 index.html#cmdoption-ls-l -
|
|
||||||
docname std:doc -1 docname.html -
|
|
||||||
foo js:module 1 index.html#foo -
|
|
||||||
foo.bar js:class 1 index.html#foo.bar -
|
|
||||||
foo.bar.baz js:method 1 index.html#foo.bar.baz -
|
|
||||||
foo.bar.qux js:data 1 index.html#foo.bar.qux -
|
|
||||||
a term including:colon std:term -1 glossary.html#term-a-term-including-colon -
|
|
||||||
The-Julia-Domain std:label -1 write_inventory/#$ The Julia Domain
|
|
||||||
''')
|
|
||||||
|
|
||||||
inventory_v2_not_having_version = b'''\
|
|
||||||
# Sphinx inventory version 2
|
|
||||||
# Project: foo
|
|
||||||
# Version:
|
|
||||||
# The remainder of this file is compressed with zlib.
|
|
||||||
''' + zlib.compress(b'''\
|
|
||||||
module1 py:module 0 foo.html#module-module1 Long Module desc
|
|
||||||
''')
|
|
||||||
|
|
||||||
|
|
||||||
def test_read_inventory_v1():
|
def test_read_inventory_v1():
|
||||||
f = BytesIO(inventory_v1)
|
f = BytesIO(INVENTORY_V1)
|
||||||
invdata = InventoryFile.load(f, '/util', posixpath.join)
|
invdata = InventoryFile.load(f, '/util', posixpath.join)
|
||||||
assert invdata['py:module']['module'] == \
|
assert invdata['py:module']['module'] == \
|
||||||
('foo', '1.0', '/util/foo.html#module-module', '-')
|
('foo', '1.0', '/util/foo.html#module-module', '-')
|
||||||
@ -64,7 +24,7 @@ def test_read_inventory_v1():
|
|||||||
|
|
||||||
|
|
||||||
def test_read_inventory_v2():
|
def test_read_inventory_v2():
|
||||||
f = BytesIO(inventory_v2)
|
f = BytesIO(INVENTORY_V2)
|
||||||
invdata = InventoryFile.load(f, '/util', posixpath.join)
|
invdata = InventoryFile.load(f, '/util', posixpath.join)
|
||||||
|
|
||||||
assert len(invdata['py:module']) == 2
|
assert len(invdata['py:module']) == 2
|
||||||
@ -82,7 +42,7 @@ def test_read_inventory_v2():
|
|||||||
|
|
||||||
|
|
||||||
def test_read_inventory_v2_not_having_version():
|
def test_read_inventory_v2_not_having_version():
|
||||||
f = BytesIO(inventory_v2_not_having_version)
|
f = BytesIO(INVENTORY_V2_NO_VERSION)
|
||||||
invdata = InventoryFile.load(f, '/util', posixpath.join)
|
invdata = InventoryFile.load(f, '/util', posixpath.join)
|
||||||
assert invdata['py:module']['module1'] == \
|
assert invdata['py:module']['module1'] == \
|
||||||
('foo', '', '/util/foo.html#module-module1', 'Long Module desc')
|
('foo', '', '/util/foo.html#module-module1', 'Long Module desc')
|
||||||
@ -102,7 +62,7 @@ def _build_inventory(srcdir):
|
|||||||
app = SphinxTestApp(srcdir=srcdir)
|
app = SphinxTestApp(srcdir=srcdir)
|
||||||
app.build()
|
app.build()
|
||||||
sphinx.locale.translators.clear()
|
sphinx.locale.translators.clear()
|
||||||
return (app.outdir / 'objects.inv')
|
return app.outdir / 'objects.inv'
|
||||||
|
|
||||||
|
|
||||||
def test_inventory_localization(tmp_path):
|
def test_inventory_localization(tmp_path):
|
||||||
|
Loading…
Reference in New Issue
Block a user