summaryrefslogtreecommitdiff
path: root/tests/test_domain_py.py
diff options
context:
space:
mode:
Diffstat (limited to 'tests/test_domain_py.py')
-rw-r--r--tests/test_domain_py.py284
1 files changed, 229 insertions, 55 deletions
diff --git a/tests/test_domain_py.py b/tests/test_domain_py.py
index f78c1e9d8..e4bc17004 100644
--- a/tests/test_domain_py.py
+++ b/tests/test_domain_py.py
@@ -8,6 +8,7 @@
:license: BSD, see LICENSE for details.
"""
+import sys
from unittest.mock import Mock
import pytest
@@ -16,11 +17,12 @@ from docutils import nodes
from sphinx import addnodes
from sphinx.addnodes import (
desc, desc_addname, desc_annotation, desc_content, desc_name, desc_optional,
- desc_parameter, desc_parameterlist, desc_returns, desc_signature
+ desc_parameter, desc_parameterlist, desc_returns, desc_signature,
+ desc_sig_name, desc_sig_operator, desc_sig_punctuation, pending_xref,
)
from sphinx.domains import IndexEntry
from sphinx.domains.python import (
- py_sig_re, _pseudo_parse_arglist, PythonDomain, PythonModuleIndex
+ py_sig_re, _parse_annotation, _pseudo_parse_arglist, PythonDomain, PythonModuleIndex
)
from sphinx.testing import restructuredtext
from sphinx.testing.util import assert_node
@@ -76,7 +78,7 @@ def test_domain_py_xrefs(app, status, warning):
assert_node(node, **attributes)
doctree = app.env.get_doctree('roles')
- refnodes = list(doctree.traverse(addnodes.pending_xref))
+ refnodes = list(doctree.traverse(pending_xref))
assert_refnode(refnodes[0], None, None, 'TopLevel', 'class')
assert_refnode(refnodes[1], None, None, 'top_level', 'meth')
assert_refnode(refnodes[2], None, 'NestedParentA', 'child_1', 'meth')
@@ -94,7 +96,7 @@ def test_domain_py_xrefs(app, status, warning):
assert len(refnodes) == 13
doctree = app.env.get_doctree('module')
- refnodes = list(doctree.traverse(addnodes.pending_xref))
+ refnodes = list(doctree.traverse(pending_xref))
assert_refnode(refnodes[0], 'module_a.submodule', None,
'ModTopLevel', 'class')
assert_refnode(refnodes[1], 'module_a.submodule', 'ModTopLevel',
@@ -123,7 +125,7 @@ def test_domain_py_xrefs(app, status, warning):
assert len(refnodes) == 16
doctree = app.env.get_doctree('module_option')
- refnodes = list(doctree.traverse(addnodes.pending_xref))
+ refnodes = list(doctree.traverse(pending_xref))
print(refnodes)
print(refnodes[0])
print(refnodes[1])
@@ -144,36 +146,36 @@ def test_domain_py_objects(app, status, warning):
assert 'module_b.submodule' in modules
assert 'module_b.submodule' in objects
- assert objects['module_a.submodule.ModTopLevel'] == ('module', 'class')
- assert objects['module_a.submodule.ModTopLevel.mod_child_1'] == ('module', 'method')
- assert objects['module_a.submodule.ModTopLevel.mod_child_2'] == ('module', 'method')
+ assert objects['module_a.submodule.ModTopLevel'][2] == 'class'
+ assert objects['module_a.submodule.ModTopLevel.mod_child_1'][2] == 'method'
+ assert objects['module_a.submodule.ModTopLevel.mod_child_2'][2] == 'method'
assert 'ModTopLevel.ModNoModule' not in objects
- assert objects['ModNoModule'] == ('module', 'class')
- assert objects['module_b.submodule.ModTopLevel'] == ('module', 'class')
-
- assert objects['TopLevel'] == ('roles', 'class')
- assert objects['top_level'] == ('roles', 'method')
- assert objects['NestedParentA'] == ('roles', 'class')
- assert objects['NestedParentA.child_1'] == ('roles', 'method')
- assert objects['NestedParentA.any_child'] == ('roles', 'method')
- assert objects['NestedParentA.NestedChildA'] == ('roles', 'class')
- assert objects['NestedParentA.NestedChildA.subchild_1'] == ('roles', 'method')
- assert objects['NestedParentA.NestedChildA.subchild_2'] == ('roles', 'method')
- assert objects['NestedParentA.child_2'] == ('roles', 'method')
- assert objects['NestedParentB'] == ('roles', 'class')
- assert objects['NestedParentB.child_1'] == ('roles', 'method')
+ assert objects['ModNoModule'][2] == 'class'
+ assert objects['module_b.submodule.ModTopLevel'][2] == 'class'
+
+ assert objects['TopLevel'][2] == 'class'
+ assert objects['top_level'][2] == 'method'
+ assert objects['NestedParentA'][2] == 'class'
+ assert objects['NestedParentA.child_1'][2] == 'method'
+ assert objects['NestedParentA.any_child'][2] == 'method'
+ assert objects['NestedParentA.NestedChildA'][2] == 'class'
+ assert objects['NestedParentA.NestedChildA.subchild_1'][2] == 'method'
+ assert objects['NestedParentA.NestedChildA.subchild_2'][2] == 'method'
+ assert objects['NestedParentA.child_2'][2] == 'method'
+ assert objects['NestedParentB'][2] == 'class'
+ assert objects['NestedParentB.child_1'][2] == 'method'
@pytest.mark.sphinx('html', testroot='domain-py')
def test_resolve_xref_for_properties(app, status, warning):
app.builder.build_all()
- content = (app.outdir / 'module.html').text()
- assert ('Link to <a class="reference internal" href="#module_a.submodule.ModTopLevel.prop"'
+ content = (app.outdir / 'module.html').read_text()
+ assert ('Link to <a class="reference internal" href="#module_a.submodule.modtoplevel.prop"'
' title="module_a.submodule.ModTopLevel.prop">'
'<code class="xref py py-attr docutils literal notranslate"><span class="pre">'
'prop</span> <span class="pre">attribute</span></code></a>' in content)
- assert ('Link to <a class="reference internal" href="#module_a.submodule.ModTopLevel.prop"'
+ assert ('Link to <a class="reference internal" href="#module_a.submodule.modtoplevel.prop"'
' title="module_a.submodule.ModTopLevel.prop">'
'<code class="xref py py-meth docutils literal notranslate"><span class="pre">'
'prop</span> <span class="pre">method</span></code></a>' in content)
@@ -190,17 +192,20 @@ def test_domain_py_find_obj(app, status, warning):
assert (find_obj(None, None, 'NONEXISTANT', 'class') == [])
assert (find_obj(None, None, 'NestedParentA', 'class') ==
- [('NestedParentA', ('roles', 'class'))])
+ [('NestedParentA', ('roles', 'nestedparenta', 'class'))])
assert (find_obj(None, None, 'NestedParentA.NestedChildA', 'class') ==
- [('NestedParentA.NestedChildA', ('roles', 'class'))])
+ [('NestedParentA.NestedChildA', ('roles', 'nestedparenta.nestedchilda', 'class'))])
assert (find_obj(None, 'NestedParentA', 'NestedChildA', 'class') ==
- [('NestedParentA.NestedChildA', ('roles', 'class'))])
+ [('NestedParentA.NestedChildA', ('roles', 'nestedparenta.nestedchilda', 'class'))])
assert (find_obj(None, None, 'NestedParentA.NestedChildA.subchild_1', 'meth') ==
- [('NestedParentA.NestedChildA.subchild_1', ('roles', 'method'))])
+ [('NestedParentA.NestedChildA.subchild_1',
+ ('roles', 'nestedparenta.nestedchilda.subchild_1', 'method'))])
assert (find_obj(None, 'NestedParentA', 'NestedChildA.subchild_1', 'meth') ==
- [('NestedParentA.NestedChildA.subchild_1', ('roles', 'method'))])
+ [('NestedParentA.NestedChildA.subchild_1',
+ ('roles', 'nestedparenta.nestedchilda.subchild_1', 'method'))])
assert (find_obj(None, 'NestedParentA.NestedChildA', 'subchild_1', 'meth') ==
- [('NestedParentA.NestedChildA.subchild_1', ('roles', 'method'))])
+ [('NestedParentA.NestedChildA.subchild_1',
+ ('roles', 'nestedparenta.nestedchilda.subchild_1', 'method'))])
def test_get_full_qualified_name():
@@ -231,17 +236,133 @@ def test_get_full_qualified_name():
assert domain.get_full_qualified_name(node) == 'module1.Class.func'
+def test_parse_annotation():
+ doctree = _parse_annotation("int")
+ assert_node(doctree, ([pending_xref, "int"],))
+
+ doctree = _parse_annotation("List[int]")
+ assert_node(doctree, ([pending_xref, "List"],
+ [desc_sig_punctuation, "["],
+ [pending_xref, "int"],
+ [desc_sig_punctuation, "]"]))
+
+ doctree = _parse_annotation("Tuple[int, int]")
+ assert_node(doctree, ([pending_xref, "Tuple"],
+ [desc_sig_punctuation, "["],
+ [pending_xref, "int"],
+ [desc_sig_punctuation, ", "],
+ [pending_xref, "int"],
+ [desc_sig_punctuation, "]"]))
+
+ doctree = _parse_annotation("Callable[[int, int], int]")
+ assert_node(doctree, ([pending_xref, "Callable"],
+ [desc_sig_punctuation, "["],
+ [desc_sig_punctuation, "["],
+ [pending_xref, "int"],
+ [desc_sig_punctuation, ", "],
+ [pending_xref, "int"],
+ [desc_sig_punctuation, "]"],
+ [desc_sig_punctuation, ", "],
+ [pending_xref, "int"],
+ [desc_sig_punctuation, "]"]))
+
+
def test_pyfunction_signature(app):
text = ".. py:function:: hello(name: str) -> str"
doctree = restructuredtext.parse(app, text)
assert_node(doctree, (addnodes.index,
[desc, ([desc_signature, ([desc_name, "hello"],
desc_parameterlist,
- [desc_returns, "str"])],
+ [desc_returns, pending_xref, "str"])],
desc_content)]))
assert_node(doctree[1], addnodes.desc, desctype="function",
domain="py", objtype="function", noindex=False)
- assert_node(doctree[1][0][1], [desc_parameterlist, desc_parameter, "name: str"])
+ assert_node(doctree[1][0][1],
+ [desc_parameterlist, desc_parameter, ([desc_sig_name, "name"],
+ [desc_sig_punctuation, ":"],
+ " ",
+ [nodes.inline, pending_xref, "str"])])
+
+
+def test_pyfunction_signature_full(app):
+ text = (".. py:function:: hello(a: str, b = 1, *args: str, "
+ "c: bool = True, **kwargs: str) -> str")
+ doctree = restructuredtext.parse(app, text)
+ assert_node(doctree, (addnodes.index,
+ [desc, ([desc_signature, ([desc_name, "hello"],
+ desc_parameterlist,
+ [desc_returns, pending_xref, "str"])],
+ desc_content)]))
+ assert_node(doctree[1], addnodes.desc, desctype="function",
+ domain="py", objtype="function", noindex=False)
+ assert_node(doctree[1][0][1],
+ [desc_parameterlist, ([desc_parameter, ([desc_sig_name, "a"],
+ [desc_sig_punctuation, ":"],
+ " ",
+ [desc_sig_name, pending_xref, "str"])],
+ [desc_parameter, ([desc_sig_name, "b"],
+ [desc_sig_operator, "="],
+ [nodes.inline, "1"])],
+ [desc_parameter, ([desc_sig_operator, "*"],
+ [desc_sig_name, "args"],
+ [desc_sig_punctuation, ":"],
+ " ",
+ [desc_sig_name, pending_xref, "str"])],
+ [desc_parameter, ([desc_sig_name, "c"],
+ [desc_sig_punctuation, ":"],
+ " ",
+ [desc_sig_name, pending_xref, "bool"],
+ " ",
+ [desc_sig_operator, "="],
+ " ",
+ [nodes.inline, "True"])],
+ [desc_parameter, ([desc_sig_operator, "**"],
+ [desc_sig_name, "kwargs"],
+ [desc_sig_punctuation, ":"],
+ " ",
+ [desc_sig_name, pending_xref, "str"])])])
+
+
+@pytest.mark.skipif(sys.version_info < (3, 8), reason='python 3.8+ is required.')
+def test_pyfunction_signature_full_py38(app):
+ # case: separator at head
+ text = ".. py:function:: hello(*, a)"
+ doctree = restructuredtext.parse(app, text)
+ assert_node(doctree[1][0][1],
+ [desc_parameterlist, ([desc_parameter, nodes.inline, "*"],
+ [desc_parameter, ([desc_sig_name, "a"],
+ [desc_sig_operator, "="],
+ [nodes.inline, "None"])])])
+
+ # case: separator in the middle
+ text = ".. py:function:: hello(a, /, b, *, c)"
+ doctree = restructuredtext.parse(app, text)
+ assert_node(doctree[1][0][1],
+ [desc_parameterlist, ([desc_parameter, desc_sig_name, "a"],
+ [desc_parameter, desc_sig_operator, "/"],
+ [desc_parameter, desc_sig_name, "b"],
+ [desc_parameter, desc_sig_operator, "*"],
+ [desc_parameter, ([desc_sig_name, "c"],
+ [desc_sig_operator, "="],
+ [nodes.inline, "None"])])])
+
+ # case: separator in the middle (2)
+ text = ".. py:function:: hello(a, /, *, b)"
+ doctree = restructuredtext.parse(app, text)
+ assert_node(doctree[1][0][1],
+ [desc_parameterlist, ([desc_parameter, desc_sig_name, "a"],
+ [desc_parameter, desc_sig_operator, "/"],
+ [desc_parameter, desc_sig_operator, "*"],
+ [desc_parameter, ([desc_sig_name, "b"],
+ [desc_sig_operator, "="],
+ [nodes.inline, "None"])])])
+
+ # case: separator at tail
+ text = ".. py:function:: hello(a, /)"
+ doctree = restructuredtext.parse(app, text)
+ assert_node(doctree[1][0][1],
+ [desc_parameterlist, ([desc_parameter, desc_sig_name, "a"],
+ [desc_parameter, desc_sig_operator, "/"])])
def test_optional_pyfunction_signature(app):
@@ -250,7 +371,7 @@ def test_optional_pyfunction_signature(app):
assert_node(doctree, (addnodes.index,
[desc, ([desc_signature, ([desc_name, "compile"],
desc_parameterlist,
- [desc_returns, "ast object"])],
+ [desc_returns, pending_xref, "ast object"])],
desc_content)]))
assert_node(doctree[1], addnodes.desc, desctype="function",
domain="py", objtype="function", noindex=False)
@@ -335,7 +456,7 @@ def test_pydata(app):
[desc, ([desc_signature, desc_name, "var"],
[desc_content, ()])]))
assert 'var' in domain.objects
- assert domain.objects['var'] == ('index', 'data')
+ assert domain.objects['var'] == ('index', 'var', 'data')
def test_pyfunction(app):
@@ -354,9 +475,9 @@ def test_pyfunction(app):
[desc_parameterlist, ()])],
[desc_content, ()])]))
assert 'func1' in domain.objects
- assert domain.objects['func1'] == ('index', 'function')
+ assert domain.objects['func1'] == ('index', 'func1', 'function')
assert 'func2' in domain.objects
- assert domain.objects['func2'] == ('index', 'function')
+ assert domain.objects['func2'] == ('index', 'func2', 'function')
def test_pymethod_options(app):
@@ -393,61 +514,61 @@ def test_pymethod_options(app):
# method
assert_node(doctree[1][1][0], addnodes.index,
- entries=[('single', 'meth1() (Class method)', 'Class.meth1', '', None)])
+ entries=[('single', 'meth1() (Class method)', 'class.meth1', '', None)])
assert_node(doctree[1][1][1], ([desc_signature, ([desc_name, "meth1"],
[desc_parameterlist, ()])],
[desc_content, ()]))
assert 'Class.meth1' in domain.objects
- assert domain.objects['Class.meth1'] == ('index', 'method')
+ assert domain.objects['Class.meth1'] == ('index', 'class.meth1', 'method')
# :classmethod:
assert_node(doctree[1][1][2], addnodes.index,
- entries=[('single', 'meth2() (Class class method)', 'Class.meth2', '', None)])
+ entries=[('single', 'meth2() (Class class method)', 'class.meth2', '', None)])
assert_node(doctree[1][1][3], ([desc_signature, ([desc_annotation, "classmethod "],
[desc_name, "meth2"],
[desc_parameterlist, ()])],
[desc_content, ()]))
assert 'Class.meth2' in domain.objects
- assert domain.objects['Class.meth2'] == ('index', 'method')
+ assert domain.objects['Class.meth2'] == ('index', 'class.meth2', 'method')
# :staticmethod:
assert_node(doctree[1][1][4], addnodes.index,
- entries=[('single', 'meth3() (Class static method)', 'Class.meth3', '', None)])
+ entries=[('single', 'meth3() (Class static method)', 'class.meth3', '', None)])
assert_node(doctree[1][1][5], ([desc_signature, ([desc_annotation, "static "],
[desc_name, "meth3"],
[desc_parameterlist, ()])],
[desc_content, ()]))
assert 'Class.meth3' in domain.objects
- assert domain.objects['Class.meth3'] == ('index', 'method')
+ assert domain.objects['Class.meth3'] == ('index', 'class.meth3', 'method')
# :async:
assert_node(doctree[1][1][6], addnodes.index,
- entries=[('single', 'meth4() (Class method)', 'Class.meth4', '', None)])
+ entries=[('single', 'meth4() (Class method)', 'class.meth4', '', None)])
assert_node(doctree[1][1][7], ([desc_signature, ([desc_annotation, "async "],
[desc_name, "meth4"],
[desc_parameterlist, ()])],
[desc_content, ()]))
assert 'Class.meth4' in domain.objects
- assert domain.objects['Class.meth4'] == ('index', 'method')
+ assert domain.objects['Class.meth4'] == ('index', 'class.meth4', 'method')
# :property:
assert_node(doctree[1][1][8], addnodes.index,
- entries=[('single', 'meth5() (Class property)', 'Class.meth5', '', None)])
+ entries=[('single', 'meth5() (Class property)', 'class.meth5', '', None)])
assert_node(doctree[1][1][9], ([desc_signature, ([desc_annotation, "property "],
[desc_name, "meth5"])],
[desc_content, ()]))
assert 'Class.meth5' in domain.objects
- assert domain.objects['Class.meth5'] == ('index', 'method')
+ assert domain.objects['Class.meth5'] == ('index', 'class.meth5', 'method')
# :abstractmethod:
assert_node(doctree[1][1][10], addnodes.index,
- entries=[('single', 'meth6() (Class method)', 'Class.meth6', '', None)])
+ entries=[('single', 'meth6() (Class method)', 'class.meth6', '', None)])
assert_node(doctree[1][1][11], ([desc_signature, ([desc_annotation, "abstract "],
[desc_name, "meth6"],
[desc_parameterlist, ()])],
[desc_content, ()]))
assert 'Class.meth6' in domain.objects
- assert domain.objects['Class.meth6'] == ('index', 'method')
+ assert domain.objects['Class.meth6'] == ('index', 'class.meth6', 'method')
def test_pyclassmethod(app):
@@ -462,13 +583,13 @@ def test_pyclassmethod(app):
[desc_content, (addnodes.index,
desc)])]))
assert_node(doctree[1][1][0], addnodes.index,
- entries=[('single', 'meth() (Class class method)', 'Class.meth', '', None)])
+ entries=[('single', 'meth() (Class class method)', 'class.meth', '', None)])
assert_node(doctree[1][1][1], ([desc_signature, ([desc_annotation, "classmethod "],
[desc_name, "meth"],
[desc_parameterlist, ()])],
[desc_content, ()]))
assert 'Class.meth' in domain.objects
- assert domain.objects['Class.meth'] == ('index', 'method')
+ assert domain.objects['Class.meth'] == ('index', 'class.meth', 'method')
def test_pystaticmethod(app):
@@ -483,13 +604,13 @@ def test_pystaticmethod(app):
[desc_content, (addnodes.index,
desc)])]))
assert_node(doctree[1][1][0], addnodes.index,
- entries=[('single', 'meth() (Class static method)', 'Class.meth', '', None)])
+ entries=[('single', 'meth() (Class static method)', 'class.meth', '', None)])
assert_node(doctree[1][1][1], ([desc_signature, ([desc_annotation, "static "],
[desc_name, "meth"],
[desc_parameterlist, ()])],
[desc_content, ()]))
assert 'Class.meth' in domain.objects
- assert domain.objects['Class.meth'] == ('index', 'method')
+ assert domain.objects['Class.meth'] == ('index', 'class.meth', 'method')
def test_pyattribute(app):
@@ -506,13 +627,43 @@ def test_pyattribute(app):
[desc_content, (addnodes.index,
desc)])]))
assert_node(doctree[1][1][0], addnodes.index,
- entries=[('single', 'attr (Class attribute)', 'Class.attr', '', None)])
+ entries=[('single', 'attr (Class attribute)', 'class.attr', '', None)])
assert_node(doctree[1][1][1], ([desc_signature, ([desc_name, "attr"],
[desc_annotation, ": str"],
[desc_annotation, " = ''"])],
[desc_content, ()]))
assert 'Class.attr' in domain.objects
- assert domain.objects['Class.attr'] == ('index', 'attribute')
+ assert domain.objects['Class.attr'] == ('index', 'class.attr', 'attribute')
+
+
+def test_pydecorator_signature(app):
+ text = ".. py:decorator:: deco"
+ domain = app.env.get_domain('py')
+ doctree = restructuredtext.parse(app, text)
+ assert_node(doctree, (addnodes.index,
+ [desc, ([desc_signature, ([desc_addname, "@"],
+ [desc_name, "deco"])],
+ desc_content)]))
+ assert_node(doctree[1], addnodes.desc, desctype="function",
+ domain="py", objtype="function", noindex=False)
+
+ assert 'deco' in domain.objects
+ assert domain.objects['deco'] == ('index', 'deco', 'function')
+
+
+def test_pydecoratormethod_signature(app):
+ text = ".. py:decoratormethod:: deco"
+ domain = app.env.get_domain('py')
+ doctree = restructuredtext.parse(app, text)
+ assert_node(doctree, (addnodes.index,
+ [desc, ([desc_signature, ([desc_addname, "@"],
+ [desc_name, "deco"])],
+ desc_content)]))
+ assert_node(doctree[1], addnodes.desc, desctype="method",
+ domain="py", objtype="method", noindex=False)
+
+ assert 'deco' in domain.objects
+ assert domain.objects['deco'] == ('index', 'deco', 'method')
@pytest.mark.sphinx(freshenv=True)
@@ -559,3 +710,26 @@ def test_module_index_not_collapsed(app):
('s', [IndexEntry('sphinx', 0, 'index', 'module-sphinx', '', '', '')])],
True
)
+
+
+@pytest.mark.sphinx(freshenv=True, confoverrides={'modindex_common_prefix': ['sphinx.']})
+def test_modindex_common_prefix(app):
+ text = (".. py:module:: docutils\n"
+ ".. py:module:: sphinx\n"
+ ".. py:module:: sphinx.config\n"
+ ".. py:module:: sphinx.builders\n"
+ ".. py:module:: sphinx.builders.html\n"
+ ".. py:module:: sphinx_intl\n")
+ restructuredtext.parse(app, text)
+ index = PythonModuleIndex(app.env.get_domain('py'))
+ assert index.generate() == (
+ [('b', [IndexEntry('sphinx.builders', 1, 'index', 'module-sphinx.builders', '', '', ''), # NOQA
+ IndexEntry('sphinx.builders.html', 2, 'index', 'module-sphinx.builders.html', '', '', '')]), # NOQA
+ ('c', [IndexEntry('sphinx.config', 0, 'index', 'module-sphinx.config', '', '', '')]),
+ ('d', [IndexEntry('docutils', 0, 'index', 'module-docutils', '', '', '')]),
+ ('s', [IndexEntry('sphinx', 0, 'index', 'module-sphinx', '', '', ''),
+ IndexEntry('sphinx_intl', 0, 'index', 'module-sphinx_intl', '', '', '')])],
+ True
+ )
+
+