diff --git a/tests/test_domains/test_domain_py.py b/tests/test_domains/test_domain_py.py index 5b6a4bafb..44db3d539 100644 --- a/tests/test_domains/test_domain_py.py +++ b/tests/test_domains/test_domain_py.py @@ -12,11 +12,9 @@ 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, @@ -254,17 +252,6 @@ def test_domain_py_find_obj(app, status, warning): ('roles', 'NestedParentA.NestedChildA.subchild_1', 'method', False))]) -@pytest.mark.sphinx('html', testroot='domain-py', freshenv=True) -def test_domain_py_canonical(app, status, warning): - app.build(force_all=True) - - content = (app.outdir / 'canonical.html').read_text(encoding='utf8') - assert ('' - '' - 'Foo' in content) - assert warning.getvalue() == '' - - def test_get_full_qualified_name(): env = Mock(domaindata={}) domain = PythonDomain(env) @@ -411,929 +398,6 @@ def test_parse_annotation_Literal(app): [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, pending_xref, "str"])], - desc_content)])) - assert_node(doctree[1], addnodes.desc, desctype="function", - domain="py", objtype="function", no_index=False) - assert_node(doctree[1][0][1], - [desc_parameterlist, desc_parameter, ([desc_sig_name, "name"], - [desc_sig_punctuation, ":"], - desc_sig_space, - [nodes.inline, pending_xref, "str"])]) - - -def test_pyfunction_signature_full(app): - text = (".. py:function:: hello(a: str, b = 1, *args: str, " - "c: bool = True, d: tuple = (1, 2), **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", no_index=False) - assert_node(doctree[1][0][1], - [desc_parameterlist, ([desc_parameter, ([desc_sig_name, "a"], - [desc_sig_punctuation, ":"], - desc_sig_space, - [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_space, - [desc_sig_name, pending_xref, "str"])], - [desc_parameter, ([desc_sig_name, "c"], - [desc_sig_punctuation, ":"], - desc_sig_space, - [desc_sig_name, pending_xref, "bool"], - desc_sig_space, - [desc_sig_operator, "="], - desc_sig_space, - [nodes.inline, "True"])], - [desc_parameter, ([desc_sig_name, "d"], - [desc_sig_punctuation, ":"], - desc_sig_space, - [desc_sig_name, pending_xref, "tuple"], - desc_sig_space, - [desc_sig_operator, "="], - desc_sig_space, - [nodes.inline, "(1, 2)"])], - [desc_parameter, ([desc_sig_operator, "**"], - [desc_sig_name, "kwargs"], - [desc_sig_punctuation, ":"], - desc_sig_space, - [desc_sig_name, pending_xref, "str"])])]) - # 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"])]) - - # 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"])]) - - # 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"])]) - - # 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_pyfunction_with_unary_operators(app): - text = ".. py:function:: menu(egg=+1, bacon=-1, sausage=~1, spam=not spam)" - doctree = restructuredtext.parse(app, text) - assert_node(doctree[1][0][1], - [desc_parameterlist, ([desc_parameter, ([desc_sig_name, "egg"], - [desc_sig_operator, "="], - [nodes.inline, "+1"])], - [desc_parameter, ([desc_sig_name, "bacon"], - [desc_sig_operator, "="], - [nodes.inline, "-1"])], - [desc_parameter, ([desc_sig_name, "sausage"], - [desc_sig_operator, "="], - [nodes.inline, "~1"])], - [desc_parameter, ([desc_sig_name, "spam"], - [desc_sig_operator, "="], - [nodes.inline, "not spam"])])]) - - -def test_pyfunction_with_binary_operators(app): - text = ".. py:function:: menu(spam=2**64)" - doctree = restructuredtext.parse(app, text) - assert_node(doctree[1][0][1], - [desc_parameterlist, ([desc_parameter, ([desc_sig_name, "spam"], - [desc_sig_operator, "="], - [nodes.inline, "2**64"])])]) - - -def test_pyfunction_with_number_literals(app): - text = ".. py:function:: hello(age=0x10, height=1_6_0)" - doctree = restructuredtext.parse(app, text) - assert_node(doctree[1][0][1], - [desc_parameterlist, ([desc_parameter, ([desc_sig_name, "age"], - [desc_sig_operator, "="], - [nodes.inline, "0x10"])], - [desc_parameter, ([desc_sig_name, "height"], - [desc_sig_operator, "="], - [nodes.inline, "1_6_0"])])]) - - -def test_pyfunction_with_union_type_operator(app): - text = ".. py:function:: hello(age: int | None)" - doctree = restructuredtext.parse(app, text) - assert_node(doctree[1][0][1], - [desc_parameterlist, ([desc_parameter, ([desc_sig_name, "age"], - [desc_sig_punctuation, ":"], - desc_sig_space, - [desc_sig_name, ([pending_xref, "int"], - desc_sig_space, - [desc_sig_punctuation, "|"], - desc_sig_space, - [pending_xref, "None"])])])]) - - -def test_optional_pyfunction_signature(app): - text = ".. py:function:: compile(source [, filename [, symbol]]) -> ast object" - doctree = restructuredtext.parse(app, text) - assert_node(doctree, (addnodes.index, - [desc, ([desc_signature, ([desc_name, "compile"], - desc_parameterlist, - [desc_returns, pending_xref, "ast object"])], - desc_content)])) - assert_node(doctree[1], addnodes.desc, desctype="function", - domain="py", objtype="function", no_index=False) - assert_node(doctree[1][0][1], - ([desc_parameter, ([desc_sig_name, "source"])], - [desc_optional, ([desc_parameter, ([desc_sig_name, "filename"])], - [desc_optional, desc_parameter, ([desc_sig_name, "symbol"])])])) - - -def test_pyexception_signature(app): - text = ".. py:exception:: builtins.IOError" - doctree = restructuredtext.parse(app, text) - assert_node(doctree, (addnodes.index, - [desc, ([desc_signature, ([desc_annotation, ('exception', desc_sig_space)], - [desc_addname, "builtins."], - [desc_name, "IOError"])], - desc_content)])) - assert_node(doctree[1], desc, desctype="exception", - domain="py", objtype="exception", no_index=False) - - -def test_pydata_signature(app): - text = (".. py:data:: version\n" - " :type: int\n" - " :value: 1\n") - doctree = restructuredtext.parse(app, text) - assert_node(doctree, (addnodes.index, - [desc, ([desc_signature, ([desc_name, "version"], - [desc_annotation, ([desc_sig_punctuation, ':'], - desc_sig_space, - [pending_xref, "int"])], - [desc_annotation, ( - desc_sig_space, - [desc_sig_punctuation, '='], - desc_sig_space, - "1")], - )], - desc_content)])) - assert_node(doctree[1], addnodes.desc, desctype="data", - domain="py", objtype="data", no_index=False) - - -def test_pydata_signature_old(app): - text = (".. py:data:: version\n" - " :annotation: = 1\n") - doctree = restructuredtext.parse(app, text) - assert_node(doctree, (addnodes.index, - [desc, ([desc_signature, ([desc_name, "version"], - [desc_annotation, (desc_sig_space, - "= 1")])], - desc_content)])) - assert_node(doctree[1], addnodes.desc, desctype="data", - domain="py", objtype="data", no_index=False) - - -def test_pydata_with_union_type_operator(app): - text = (".. py:data:: version\n" - " :type: int | str") - doctree = restructuredtext.parse(app, text) - assert_node(doctree[1][0], - ([desc_name, "version"], - [desc_annotation, ([desc_sig_punctuation, ':'], - desc_sig_space, - [pending_xref, "int"], - desc_sig_space, - [desc_sig_punctuation, "|"], - desc_sig_space, - [pending_xref, "str"])])) - - -def test_pyobject_prefix(app): - text = (".. py:class:: Foo\n" - "\n" - " .. py:method:: Foo.say\n" - " .. py:method:: FooBar.say") - doctree = restructuredtext.parse(app, text) - assert_node(doctree, (addnodes.index, - [desc, ([desc_signature, ([desc_annotation, ('class', desc_sig_space)], - [desc_name, "Foo"])], - [desc_content, (addnodes.index, - desc, - addnodes.index, - desc)])])) - assert doctree[1][1][1].astext().strip() == 'say()' # prefix is stripped - assert doctree[1][1][3].astext().strip() == 'FooBar.say()' # not stripped - - -def test_pydata(app): - text = (".. py:module:: example\n" - ".. py:data:: var\n" - " :type: int\n") - domain = app.env.get_domain('py') - doctree = restructuredtext.parse(app, text) - assert_node(doctree, (addnodes.index, - addnodes.index, - nodes.target, - [desc, ([desc_signature, ([desc_addname, "example."], - [desc_name, "var"], - [desc_annotation, ([desc_sig_punctuation, ':'], - desc_sig_space, - [pending_xref, "int"])])], - [desc_content, ()])])) - assert_node(doctree[3][0][2][2], pending_xref, **{"py:module": "example"}) - assert 'example.var' in domain.objects - assert domain.objects['example.var'] == ('index', 'example.var', 'data', False) - - -def test_pyfunction(app): - text = (".. py:function:: func1\n" - ".. py:module:: example\n" - ".. py:function:: func2\n" - " :async:\n") - domain = app.env.get_domain('py') - doctree = restructuredtext.parse(app, text) - assert_node(doctree, (addnodes.index, - [desc, ([desc_signature, ([desc_name, "func1"], - [desc_parameterlist, ()])], - [desc_content, ()])], - addnodes.index, - addnodes.index, - nodes.target, - [desc, ([desc_signature, ([desc_annotation, ([desc_sig_keyword, 'async'], - desc_sig_space)], - [desc_addname, "example."], - [desc_name, "func2"], - [desc_parameterlist, ()])], - [desc_content, ()])])) - assert_node(doctree[0], addnodes.index, - entries=[('pair', 'built-in function; func1()', 'func1', '', None)]) - assert_node(doctree[2], addnodes.index, - entries=[('pair', 'module; example', 'module-example', '', None)]) - assert_node(doctree[3], addnodes.index, - entries=[('single', 'func2() (in module example)', 'example.func2', '', None)]) - - assert 'func1' in domain.objects - assert domain.objects['func1'] == ('index', 'func1', 'function', False) - assert 'example.func2' in domain.objects - assert domain.objects['example.func2'] == ('index', 'example.func2', 'function', False) - - -def test_pyclass_options(app): - text = (".. py:class:: Class1\n" - ".. py:class:: Class2\n" - " :final:\n") - domain = app.env.get_domain('py') - doctree = restructuredtext.parse(app, text) - assert_node(doctree, (addnodes.index, - [desc, ([desc_signature, ([desc_annotation, ("class", desc_sig_space)], - [desc_name, "Class1"])], - [desc_content, ()])], - addnodes.index, - [desc, ([desc_signature, ([desc_annotation, ("final", - desc_sig_space, - "class", - desc_sig_space)], - [desc_name, "Class2"])], - [desc_content, ()])])) - - # class - assert_node(doctree[0], addnodes.index, - entries=[('single', 'Class1 (built-in class)', 'Class1', '', None)]) - assert 'Class1' in domain.objects - assert domain.objects['Class1'] == ('index', 'Class1', 'class', False) - - # :final: - assert_node(doctree[2], addnodes.index, - entries=[('single', 'Class2 (built-in class)', 'Class2', '', None)]) - assert 'Class2' in domain.objects - assert domain.objects['Class2'] == ('index', 'Class2', 'class', False) - - -def test_pymethod_options(app): - text = (".. py:class:: Class\n" - "\n" - " .. py:method:: meth1\n" - " .. py:method:: meth2\n" - " :classmethod:\n" - " .. py:method:: meth3\n" - " :staticmethod:\n" - " .. py:method:: meth4\n" - " :async:\n" - " .. py:method:: meth5\n" - " :abstractmethod:\n" - " .. py:method:: meth6\n" - " :final:\n") - domain = app.env.get_domain('py') - doctree = restructuredtext.parse(app, text) - assert_node(doctree, (addnodes.index, - [desc, ([desc_signature, ([desc_annotation, ("class", desc_sig_space)], - [desc_name, "Class"])], - [desc_content, (addnodes.index, - desc, - addnodes.index, - desc, - addnodes.index, - desc, - addnodes.index, - desc, - addnodes.index, - desc, - addnodes.index, - desc)])])) - - # method - assert_node(doctree[1][1][0], addnodes.index, - entries=[('single', 'meth1() (Class method)', 'Class.meth1', '', None)]) - assert_node(doctree[1][1][1], ([desc_signature, ([desc_name, "meth1"], - [desc_parameterlist, ()])], - [desc_content, ()])) - assert 'Class.meth1' in domain.objects - assert domain.objects['Class.meth1'] == ('index', 'Class.meth1', 'method', False) - - # :classmethod: - assert_node(doctree[1][1][2], addnodes.index, - entries=[('single', 'meth2() (Class class method)', 'Class.meth2', '', None)]) - assert_node(doctree[1][1][3], ([desc_signature, ([desc_annotation, ("classmethod", desc_sig_space)], - [desc_name, "meth2"], - [desc_parameterlist, ()])], - [desc_content, ()])) - assert 'Class.meth2' in domain.objects - assert domain.objects['Class.meth2'] == ('index', 'Class.meth2', 'method', False) - - # :staticmethod: - assert_node(doctree[1][1][4], addnodes.index, - entries=[('single', 'meth3() (Class static method)', 'Class.meth3', '', None)]) - assert_node(doctree[1][1][5], ([desc_signature, ([desc_annotation, ("static", desc_sig_space)], - [desc_name, "meth3"], - [desc_parameterlist, ()])], - [desc_content, ()])) - assert 'Class.meth3' in domain.objects - assert domain.objects['Class.meth3'] == ('index', 'Class.meth3', 'method', False) - - # :async: - assert_node(doctree[1][1][6], addnodes.index, - entries=[('single', 'meth4() (Class method)', 'Class.meth4', '', None)]) - assert_node(doctree[1][1][7], ([desc_signature, ([desc_annotation, ("async", desc_sig_space)], - [desc_name, "meth4"], - [desc_parameterlist, ()])], - [desc_content, ()])) - assert 'Class.meth4' in domain.objects - assert domain.objects['Class.meth4'] == ('index', 'Class.meth4', 'method', False) - - # :abstractmethod: - assert_node(doctree[1][1][8], addnodes.index, - entries=[('single', 'meth5() (Class method)', 'Class.meth5', '', None)]) - assert_node(doctree[1][1][9], ([desc_signature, ([desc_annotation, ("abstract", desc_sig_space)], - [desc_name, "meth5"], - [desc_parameterlist, ()])], - [desc_content, ()])) - assert 'Class.meth5' in domain.objects - assert domain.objects['Class.meth5'] == ('index', 'Class.meth5', 'method', False) - - # :final: - assert_node(doctree[1][1][10], addnodes.index, - entries=[('single', 'meth6() (Class method)', 'Class.meth6', '', None)]) - assert_node(doctree[1][1][11], ([desc_signature, ([desc_annotation, ("final", desc_sig_space)], - [desc_name, "meth6"], - [desc_parameterlist, ()])], - [desc_content, ()])) - assert 'Class.meth6' in domain.objects - assert domain.objects['Class.meth6'] == ('index', 'Class.meth6', 'method', False) - - -def test_pyclassmethod(app): - text = (".. py:class:: Class\n" - "\n" - " .. py:classmethod:: meth\n") - domain = app.env.get_domain('py') - doctree = restructuredtext.parse(app, text) - assert_node(doctree, (addnodes.index, - [desc, ([desc_signature, ([desc_annotation, ("class", desc_sig_space)], - [desc_name, "Class"])], - [desc_content, (addnodes.index, - desc)])])) - assert_node(doctree[1][1][0], addnodes.index, - entries=[('single', 'meth() (Class class method)', 'Class.meth', '', None)]) - assert_node(doctree[1][1][1], ([desc_signature, ([desc_annotation, ("classmethod", desc_sig_space)], - [desc_name, "meth"], - [desc_parameterlist, ()])], - [desc_content, ()])) - assert 'Class.meth' in domain.objects - assert domain.objects['Class.meth'] == ('index', 'Class.meth', 'method', False) - - -def test_pystaticmethod(app): - text = (".. py:class:: Class\n" - "\n" - " .. py:staticmethod:: meth\n") - domain = app.env.get_domain('py') - doctree = restructuredtext.parse(app, text) - assert_node(doctree, (addnodes.index, - [desc, ([desc_signature, ([desc_annotation, ("class", desc_sig_space)], - [desc_name, "Class"])], - [desc_content, (addnodes.index, - desc)])])) - assert_node(doctree[1][1][0], addnodes.index, - entries=[('single', 'meth() (Class static method)', 'Class.meth', '', None)]) - assert_node(doctree[1][1][1], ([desc_signature, ([desc_annotation, ("static", desc_sig_space)], - [desc_name, "meth"], - [desc_parameterlist, ()])], - [desc_content, ()])) - assert 'Class.meth' in domain.objects - assert domain.objects['Class.meth'] == ('index', 'Class.meth', 'method', False) - - -def test_pyattribute(app): - text = (".. py:class:: Class\n" - "\n" - " .. py:attribute:: attr\n" - " :type: Optional[str]\n" - " :value: ''\n") - domain = app.env.get_domain('py') - doctree = restructuredtext.parse(app, text) - assert_node(doctree, (addnodes.index, - [desc, ([desc_signature, ([desc_annotation, ("class", desc_sig_space)], - [desc_name, "Class"])], - [desc_content, (addnodes.index, - desc)])])) - assert_node(doctree[1][1][0], addnodes.index, - entries=[('single', 'attr (Class attribute)', 'Class.attr', '', None)]) - assert_node(doctree[1][1][1], ([desc_signature, ([desc_name, "attr"], - [desc_annotation, ([desc_sig_punctuation, ':'], - desc_sig_space, - [pending_xref, "str"], - desc_sig_space, - [desc_sig_punctuation, "|"], - desc_sig_space, - [pending_xref, "None"])], - [desc_annotation, (desc_sig_space, - [desc_sig_punctuation, '='], - desc_sig_space, - "''")], - )], - [desc_content, ()])) - assert_node(doctree[1][1][1][0][1][2], pending_xref, **{"py:class": "Class"}) - assert_node(doctree[1][1][1][0][1][6], pending_xref, **{"py:class": "Class"}) - assert 'Class.attr' in domain.objects - assert domain.objects['Class.attr'] == ('index', 'Class.attr', 'attribute', False) - - -def test_pyproperty(app): - text = (".. py:class:: Class\n" - "\n" - " .. py:property:: prop1\n" - " :abstractmethod:\n" - " :type: str\n" - "\n" - " .. py:property:: prop2\n" - " :classmethod:\n" - " :type: str\n") - domain = app.env.get_domain('py') - doctree = restructuredtext.parse(app, text) - assert_node(doctree, (addnodes.index, - [desc, ([desc_signature, ([desc_annotation, ("class", desc_sig_space)], - [desc_name, "Class"])], - [desc_content, (addnodes.index, - desc, - addnodes.index, - desc)])])) - assert_node(doctree[1][1][0], addnodes.index, - entries=[('single', 'prop1 (Class property)', 'Class.prop1', '', None)]) - assert_node(doctree[1][1][1], ([desc_signature, ([desc_annotation, ("abstract", desc_sig_space, - "property", desc_sig_space)], - [desc_name, "prop1"], - [desc_annotation, ([desc_sig_punctuation, ':'], - desc_sig_space, - [pending_xref, "str"])])], - [desc_content, ()])) - assert_node(doctree[1][1][2], addnodes.index, - entries=[('single', 'prop2 (Class property)', 'Class.prop2', '', None)]) - assert_node(doctree[1][1][3], ([desc_signature, ([desc_annotation, ("class", desc_sig_space, - "property", desc_sig_space)], - [desc_name, "prop2"], - [desc_annotation, ([desc_sig_punctuation, ':'], - desc_sig_space, - [pending_xref, "str"])])], - [desc_content, ()])) - assert 'Class.prop1' in domain.objects - assert domain.objects['Class.prop1'] == ('index', 'Class.prop1', 'property', False) - assert 'Class.prop2' in domain.objects - assert domain.objects['Class.prop2'] == ('index', 'Class.prop2', 'property', False) - - -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", no_index=False) - - assert 'deco' in domain.objects - assert domain.objects['deco'] == ('index', 'deco', 'function', False) - - -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", no_index=False) - - assert 'deco' in domain.objects - assert domain.objects['deco'] == ('index', 'deco', 'method', False) - - -def test_canonical(app): - text = (".. py:class:: io.StringIO\n" - " :canonical: _io.StringIO") - domain = app.env.get_domain('py') - doctree = restructuredtext.parse(app, text) - assert_node(doctree, (addnodes.index, - [desc, ([desc_signature, ([desc_annotation, ("class", desc_sig_space)], - [desc_addname, "io."], - [desc_name, "StringIO"])], - desc_content)])) - assert 'io.StringIO' in domain.objects - assert domain.objects['io.StringIO'] == ('index', 'io.StringIO', 'class', False) - assert domain.objects['_io.StringIO'] == ('index', 'io.StringIO', 'class', True) - - -def test_canonical_definition_overrides(app, warning): - text = (".. py:class:: io.StringIO\n" - " :canonical: _io.StringIO\n" - ".. py:class:: _io.StringIO\n") - restructuredtext.parse(app, text) - assert warning.getvalue() == "" - - domain = app.env.get_domain('py') - assert domain.objects['_io.StringIO'] == ('index', 'id0', 'class', False) - - -def test_canonical_definition_skip(app, warning): - text = (".. py:class:: _io.StringIO\n" - ".. py:class:: io.StringIO\n" - " :canonical: _io.StringIO\n") - - restructuredtext.parse(app, text) - assert warning.getvalue() == "" - - domain = app.env.get_domain('py') - assert domain.objects['_io.StringIO'] == ('index', 'io.StringIO', 'class', False) - - -def test_canonical_duplicated(app, warning): - text = (".. py:class:: mypackage.StringIO\n" - " :canonical: _io.StringIO\n" - ".. py:class:: io.StringIO\n" - " :canonical: _io.StringIO\n") - - restructuredtext.parse(app, text) - assert warning.getvalue() != "" - - -def test_info_field_list(app): - text = (".. py:module:: example\n" - ".. py:class:: Class\n" - "\n" - " :meta blah: this meta-field must not show up in the toc-tree\n" - " :param str name: blah blah\n" - " :meta another meta field:\n" - " :param age: blah blah\n" - " :type age: int\n" - " :param items: blah blah\n" - " :type items: Tuple[str, ...]\n" - " :param Dict[str, str] params: blah blah\n") - doctree = restructuredtext.parse(app, text) - print(doctree) - - assert_node(doctree, (addnodes.index, - addnodes.index, - nodes.target, - [desc, ([desc_signature, ([desc_annotation, ("class", desc_sig_space)], - [desc_addname, "example."], - [desc_name, "Class"])], - [desc_content, nodes.field_list, nodes.field])])) - assert_node(doctree[3][1][0][0], - ([nodes.field_name, "Parameters"], - [nodes.field_body, nodes.bullet_list, ([nodes.list_item, nodes.paragraph], - [nodes.list_item, nodes.paragraph], - [nodes.list_item, nodes.paragraph], - [nodes.list_item, nodes.paragraph])])) - - # :param str name: - assert_node(doctree[3][1][0][0][1][0][0][0], - ([addnodes.literal_strong, "name"], - " (", - [pending_xref, addnodes.literal_emphasis, "str"], - ")", - " -- ", - "blah blah")) - assert_node(doctree[3][1][0][0][1][0][0][0][2], pending_xref, - refdomain="py", reftype="class", reftarget="str", - **{"py:module": "example", "py:class": "Class"}) - - # :param age: + :type age: - assert_node(doctree[3][1][0][0][1][0][1][0], - ([addnodes.literal_strong, "age"], - " (", - [pending_xref, addnodes.literal_emphasis, "int"], - ")", - " -- ", - "blah blah")) - assert_node(doctree[3][1][0][0][1][0][1][0][2], pending_xref, - refdomain="py", reftype="class", reftarget="int", - **{"py:module": "example", "py:class": "Class"}) - - # :param items: + :type items: - assert_node(doctree[3][1][0][0][1][0][2][0], - ([addnodes.literal_strong, "items"], - " (", - [pending_xref, addnodes.literal_emphasis, "Tuple"], - [addnodes.literal_emphasis, "["], - [pending_xref, addnodes.literal_emphasis, "str"], - [addnodes.literal_emphasis, ", "], - [addnodes.literal_emphasis, "..."], - [addnodes.literal_emphasis, "]"], - ")", - " -- ", - "blah blah")) - assert_node(doctree[3][1][0][0][1][0][2][0][2], pending_xref, - refdomain="py", reftype="class", reftarget="Tuple", - **{"py:module": "example", "py:class": "Class"}) - assert_node(doctree[3][1][0][0][1][0][2][0][4], pending_xref, - refdomain="py", reftype="class", reftarget="str", - **{"py:module": "example", "py:class": "Class"}) - - # :param Dict[str, str] params: - assert_node(doctree[3][1][0][0][1][0][3][0], - ([addnodes.literal_strong, "params"], - " (", - [pending_xref, addnodes.literal_emphasis, "Dict"], - [addnodes.literal_emphasis, "["], - [pending_xref, addnodes.literal_emphasis, "str"], - [addnodes.literal_emphasis, ", "], - [pending_xref, addnodes.literal_emphasis, "str"], - [addnodes.literal_emphasis, "]"], - ")", - " -- ", - "blah blah")) - assert_node(doctree[3][1][0][0][1][0][3][0][2], pending_xref, - refdomain="py", reftype="class", reftarget="Dict", - **{"py:module": "example", "py:class": "Class"}) - assert_node(doctree[3][1][0][0][1][0][3][0][4], pending_xref, - refdomain="py", reftype="class", reftarget="str", - **{"py:module": "example", "py:class": "Class"}) - assert_node(doctree[3][1][0][0][1][0][3][0][6], pending_xref, - refdomain="py", reftype="class", reftarget="str", - **{"py:module": "example", "py:class": "Class"}) - - -def test_info_field_list_piped_type(app): - text = (".. py:module:: example\n" - ".. py:class:: Class\n" - "\n" - " :param age: blah blah\n" - " :type age: int | str\n") - doctree = restructuredtext.parse(app, text) - - assert_node(doctree, - (addnodes.index, - addnodes.index, - nodes.target, - [desc, ([desc_signature, ([desc_annotation, ("class", desc_sig_space)], - [desc_addname, "example."], - [desc_name, "Class"])], - [desc_content, nodes.field_list, nodes.field, (nodes.field_name, - nodes.field_body)])])) - assert_node(doctree[3][1][0][0][1], - ([nodes.paragraph, ([addnodes.literal_strong, "age"], - " (", - [pending_xref, addnodes.literal_emphasis, "int"], - [addnodes.literal_emphasis, " | "], - [pending_xref, addnodes.literal_emphasis, "str"], - ")", - " -- ", - "blah blah")],)) - assert_node(doctree[3][1][0][0][1][0][2], pending_xref, - refdomain="py", reftype="class", reftarget="int", - **{"py:module": "example", "py:class": "Class"}) - assert_node(doctree[3][1][0][0][1][0][4], pending_xref, - refdomain="py", reftype="class", reftarget="str", - **{"py:module": "example", "py:class": "Class"}) - - -def test_info_field_list_Literal(app): - text = (".. py:module:: example\n" - ".. py:class:: Class\n" - "\n" - " :param age: blah blah\n" - " :type age: Literal['foo', 'bar', 'baz']\n") - doctree = restructuredtext.parse(app, text) - - assert_node(doctree, - (addnodes.index, - addnodes.index, - nodes.target, - [desc, ([desc_signature, ([desc_annotation, ("class", desc_sig_space)], - [desc_addname, "example."], - [desc_name, "Class"])], - [desc_content, nodes.field_list, nodes.field, (nodes.field_name, - nodes.field_body)])])) - assert_node(doctree[3][1][0][0][1], - ([nodes.paragraph, ([addnodes.literal_strong, "age"], - " (", - [pending_xref, addnodes.literal_emphasis, "Literal"], - [addnodes.literal_emphasis, "["], - [addnodes.literal_emphasis, "'foo'"], - [addnodes.literal_emphasis, ", "], - [addnodes.literal_emphasis, "'bar'"], - [addnodes.literal_emphasis, ", "], - [addnodes.literal_emphasis, "'baz'"], - [addnodes.literal_emphasis, "]"], - ")", - " -- ", - "blah blah")],)) - assert_node(doctree[3][1][0][0][1][0][2], pending_xref, - refdomain="py", reftype="class", reftarget="Literal", - **{"py:module": "example", "py:class": "Class"}) - - -def test_info_field_list_var(app): - text = (".. py:class:: Class\n" - "\n" - " :var int attr: blah blah\n") - doctree = restructuredtext.parse(app, text) - - assert_node(doctree, (addnodes.index, - [desc, (desc_signature, - [desc_content, nodes.field_list, nodes.field])])) - assert_node(doctree[1][1][0][0], ([nodes.field_name, "Variables"], - [nodes.field_body, nodes.paragraph])) - - # :var int attr: - assert_node(doctree[1][1][0][0][1][0], - ([addnodes.literal_strong, "attr"], - " (", - [pending_xref, addnodes.literal_emphasis, "int"], - ")", - " -- ", - "blah blah")) - assert_node(doctree[1][1][0][0][1][0][2], pending_xref, - refdomain="py", reftype="class", reftarget="int", **{"py:class": "Class"}) - - -def test_info_field_list_napoleon_deliminator_of(app): - text = (".. py:module:: example\n" - ".. py:class:: Class\n" - "\n" - " :param list_str_var: example description.\n" - " :type list_str_var: list of str\n" - " :param tuple_int_var: example description.\n" - " :type tuple_int_var: tuple of tuple of int\n" - ) - doctree = restructuredtext.parse(app, text) - - # :param list of str list_str_var: - assert_node(doctree[3][1][0][0][1][0][0][0], - ([addnodes.literal_strong, "list_str_var"], - " (", - [pending_xref, addnodes.literal_emphasis, "list"], - [addnodes.literal_emphasis, " of "], - [pending_xref, addnodes.literal_emphasis, "str"], - ")", - " -- ", - "example description.")) - - # :param tuple of tuple of int tuple_int_var: - assert_node(doctree[3][1][0][0][1][0][1][0], - ([addnodes.literal_strong, "tuple_int_var"], - " (", - [pending_xref, addnodes.literal_emphasis, "tuple"], - [addnodes.literal_emphasis, " of "], - [pending_xref, addnodes.literal_emphasis, "tuple"], - [addnodes.literal_emphasis, " of "], - [pending_xref, addnodes.literal_emphasis, "int"], - ")", - " -- ", - "example description.")) - - -def test_info_field_list_napoleon_deliminator_or(app): - text = (".. py:module:: example\n" - ".. py:class:: Class\n" - "\n" - " :param bool_str_var: example description.\n" - " :type bool_str_var: bool or str\n" - " :param str_float_int_var: example description.\n" - " :type str_float_int_var: str or float or int\n" - ) - doctree = restructuredtext.parse(app, text) - - # :param bool or str bool_str_var: - assert_node(doctree[3][1][0][0][1][0][0][0], - ([addnodes.literal_strong, "bool_str_var"], - " (", - [pending_xref, addnodes.literal_emphasis, "bool"], - [addnodes.literal_emphasis, " or "], - [pending_xref, addnodes.literal_emphasis, "str"], - ")", - " -- ", - "example description.")) - - # :param str or float or int str_float_int_var: - assert_node(doctree[3][1][0][0][1][0][1][0], - ([addnodes.literal_strong, "str_float_int_var"], - " (", - [pending_xref, addnodes.literal_emphasis, "str"], - [addnodes.literal_emphasis, " or "], - [pending_xref, addnodes.literal_emphasis, "float"], - [addnodes.literal_emphasis, " or "], - [pending_xref, addnodes.literal_emphasis, "int"], - ")", - " -- ", - "example description.")) - - -def test_type_field(app): - text = (".. py:data:: var1\n" - " :type: .int\n" - ".. py:data:: var2\n" - " :type: ~builtins.int\n" - ".. py:data:: var3\n" - " :type: typing.Optional[typing.Tuple[int, typing.Any]]\n") - doctree = restructuredtext.parse(app, text) - assert_node(doctree, (addnodes.index, - [desc, ([desc_signature, ([desc_name, "var1"], - [desc_annotation, ([desc_sig_punctuation, ':'], - desc_sig_space, - [pending_xref, "int"])])], - [desc_content, ()])], - addnodes.index, - [desc, ([desc_signature, ([desc_name, "var2"], - [desc_annotation, ([desc_sig_punctuation, ':'], - desc_sig_space, - [pending_xref, "int"])])], - [desc_content, ()])], - addnodes.index, - [desc, ([desc_signature, ([desc_name, "var3"], - [desc_annotation, ([desc_sig_punctuation, ":"], - desc_sig_space, - [pending_xref, "Optional"], - [desc_sig_punctuation, "["], - [pending_xref, "Tuple"], - [desc_sig_punctuation, "["], - [pending_xref, "int"], - [desc_sig_punctuation, ","], - desc_sig_space, - [pending_xref, "Any"], - [desc_sig_punctuation, "]"], - [desc_sig_punctuation, "]"])])], - [desc_content, ()])])) - assert_node(doctree[1][0][1][2], pending_xref, reftarget='int', refspecific=True) - assert_node(doctree[3][0][1][2], pending_xref, reftarget='builtins.int', refspecific=False) - assert_node(doctree[5][0][1][2], pending_xref, reftarget='typing.Optional', refspecific=False) - assert_node(doctree[5][0][1][4], pending_xref, reftarget='typing.Tuple', refspecific=False) - assert_node(doctree[5][0][1][6], pending_xref, reftarget='int', refspecific=False) - assert_node(doctree[5][0][1][9], pending_xref, reftarget='typing.Any', refspecific=False) - - @pytest.mark.sphinx(freshenv=True) def test_module_index(app): text = (".. py:module:: docutils\n" @@ -1465,176 +529,6 @@ def test_signature_line_number(app, include_options): assert line == 1 -@pytest.mark.sphinx('html', confoverrides={ - 'python_maximum_signature_line_length': len("hello(name: str) -> str"), -}) -def test_pyfunction_signature_with_python_maximum_signature_line_length_equal(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, pending_xref, "str"], - )], - desc_content, - )], - )) - assert_node(doctree[1], addnodes.desc, desctype="function", - domain="py", objtype="function", no_index=False) - assert_node(doctree[1][0][1], [desc_parameterlist, desc_parameter, ( - [desc_sig_name, "name"], - [desc_sig_punctuation, ":"], - desc_sig_space, - [nodes.inline, pending_xref, "str"], - )]) - assert_node(doctree[1][0][1], desc_parameterlist, multi_line_parameter_list=False) - - -@pytest.mark.sphinx('html', confoverrides={ - 'python_maximum_signature_line_length': len("hello(name: str) -> str"), -}) -def test_pyfunction_signature_with_python_maximum_signature_line_length_force_single(app): - text = (".. py:function:: hello(names: str) -> str\n" - " :single-line-parameter-list:") - 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", no_index=False) - assert_node(doctree[1][0][1], [desc_parameterlist, desc_parameter, ( - [desc_sig_name, "names"], - [desc_sig_punctuation, ":"], - desc_sig_space, - [nodes.inline, pending_xref, "str"], - )]) - assert_node(doctree[1][0][1], desc_parameterlist, multi_line_parameter_list=False) - - -@pytest.mark.sphinx('html', confoverrides={ - 'python_maximum_signature_line_length': len("hello(name: str) -> str"), -}) -def test_pyfunction_signature_with_python_maximum_signature_line_length_break(app): - text = ".. py:function:: hello(names: 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", no_index=False) - assert_node(doctree[1][0][1], [desc_parameterlist, desc_parameter, ( - [desc_sig_name, "names"], - [desc_sig_punctuation, ":"], - desc_sig_space, - [nodes.inline, pending_xref, "str"], - )]) - assert_node(doctree[1][0][1], desc_parameterlist, multi_line_parameter_list=True) - - -@pytest.mark.sphinx('html', confoverrides={ - 'maximum_signature_line_length': len("hello(name: str) -> str"), -}) -def test_pyfunction_signature_with_maximum_signature_line_length_equal(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, pending_xref, "str"], - )], - desc_content, - )], - )) - assert_node(doctree[1], addnodes.desc, desctype="function", - domain="py", objtype="function", no_index=False) - assert_node(doctree[1][0][1], [desc_parameterlist, desc_parameter, ( - [desc_sig_name, "name"], - [desc_sig_punctuation, ":"], - desc_sig_space, - [nodes.inline, pending_xref, "str"], - )]) - assert_node(doctree[1][0][1], desc_parameterlist, multi_line_parameter_list=False) - - -@pytest.mark.sphinx('html', confoverrides={ - 'maximum_signature_line_length': len("hello(name: str) -> str"), -}) -def test_pyfunction_signature_with_maximum_signature_line_length_force_single(app): - text = (".. py:function:: hello(names: str) -> str\n" - " :single-line-parameter-list:") - 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", no_index=False) - assert_node(doctree[1][0][1], [desc_parameterlist, desc_parameter, ( - [desc_sig_name, "names"], - [desc_sig_punctuation, ":"], - desc_sig_space, - [nodes.inline, pending_xref, "str"], - )]) - assert_node(doctree[1][0][1], desc_parameterlist, multi_line_parameter_list=False) - - -@pytest.mark.sphinx('html', confoverrides={ - 'maximum_signature_line_length': len("hello(name: str) -> str"), -}) -def test_pyfunction_signature_with_maximum_signature_line_length_break(app): - text = ".. py:function:: hello(names: 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", no_index=False) - assert_node(doctree[1][0][1], [desc_parameterlist, desc_parameter, ( - [desc_sig_name, "names"], - [desc_sig_punctuation, ":"], - desc_sig_space, - [nodes.inline, pending_xref, "str"], - )]) - assert_node(doctree[1][0][1], desc_parameterlist, multi_line_parameter_list=True) - - @pytest.mark.sphinx( 'html', confoverrides={ diff --git a/tests/test_domains/test_domain_py_canonical.py b/tests/test_domains/test_domain_py_canonical.py new file mode 100644 index 000000000..3635cd1ff --- /dev/null +++ b/tests/test_domains/test_domain_py_canonical.py @@ -0,0 +1,77 @@ +"""Tests the Python Domain""" + +from __future__ import annotations + +import pytest + +from sphinx import addnodes +from sphinx.addnodes import ( + desc, + desc_addname, + desc_annotation, + desc_content, + desc_name, + desc_sig_space, + desc_signature, +) +from sphinx.testing import restructuredtext +from sphinx.testing.util import assert_node + + +@pytest.mark.sphinx('html', testroot='domain-py', freshenv=True) +def test_domain_py_canonical(app, status, warning): + app.build(force_all=True) + + content = (app.outdir / 'canonical.html').read_text(encoding='utf8') + assert ('' + '' + 'Foo' in content) + assert warning.getvalue() == '' + + +def test_canonical(app): + text = (".. py:class:: io.StringIO\n" + " :canonical: _io.StringIO") + domain = app.env.get_domain('py') + doctree = restructuredtext.parse(app, text) + assert_node(doctree, (addnodes.index, + [desc, ([desc_signature, ([desc_annotation, ("class", desc_sig_space)], + [desc_addname, "io."], + [desc_name, "StringIO"])], + desc_content)])) + assert 'io.StringIO' in domain.objects + assert domain.objects['io.StringIO'] == ('index', 'io.StringIO', 'class', False) + assert domain.objects['_io.StringIO'] == ('index', 'io.StringIO', 'class', True) + + +def test_canonical_definition_overrides(app, warning): + text = (".. py:class:: io.StringIO\n" + " :canonical: _io.StringIO\n" + ".. py:class:: _io.StringIO\n") + restructuredtext.parse(app, text) + assert warning.getvalue() == "" + + domain = app.env.get_domain('py') + assert domain.objects['_io.StringIO'] == ('index', 'id0', 'class', False) + + +def test_canonical_definition_skip(app, warning): + text = (".. py:class:: _io.StringIO\n" + ".. py:class:: io.StringIO\n" + " :canonical: _io.StringIO\n") + + restructuredtext.parse(app, text) + assert warning.getvalue() == "" + + domain = app.env.get_domain('py') + assert domain.objects['_io.StringIO'] == ('index', 'io.StringIO', 'class', False) + + +def test_canonical_duplicated(app, warning): + text = (".. py:class:: mypackage.StringIO\n" + " :canonical: _io.StringIO\n" + ".. py:class:: io.StringIO\n" + " :canonical: _io.StringIO\n") + + restructuredtext.parse(app, text) + assert warning.getvalue() != "" diff --git a/tests/test_domains/test_domain_py_fields.py b/tests/test_domains/test_domain_py_fields.py new file mode 100644 index 000000000..47c40f591 --- /dev/null +++ b/tests/test_domains/test_domain_py_fields.py @@ -0,0 +1,326 @@ +"""Tests the Python Domain""" + +from __future__ import annotations + +from docutils import nodes + +from sphinx import addnodes +from sphinx.addnodes import ( + desc, + desc_addname, + desc_annotation, + desc_content, + desc_name, + desc_sig_punctuation, + desc_sig_space, + desc_signature, + pending_xref, +) +from sphinx.testing import restructuredtext +from sphinx.testing.util import assert_node + + +def test_info_field_list(app): + text = (".. py:module:: example\n" + ".. py:class:: Class\n" + "\n" + " :meta blah: this meta-field must not show up in the toc-tree\n" + " :param str name: blah blah\n" + " :meta another meta field:\n" + " :param age: blah blah\n" + " :type age: int\n" + " :param items: blah blah\n" + " :type items: Tuple[str, ...]\n" + " :param Dict[str, str] params: blah blah\n") + doctree = restructuredtext.parse(app, text) + print(doctree) + + assert_node(doctree, (addnodes.index, + addnodes.index, + nodes.target, + [desc, ([desc_signature, ([desc_annotation, ("class", desc_sig_space)], + [desc_addname, "example."], + [desc_name, "Class"])], + [desc_content, nodes.field_list, nodes.field])])) + assert_node(doctree[3][1][0][0], + ([nodes.field_name, "Parameters"], + [nodes.field_body, nodes.bullet_list, ([nodes.list_item, nodes.paragraph], + [nodes.list_item, nodes.paragraph], + [nodes.list_item, nodes.paragraph], + [nodes.list_item, nodes.paragraph])])) + + # :param str name: + assert_node(doctree[3][1][0][0][1][0][0][0], + ([addnodes.literal_strong, "name"], + " (", + [pending_xref, addnodes.literal_emphasis, "str"], + ")", + " -- ", + "blah blah")) + assert_node(doctree[3][1][0][0][1][0][0][0][2], pending_xref, + refdomain="py", reftype="class", reftarget="str", + **{"py:module": "example", "py:class": "Class"}) + + # :param age: + :type age: + assert_node(doctree[3][1][0][0][1][0][1][0], + ([addnodes.literal_strong, "age"], + " (", + [pending_xref, addnodes.literal_emphasis, "int"], + ")", + " -- ", + "blah blah")) + assert_node(doctree[3][1][0][0][1][0][1][0][2], pending_xref, + refdomain="py", reftype="class", reftarget="int", + **{"py:module": "example", "py:class": "Class"}) + + # :param items: + :type items: + assert_node(doctree[3][1][0][0][1][0][2][0], + ([addnodes.literal_strong, "items"], + " (", + [pending_xref, addnodes.literal_emphasis, "Tuple"], + [addnodes.literal_emphasis, "["], + [pending_xref, addnodes.literal_emphasis, "str"], + [addnodes.literal_emphasis, ", "], + [addnodes.literal_emphasis, "..."], + [addnodes.literal_emphasis, "]"], + ")", + " -- ", + "blah blah")) + assert_node(doctree[3][1][0][0][1][0][2][0][2], pending_xref, + refdomain="py", reftype="class", reftarget="Tuple", + **{"py:module": "example", "py:class": "Class"}) + assert_node(doctree[3][1][0][0][1][0][2][0][4], pending_xref, + refdomain="py", reftype="class", reftarget="str", + **{"py:module": "example", "py:class": "Class"}) + + # :param Dict[str, str] params: + assert_node(doctree[3][1][0][0][1][0][3][0], + ([addnodes.literal_strong, "params"], + " (", + [pending_xref, addnodes.literal_emphasis, "Dict"], + [addnodes.literal_emphasis, "["], + [pending_xref, addnodes.literal_emphasis, "str"], + [addnodes.literal_emphasis, ", "], + [pending_xref, addnodes.literal_emphasis, "str"], + [addnodes.literal_emphasis, "]"], + ")", + " -- ", + "blah blah")) + assert_node(doctree[3][1][0][0][1][0][3][0][2], pending_xref, + refdomain="py", reftype="class", reftarget="Dict", + **{"py:module": "example", "py:class": "Class"}) + assert_node(doctree[3][1][0][0][1][0][3][0][4], pending_xref, + refdomain="py", reftype="class", reftarget="str", + **{"py:module": "example", "py:class": "Class"}) + assert_node(doctree[3][1][0][0][1][0][3][0][6], pending_xref, + refdomain="py", reftype="class", reftarget="str", + **{"py:module": "example", "py:class": "Class"}) + + +def test_info_field_list_piped_type(app): + text = (".. py:module:: example\n" + ".. py:class:: Class\n" + "\n" + " :param age: blah blah\n" + " :type age: int | str\n") + doctree = restructuredtext.parse(app, text) + + assert_node(doctree, + (addnodes.index, + addnodes.index, + nodes.target, + [desc, ([desc_signature, ([desc_annotation, ("class", desc_sig_space)], + [desc_addname, "example."], + [desc_name, "Class"])], + [desc_content, nodes.field_list, nodes.field, (nodes.field_name, + nodes.field_body)])])) + assert_node(doctree[3][1][0][0][1], + ([nodes.paragraph, ([addnodes.literal_strong, "age"], + " (", + [pending_xref, addnodes.literal_emphasis, "int"], + [addnodes.literal_emphasis, " | "], + [pending_xref, addnodes.literal_emphasis, "str"], + ")", + " -- ", + "blah blah")],)) + assert_node(doctree[3][1][0][0][1][0][2], pending_xref, + refdomain="py", reftype="class", reftarget="int", + **{"py:module": "example", "py:class": "Class"}) + assert_node(doctree[3][1][0][0][1][0][4], pending_xref, + refdomain="py", reftype="class", reftarget="str", + **{"py:module": "example", "py:class": "Class"}) + + +def test_info_field_list_Literal(app): + text = (".. py:module:: example\n" + ".. py:class:: Class\n" + "\n" + " :param age: blah blah\n" + " :type age: Literal['foo', 'bar', 'baz']\n") + doctree = restructuredtext.parse(app, text) + + assert_node(doctree, + (addnodes.index, + addnodes.index, + nodes.target, + [desc, ([desc_signature, ([desc_annotation, ("class", desc_sig_space)], + [desc_addname, "example."], + [desc_name, "Class"])], + [desc_content, nodes.field_list, nodes.field, (nodes.field_name, + nodes.field_body)])])) + assert_node(doctree[3][1][0][0][1], + ([nodes.paragraph, ([addnodes.literal_strong, "age"], + " (", + [pending_xref, addnodes.literal_emphasis, "Literal"], + [addnodes.literal_emphasis, "["], + [addnodes.literal_emphasis, "'foo'"], + [addnodes.literal_emphasis, ", "], + [addnodes.literal_emphasis, "'bar'"], + [addnodes.literal_emphasis, ", "], + [addnodes.literal_emphasis, "'baz'"], + [addnodes.literal_emphasis, "]"], + ")", + " -- ", + "blah blah")],)) + assert_node(doctree[3][1][0][0][1][0][2], pending_xref, + refdomain="py", reftype="class", reftarget="Literal", + **{"py:module": "example", "py:class": "Class"}) + + +def test_info_field_list_var(app): + text = (".. py:class:: Class\n" + "\n" + " :var int attr: blah blah\n") + doctree = restructuredtext.parse(app, text) + + assert_node(doctree, (addnodes.index, + [desc, (desc_signature, + [desc_content, nodes.field_list, nodes.field])])) + assert_node(doctree[1][1][0][0], ([nodes.field_name, "Variables"], + [nodes.field_body, nodes.paragraph])) + + # :var int attr: + assert_node(doctree[1][1][0][0][1][0], + ([addnodes.literal_strong, "attr"], + " (", + [pending_xref, addnodes.literal_emphasis, "int"], + ")", + " -- ", + "blah blah")) + assert_node(doctree[1][1][0][0][1][0][2], pending_xref, + refdomain="py", reftype="class", reftarget="int", **{"py:class": "Class"}) + + +def test_info_field_list_napoleon_deliminator_of(app): + text = (".. py:module:: example\n" + ".. py:class:: Class\n" + "\n" + " :param list_str_var: example description.\n" + " :type list_str_var: list of str\n" + " :param tuple_int_var: example description.\n" + " :type tuple_int_var: tuple of tuple of int\n" + ) + doctree = restructuredtext.parse(app, text) + + # :param list of str list_str_var: + assert_node(doctree[3][1][0][0][1][0][0][0], + ([addnodes.literal_strong, "list_str_var"], + " (", + [pending_xref, addnodes.literal_emphasis, "list"], + [addnodes.literal_emphasis, " of "], + [pending_xref, addnodes.literal_emphasis, "str"], + ")", + " -- ", + "example description.")) + + # :param tuple of tuple of int tuple_int_var: + assert_node(doctree[3][1][0][0][1][0][1][0], + ([addnodes.literal_strong, "tuple_int_var"], + " (", + [pending_xref, addnodes.literal_emphasis, "tuple"], + [addnodes.literal_emphasis, " of "], + [pending_xref, addnodes.literal_emphasis, "tuple"], + [addnodes.literal_emphasis, " of "], + [pending_xref, addnodes.literal_emphasis, "int"], + ")", + " -- ", + "example description.")) + + +def test_info_field_list_napoleon_deliminator_or(app): + text = (".. py:module:: example\n" + ".. py:class:: Class\n" + "\n" + " :param bool_str_var: example description.\n" + " :type bool_str_var: bool or str\n" + " :param str_float_int_var: example description.\n" + " :type str_float_int_var: str or float or int\n" + ) + doctree = restructuredtext.parse(app, text) + + # :param bool or str bool_str_var: + assert_node(doctree[3][1][0][0][1][0][0][0], + ([addnodes.literal_strong, "bool_str_var"], + " (", + [pending_xref, addnodes.literal_emphasis, "bool"], + [addnodes.literal_emphasis, " or "], + [pending_xref, addnodes.literal_emphasis, "str"], + ")", + " -- ", + "example description.")) + + # :param str or float or int str_float_int_var: + assert_node(doctree[3][1][0][0][1][0][1][0], + ([addnodes.literal_strong, "str_float_int_var"], + " (", + [pending_xref, addnodes.literal_emphasis, "str"], + [addnodes.literal_emphasis, " or "], + [pending_xref, addnodes.literal_emphasis, "float"], + [addnodes.literal_emphasis, " or "], + [pending_xref, addnodes.literal_emphasis, "int"], + ")", + " -- ", + "example description.")) + + +def test_type_field(app): + text = (".. py:data:: var1\n" + " :type: .int\n" + ".. py:data:: var2\n" + " :type: ~builtins.int\n" + ".. py:data:: var3\n" + " :type: typing.Optional[typing.Tuple[int, typing.Any]]\n") + doctree = restructuredtext.parse(app, text) + assert_node(doctree, (addnodes.index, + [desc, ([desc_signature, ([desc_name, "var1"], + [desc_annotation, ([desc_sig_punctuation, ':'], + desc_sig_space, + [pending_xref, "int"])])], + [desc_content, ()])], + addnodes.index, + [desc, ([desc_signature, ([desc_name, "var2"], + [desc_annotation, ([desc_sig_punctuation, ':'], + desc_sig_space, + [pending_xref, "int"])])], + [desc_content, ()])], + addnodes.index, + [desc, ([desc_signature, ([desc_name, "var3"], + [desc_annotation, ([desc_sig_punctuation, ":"], + desc_sig_space, + [pending_xref, "Optional"], + [desc_sig_punctuation, "["], + [pending_xref, "Tuple"], + [desc_sig_punctuation, "["], + [pending_xref, "int"], + [desc_sig_punctuation, ","], + desc_sig_space, + [pending_xref, "Any"], + [desc_sig_punctuation, "]"], + [desc_sig_punctuation, "]"])])], + [desc_content, ()])])) + assert_node(doctree[1][0][1][2], pending_xref, reftarget='int', refspecific=True) + assert_node(doctree[3][0][1][2], pending_xref, reftarget='builtins.int', refspecific=False) + assert_node(doctree[5][0][1][2], pending_xref, reftarget='typing.Optional', refspecific=False) + assert_node(doctree[5][0][1][4], pending_xref, reftarget='typing.Tuple', refspecific=False) + assert_node(doctree[5][0][1][6], pending_xref, reftarget='int', refspecific=False) + assert_node(doctree[5][0][1][9], pending_xref, reftarget='typing.Any', refspecific=False) diff --git a/tests/test_domains/test_domain_py_pyfunction.py b/tests/test_domains/test_domain_py_pyfunction.py new file mode 100644 index 000000000..187b9197f --- /dev/null +++ b/tests/test_domains/test_domain_py_pyfunction.py @@ -0,0 +1,396 @@ +"""Tests the Python Domain""" + +from __future__ import annotations + +import pytest +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_sig_keyword, + desc_sig_name, + desc_sig_operator, + desc_sig_punctuation, + desc_sig_space, + desc_signature, + pending_xref, +) +from sphinx.testing import restructuredtext +from sphinx.testing.util import assert_node + + +def test_pyfunction(app): + text = (".. py:function:: func1\n" + ".. py:module:: example\n" + ".. py:function:: func2\n" + " :async:\n") + domain = app.env.get_domain('py') + doctree = restructuredtext.parse(app, text) + assert_node(doctree, (addnodes.index, + [desc, ([desc_signature, ([desc_name, "func1"], + [desc_parameterlist, ()])], + [desc_content, ()])], + addnodes.index, + addnodes.index, + nodes.target, + [desc, ([desc_signature, ([desc_annotation, ([desc_sig_keyword, 'async'], + desc_sig_space)], + [desc_addname, "example."], + [desc_name, "func2"], + [desc_parameterlist, ()])], + [desc_content, ()])])) + assert_node(doctree[0], addnodes.index, + entries=[('pair', 'built-in function; func1()', 'func1', '', None)]) + assert_node(doctree[2], addnodes.index, + entries=[('pair', 'module; example', 'module-example', '', None)]) + assert_node(doctree[3], addnodes.index, + entries=[('single', 'func2() (in module example)', 'example.func2', '', None)]) + + assert 'func1' in domain.objects + assert domain.objects['func1'] == ('index', 'func1', 'function', False) + assert 'example.func2' in domain.objects + assert domain.objects['example.func2'] == ('index', 'example.func2', 'function', False) + + +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, pending_xref, "str"])], + desc_content)])) + assert_node(doctree[1], addnodes.desc, desctype="function", + domain="py", objtype="function", no_index=False) + assert_node(doctree[1][0][1], + [desc_parameterlist, desc_parameter, ([desc_sig_name, "name"], + [desc_sig_punctuation, ":"], + desc_sig_space, + [nodes.inline, pending_xref, "str"])]) + + +def test_pyfunction_signature_full(app): + text = (".. py:function:: hello(a: str, b = 1, *args: str, " + "c: bool = True, d: tuple = (1, 2), **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", no_index=False) + assert_node(doctree[1][0][1], + [desc_parameterlist, ([desc_parameter, ([desc_sig_name, "a"], + [desc_sig_punctuation, ":"], + desc_sig_space, + [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_space, + [desc_sig_name, pending_xref, "str"])], + [desc_parameter, ([desc_sig_name, "c"], + [desc_sig_punctuation, ":"], + desc_sig_space, + [desc_sig_name, pending_xref, "bool"], + desc_sig_space, + [desc_sig_operator, "="], + desc_sig_space, + [nodes.inline, "True"])], + [desc_parameter, ([desc_sig_name, "d"], + [desc_sig_punctuation, ":"], + desc_sig_space, + [desc_sig_name, pending_xref, "tuple"], + desc_sig_space, + [desc_sig_operator, "="], + desc_sig_space, + [nodes.inline, "(1, 2)"])], + [desc_parameter, ([desc_sig_operator, "**"], + [desc_sig_name, "kwargs"], + [desc_sig_punctuation, ":"], + desc_sig_space, + [desc_sig_name, pending_xref, "str"])])]) + # 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"])]) + + # 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"])]) + + # 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"])]) + + # 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_pyfunction_with_unary_operators(app): + text = ".. py:function:: menu(egg=+1, bacon=-1, sausage=~1, spam=not spam)" + doctree = restructuredtext.parse(app, text) + assert_node(doctree[1][0][1], + [desc_parameterlist, ([desc_parameter, ([desc_sig_name, "egg"], + [desc_sig_operator, "="], + [nodes.inline, "+1"])], + [desc_parameter, ([desc_sig_name, "bacon"], + [desc_sig_operator, "="], + [nodes.inline, "-1"])], + [desc_parameter, ([desc_sig_name, "sausage"], + [desc_sig_operator, "="], + [nodes.inline, "~1"])], + [desc_parameter, ([desc_sig_name, "spam"], + [desc_sig_operator, "="], + [nodes.inline, "not spam"])])]) + + +def test_pyfunction_with_binary_operators(app): + text = ".. py:function:: menu(spam=2**64)" + doctree = restructuredtext.parse(app, text) + assert_node(doctree[1][0][1], + [desc_parameterlist, ([desc_parameter, ([desc_sig_name, "spam"], + [desc_sig_operator, "="], + [nodes.inline, "2**64"])])]) + + +def test_pyfunction_with_number_literals(app): + text = ".. py:function:: hello(age=0x10, height=1_6_0)" + doctree = restructuredtext.parse(app, text) + assert_node(doctree[1][0][1], + [desc_parameterlist, ([desc_parameter, ([desc_sig_name, "age"], + [desc_sig_operator, "="], + [nodes.inline, "0x10"])], + [desc_parameter, ([desc_sig_name, "height"], + [desc_sig_operator, "="], + [nodes.inline, "1_6_0"])])]) + + +def test_pyfunction_with_union_type_operator(app): + text = ".. py:function:: hello(age: int | None)" + doctree = restructuredtext.parse(app, text) + assert_node(doctree[1][0][1], + [desc_parameterlist, ([desc_parameter, ([desc_sig_name, "age"], + [desc_sig_punctuation, ":"], + desc_sig_space, + [desc_sig_name, ([pending_xref, "int"], + desc_sig_space, + [desc_sig_punctuation, "|"], + desc_sig_space, + [pending_xref, "None"])])])]) + + +def test_optional_pyfunction_signature(app): + text = ".. py:function:: compile(source [, filename [, symbol]]) -> ast object" + doctree = restructuredtext.parse(app, text) + assert_node(doctree, (addnodes.index, + [desc, ([desc_signature, ([desc_name, "compile"], + desc_parameterlist, + [desc_returns, pending_xref, "ast object"])], + desc_content)])) + assert_node(doctree[1], addnodes.desc, desctype="function", + domain="py", objtype="function", no_index=False) + assert_node(doctree[1][0][1], + ([desc_parameter, ([desc_sig_name, "source"])], + [desc_optional, ([desc_parameter, ([desc_sig_name, "filename"])], + [desc_optional, desc_parameter, ([desc_sig_name, "symbol"])])])) + + +@pytest.mark.sphinx('html', confoverrides={ + 'python_maximum_signature_line_length': len("hello(name: str) -> str"), +}) +def test_pyfunction_signature_with_python_maximum_signature_line_length_equal(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, pending_xref, "str"], + )], + desc_content, + )], + )) + assert_node(doctree[1], addnodes.desc, desctype="function", + domain="py", objtype="function", no_index=False) + assert_node(doctree[1][0][1], [desc_parameterlist, desc_parameter, ( + [desc_sig_name, "name"], + [desc_sig_punctuation, ":"], + desc_sig_space, + [nodes.inline, pending_xref, "str"], + )]) + assert_node(doctree[1][0][1], desc_parameterlist, multi_line_parameter_list=False) + + +@pytest.mark.sphinx('html', confoverrides={ + 'python_maximum_signature_line_length': len("hello(name: str) -> str"), +}) +def test_pyfunction_signature_with_python_maximum_signature_line_length_force_single(app): + text = (".. py:function:: hello(names: str) -> str\n" + " :single-line-parameter-list:") + 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", no_index=False) + assert_node(doctree[1][0][1], [desc_parameterlist, desc_parameter, ( + [desc_sig_name, "names"], + [desc_sig_punctuation, ":"], + desc_sig_space, + [nodes.inline, pending_xref, "str"], + )]) + assert_node(doctree[1][0][1], desc_parameterlist, multi_line_parameter_list=False) + + +@pytest.mark.sphinx('html', confoverrides={ + 'python_maximum_signature_line_length': len("hello(name: str) -> str"), +}) +def test_pyfunction_signature_with_python_maximum_signature_line_length_break(app): + text = ".. py:function:: hello(names: 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", no_index=False) + assert_node(doctree[1][0][1], [desc_parameterlist, desc_parameter, ( + [desc_sig_name, "names"], + [desc_sig_punctuation, ":"], + desc_sig_space, + [nodes.inline, pending_xref, "str"], + )]) + assert_node(doctree[1][0][1], desc_parameterlist, multi_line_parameter_list=True) + + +@pytest.mark.sphinx('html', confoverrides={ + 'maximum_signature_line_length': len("hello(name: str) -> str"), +}) +def test_pyfunction_signature_with_maximum_signature_line_length_equal(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, pending_xref, "str"], + )], + desc_content, + )], + )) + assert_node(doctree[1], addnodes.desc, desctype="function", + domain="py", objtype="function", no_index=False) + assert_node(doctree[1][0][1], [desc_parameterlist, desc_parameter, ( + [desc_sig_name, "name"], + [desc_sig_punctuation, ":"], + desc_sig_space, + [nodes.inline, pending_xref, "str"], + )]) + assert_node(doctree[1][0][1], desc_parameterlist, multi_line_parameter_list=False) + + +@pytest.mark.sphinx('html', confoverrides={ + 'maximum_signature_line_length': len("hello(name: str) -> str"), +}) +def test_pyfunction_signature_with_maximum_signature_line_length_force_single(app): + text = (".. py:function:: hello(names: str) -> str\n" + " :single-line-parameter-list:") + 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", no_index=False) + assert_node(doctree[1][0][1], [desc_parameterlist, desc_parameter, ( + [desc_sig_name, "names"], + [desc_sig_punctuation, ":"], + desc_sig_space, + [nodes.inline, pending_xref, "str"], + )]) + assert_node(doctree[1][0][1], desc_parameterlist, multi_line_parameter_list=False) + + +@pytest.mark.sphinx('html', confoverrides={ + 'maximum_signature_line_length': len("hello(name: str) -> str"), +}) +def test_pyfunction_signature_with_maximum_signature_line_length_break(app): + text = ".. py:function:: hello(names: 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", no_index=False) + assert_node(doctree[1][0][1], [desc_parameterlist, desc_parameter, ( + [desc_sig_name, "names"], + [desc_sig_punctuation, ":"], + desc_sig_space, + [nodes.inline, pending_xref, "str"], + )]) + assert_node(doctree[1][0][1], desc_parameterlist, multi_line_parameter_list=True) diff --git a/tests/test_domains/test_domain_py_pyobject.py b/tests/test_domains/test_domain_py_pyobject.py new file mode 100644 index 000000000..d68f09ded --- /dev/null +++ b/tests/test_domains/test_domain_py_pyobject.py @@ -0,0 +1,394 @@ +"""Tests the Python Domain""" + +from __future__ import annotations + +from docutils import nodes + +from sphinx import addnodes +from sphinx.addnodes import ( + desc, + desc_addname, + desc_annotation, + desc_content, + desc_name, + desc_parameterlist, + desc_sig_punctuation, + desc_sig_space, + desc_signature, + pending_xref, +) +from sphinx.testing import restructuredtext +from sphinx.testing.util import assert_node + + +def test_pyexception_signature(app): + text = ".. py:exception:: builtins.IOError" + doctree = restructuredtext.parse(app, text) + assert_node(doctree, (addnodes.index, + [desc, ([desc_signature, ([desc_annotation, ('exception', desc_sig_space)], + [desc_addname, "builtins."], + [desc_name, "IOError"])], + desc_content)])) + assert_node(doctree[1], desc, desctype="exception", + domain="py", objtype="exception", no_index=False) + + +def test_pydata_signature(app): + text = (".. py:data:: version\n" + " :type: int\n" + " :value: 1\n") + doctree = restructuredtext.parse(app, text) + assert_node(doctree, (addnodes.index, + [desc, ([desc_signature, ([desc_name, "version"], + [desc_annotation, ([desc_sig_punctuation, ':'], + desc_sig_space, + [pending_xref, "int"])], + [desc_annotation, ( + desc_sig_space, + [desc_sig_punctuation, '='], + desc_sig_space, + "1")], + )], + desc_content)])) + assert_node(doctree[1], addnodes.desc, desctype="data", + domain="py", objtype="data", no_index=False) + + +def test_pydata_signature_old(app): + text = (".. py:data:: version\n" + " :annotation: = 1\n") + doctree = restructuredtext.parse(app, text) + assert_node(doctree, (addnodes.index, + [desc, ([desc_signature, ([desc_name, "version"], + [desc_annotation, (desc_sig_space, + "= 1")])], + desc_content)])) + assert_node(doctree[1], addnodes.desc, desctype="data", + domain="py", objtype="data", no_index=False) + + +def test_pydata_with_union_type_operator(app): + text = (".. py:data:: version\n" + " :type: int | str") + doctree = restructuredtext.parse(app, text) + assert_node(doctree[1][0], + ([desc_name, "version"], + [desc_annotation, ([desc_sig_punctuation, ':'], + desc_sig_space, + [pending_xref, "int"], + desc_sig_space, + [desc_sig_punctuation, "|"], + desc_sig_space, + [pending_xref, "str"])])) + + +def test_pyobject_prefix(app): + text = (".. py:class:: Foo\n" + "\n" + " .. py:method:: Foo.say\n" + " .. py:method:: FooBar.say") + doctree = restructuredtext.parse(app, text) + assert_node(doctree, (addnodes.index, + [desc, ([desc_signature, ([desc_annotation, ('class', desc_sig_space)], + [desc_name, "Foo"])], + [desc_content, (addnodes.index, + desc, + addnodes.index, + desc)])])) + assert doctree[1][1][1].astext().strip() == 'say()' # prefix is stripped + assert doctree[1][1][3].astext().strip() == 'FooBar.say()' # not stripped + + +def test_pydata(app): + text = (".. py:module:: example\n" + ".. py:data:: var\n" + " :type: int\n") + domain = app.env.get_domain('py') + doctree = restructuredtext.parse(app, text) + assert_node(doctree, (addnodes.index, + addnodes.index, + nodes.target, + [desc, ([desc_signature, ([desc_addname, "example."], + [desc_name, "var"], + [desc_annotation, ([desc_sig_punctuation, ':'], + desc_sig_space, + [pending_xref, "int"])])], + [desc_content, ()])])) + assert_node(doctree[3][0][2][2], pending_xref, **{"py:module": "example"}) + assert 'example.var' in domain.objects + assert domain.objects['example.var'] == ('index', 'example.var', 'data', False) + + +def test_pyclass_options(app): + text = (".. py:class:: Class1\n" + ".. py:class:: Class2\n" + " :final:\n") + domain = app.env.get_domain('py') + doctree = restructuredtext.parse(app, text) + assert_node(doctree, (addnodes.index, + [desc, ([desc_signature, ([desc_annotation, ("class", desc_sig_space)], + [desc_name, "Class1"])], + [desc_content, ()])], + addnodes.index, + [desc, ([desc_signature, ([desc_annotation, ("final", + desc_sig_space, + "class", + desc_sig_space)], + [desc_name, "Class2"])], + [desc_content, ()])])) + + # class + assert_node(doctree[0], addnodes.index, + entries=[('single', 'Class1 (built-in class)', 'Class1', '', None)]) + assert 'Class1' in domain.objects + assert domain.objects['Class1'] == ('index', 'Class1', 'class', False) + + # :final: + assert_node(doctree[2], addnodes.index, + entries=[('single', 'Class2 (built-in class)', 'Class2', '', None)]) + assert 'Class2' in domain.objects + assert domain.objects['Class2'] == ('index', 'Class2', 'class', False) + + +def test_pymethod_options(app): + text = (".. py:class:: Class\n" + "\n" + " .. py:method:: meth1\n" + " .. py:method:: meth2\n" + " :classmethod:\n" + " .. py:method:: meth3\n" + " :staticmethod:\n" + " .. py:method:: meth4\n" + " :async:\n" + " .. py:method:: meth5\n" + " :abstractmethod:\n" + " .. py:method:: meth6\n" + " :final:\n") + domain = app.env.get_domain('py') + doctree = restructuredtext.parse(app, text) + assert_node(doctree, (addnodes.index, + [desc, ([desc_signature, ([desc_annotation, ("class", desc_sig_space)], + [desc_name, "Class"])], + [desc_content, (addnodes.index, + desc, + addnodes.index, + desc, + addnodes.index, + desc, + addnodes.index, + desc, + addnodes.index, + desc, + addnodes.index, + desc)])])) + + # method + assert_node(doctree[1][1][0], addnodes.index, + entries=[('single', 'meth1() (Class method)', 'Class.meth1', '', None)]) + assert_node(doctree[1][1][1], ([desc_signature, ([desc_name, "meth1"], + [desc_parameterlist, ()])], + [desc_content, ()])) + assert 'Class.meth1' in domain.objects + assert domain.objects['Class.meth1'] == ('index', 'Class.meth1', 'method', False) + + # :classmethod: + assert_node(doctree[1][1][2], addnodes.index, + entries=[('single', 'meth2() (Class class method)', 'Class.meth2', '', None)]) + assert_node(doctree[1][1][3], ([desc_signature, ([desc_annotation, ("classmethod", desc_sig_space)], + [desc_name, "meth2"], + [desc_parameterlist, ()])], + [desc_content, ()])) + assert 'Class.meth2' in domain.objects + assert domain.objects['Class.meth2'] == ('index', 'Class.meth2', 'method', False) + + # :staticmethod: + assert_node(doctree[1][1][4], addnodes.index, + entries=[('single', 'meth3() (Class static method)', 'Class.meth3', '', None)]) + assert_node(doctree[1][1][5], ([desc_signature, ([desc_annotation, ("static", desc_sig_space)], + [desc_name, "meth3"], + [desc_parameterlist, ()])], + [desc_content, ()])) + assert 'Class.meth3' in domain.objects + assert domain.objects['Class.meth3'] == ('index', 'Class.meth3', 'method', False) + + # :async: + assert_node(doctree[1][1][6], addnodes.index, + entries=[('single', 'meth4() (Class method)', 'Class.meth4', '', None)]) + assert_node(doctree[1][1][7], ([desc_signature, ([desc_annotation, ("async", desc_sig_space)], + [desc_name, "meth4"], + [desc_parameterlist, ()])], + [desc_content, ()])) + assert 'Class.meth4' in domain.objects + assert domain.objects['Class.meth4'] == ('index', 'Class.meth4', 'method', False) + + # :abstractmethod: + assert_node(doctree[1][1][8], addnodes.index, + entries=[('single', 'meth5() (Class method)', 'Class.meth5', '', None)]) + assert_node(doctree[1][1][9], ([desc_signature, ([desc_annotation, ("abstract", desc_sig_space)], + [desc_name, "meth5"], + [desc_parameterlist, ()])], + [desc_content, ()])) + assert 'Class.meth5' in domain.objects + assert domain.objects['Class.meth5'] == ('index', 'Class.meth5', 'method', False) + + # :final: + assert_node(doctree[1][1][10], addnodes.index, + entries=[('single', 'meth6() (Class method)', 'Class.meth6', '', None)]) + assert_node(doctree[1][1][11], ([desc_signature, ([desc_annotation, ("final", desc_sig_space)], + [desc_name, "meth6"], + [desc_parameterlist, ()])], + [desc_content, ()])) + assert 'Class.meth6' in domain.objects + assert domain.objects['Class.meth6'] == ('index', 'Class.meth6', 'method', False) + + +def test_pyclassmethod(app): + text = (".. py:class:: Class\n" + "\n" + " .. py:classmethod:: meth\n") + domain = app.env.get_domain('py') + doctree = restructuredtext.parse(app, text) + assert_node(doctree, (addnodes.index, + [desc, ([desc_signature, ([desc_annotation, ("class", desc_sig_space)], + [desc_name, "Class"])], + [desc_content, (addnodes.index, + desc)])])) + assert_node(doctree[1][1][0], addnodes.index, + entries=[('single', 'meth() (Class class method)', 'Class.meth', '', None)]) + assert_node(doctree[1][1][1], ([desc_signature, ([desc_annotation, ("classmethod", desc_sig_space)], + [desc_name, "meth"], + [desc_parameterlist, ()])], + [desc_content, ()])) + assert 'Class.meth' in domain.objects + assert domain.objects['Class.meth'] == ('index', 'Class.meth', 'method', False) + + +def test_pystaticmethod(app): + text = (".. py:class:: Class\n" + "\n" + " .. py:staticmethod:: meth\n") + domain = app.env.get_domain('py') + doctree = restructuredtext.parse(app, text) + assert_node(doctree, (addnodes.index, + [desc, ([desc_signature, ([desc_annotation, ("class", desc_sig_space)], + [desc_name, "Class"])], + [desc_content, (addnodes.index, + desc)])])) + assert_node(doctree[1][1][0], addnodes.index, + entries=[('single', 'meth() (Class static method)', 'Class.meth', '', None)]) + assert_node(doctree[1][1][1], ([desc_signature, ([desc_annotation, ("static", desc_sig_space)], + [desc_name, "meth"], + [desc_parameterlist, ()])], + [desc_content, ()])) + assert 'Class.meth' in domain.objects + assert domain.objects['Class.meth'] == ('index', 'Class.meth', 'method', False) + + +def test_pyattribute(app): + text = (".. py:class:: Class\n" + "\n" + " .. py:attribute:: attr\n" + " :type: Optional[str]\n" + " :value: ''\n") + domain = app.env.get_domain('py') + doctree = restructuredtext.parse(app, text) + assert_node(doctree, (addnodes.index, + [desc, ([desc_signature, ([desc_annotation, ("class", desc_sig_space)], + [desc_name, "Class"])], + [desc_content, (addnodes.index, + desc)])])) + assert_node(doctree[1][1][0], addnodes.index, + entries=[('single', 'attr (Class attribute)', 'Class.attr', '', None)]) + assert_node(doctree[1][1][1], ([desc_signature, ([desc_name, "attr"], + [desc_annotation, ([desc_sig_punctuation, ':'], + desc_sig_space, + [pending_xref, "str"], + desc_sig_space, + [desc_sig_punctuation, "|"], + desc_sig_space, + [pending_xref, "None"])], + [desc_annotation, (desc_sig_space, + [desc_sig_punctuation, '='], + desc_sig_space, + "''")], + )], + [desc_content, ()])) + assert_node(doctree[1][1][1][0][1][2], pending_xref, **{"py:class": "Class"}) + assert_node(doctree[1][1][1][0][1][6], pending_xref, **{"py:class": "Class"}) + assert 'Class.attr' in domain.objects + assert domain.objects['Class.attr'] == ('index', 'Class.attr', 'attribute', False) + + +def test_pyproperty(app): + text = (".. py:class:: Class\n" + "\n" + " .. py:property:: prop1\n" + " :abstractmethod:\n" + " :type: str\n" + "\n" + " .. py:property:: prop2\n" + " :classmethod:\n" + " :type: str\n") + domain = app.env.get_domain('py') + doctree = restructuredtext.parse(app, text) + assert_node(doctree, (addnodes.index, + [desc, ([desc_signature, ([desc_annotation, ("class", desc_sig_space)], + [desc_name, "Class"])], + [desc_content, (addnodes.index, + desc, + addnodes.index, + desc)])])) + assert_node(doctree[1][1][0], addnodes.index, + entries=[('single', 'prop1 (Class property)', 'Class.prop1', '', None)]) + assert_node(doctree[1][1][1], ([desc_signature, ([desc_annotation, ("abstract", desc_sig_space, + "property", desc_sig_space)], + [desc_name, "prop1"], + [desc_annotation, ([desc_sig_punctuation, ':'], + desc_sig_space, + [pending_xref, "str"])])], + [desc_content, ()])) + assert_node(doctree[1][1][2], addnodes.index, + entries=[('single', 'prop2 (Class property)', 'Class.prop2', '', None)]) + assert_node(doctree[1][1][3], ([desc_signature, ([desc_annotation, ("class", desc_sig_space, + "property", desc_sig_space)], + [desc_name, "prop2"], + [desc_annotation, ([desc_sig_punctuation, ':'], + desc_sig_space, + [pending_xref, "str"])])], + [desc_content, ()])) + assert 'Class.prop1' in domain.objects + assert domain.objects['Class.prop1'] == ('index', 'Class.prop1', 'property', False) + assert 'Class.prop2' in domain.objects + assert domain.objects['Class.prop2'] == ('index', 'Class.prop2', 'property', False) + + +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", no_index=False) + + assert 'deco' in domain.objects + assert domain.objects['deco'] == ('index', 'deco', 'function', False) + + +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", no_index=False) + + assert 'deco' in domain.objects + assert domain.objects['deco'] == ('index', 'deco', 'method', False) + +