Add `py:type` directive and role for documenting type aliases (#11989)

Co-authored-by: Adam Turner <9087854+aa-turner@users.noreply.github.com>
This commit is contained in:
Ashley Whetter
2024-07-11 04:15:54 -07:00
committed by GitHub
parent 91c5cd3abd
commit e38a60d3f2
9 changed files with 221 additions and 15 deletions

View File

@@ -92,19 +92,21 @@ def test_domain_py_xrefs(app, status, warning):
refnodes = list(doctree.findall(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')
assert_refnode(refnodes[3], None, 'NestedParentA', 'NestedChildA.subchild_2', 'meth')
assert_refnode(refnodes[4], None, 'NestedParentA', 'child_2', 'meth')
assert_refnode(refnodes[5], False, 'NestedParentA', 'any_child', domain='')
assert_refnode(refnodes[6], None, 'NestedParentA', 'NestedChildA', 'class')
assert_refnode(refnodes[7], None, 'NestedParentA.NestedChildA', 'subchild_2', 'meth')
assert_refnode(refnodes[8], None, 'NestedParentA.NestedChildA',
assert_refnode(refnodes[2], None, None, 'TopLevelType', 'type')
assert_refnode(refnodes[3], None, 'NestedParentA', 'child_1', 'meth')
assert_refnode(refnodes[4], None, 'NestedParentA', 'NestedChildA.subchild_2', 'meth')
assert_refnode(refnodes[5], None, 'NestedParentA', 'child_2', 'meth')
assert_refnode(refnodes[6], False, 'NestedParentA', 'any_child', domain='')
assert_refnode(refnodes[7], None, 'NestedParentA', 'NestedChildA', 'class')
assert_refnode(refnodes[8], None, 'NestedParentA.NestedChildA', 'subchild_2', 'meth')
assert_refnode(refnodes[9], None, 'NestedParentA.NestedChildA',
'NestedParentA.child_1', 'meth')
assert_refnode(refnodes[9], None, 'NestedParentA', 'NestedChildA.subchild_1', 'meth')
assert_refnode(refnodes[10], None, 'NestedParentB', 'child_1', 'meth')
assert_refnode(refnodes[11], None, 'NestedParentB', 'NestedParentB', 'class')
assert_refnode(refnodes[12], None, None, 'NestedParentA.NestedChildA', 'class')
assert len(refnodes) == 13
assert_refnode(refnodes[10], None, 'NestedParentA', 'NestedChildA.subchild_1', 'meth')
assert_refnode(refnodes[11], None, 'NestedParentB', 'child_1', 'meth')
assert_refnode(refnodes[12], None, 'NestedParentB', 'NestedParentB', 'class')
assert_refnode(refnodes[13], None, None, 'NestedParentA.NestedChildA', 'class')
assert_refnode(refnodes[14], None, None, 'NestedParentA.NestedTypeA', 'type')
assert len(refnodes) == 15
doctree = app.env.get_doctree('module')
refnodes = list(doctree.findall(pending_xref))
@@ -135,7 +137,10 @@ def test_domain_py_xrefs(app, status, warning):
assert_refnode(refnodes[15], False, False, 'index', 'doc', domain='std')
assert_refnode(refnodes[16], False, False, 'typing.Literal', 'obj', domain='py')
assert_refnode(refnodes[17], False, False, 'typing.Literal', 'obj', domain='py')
assert len(refnodes) == 18
assert_refnode(refnodes[18], False, False, 'list', 'class', domain='py')
assert_refnode(refnodes[19], False, False, 'int', 'class', domain='py')
assert_refnode(refnodes[20], False, False, 'str', 'class', domain='py')
assert len(refnodes) == 21
doctree = app.env.get_doctree('module_option')
refnodes = list(doctree.findall(pending_xref))
@@ -191,7 +196,9 @@ def test_domain_py_objects(app, status, warning):
assert objects['TopLevel'][2] == 'class'
assert objects['top_level'][2] == 'method'
assert objects['TopLevelType'][2] == 'type'
assert objects['NestedParentA'][2] == 'class'
assert objects['NestedParentA.NestedTypeA'][2] == 'type'
assert objects['NestedParentA.child_1'][2] == 'method'
assert objects['NestedParentA.any_child'][2] == 'method'
assert objects['NestedParentA.NestedChildA'][2] == 'class'
@@ -233,6 +240,9 @@ 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', 'NestedParentA', 'class', False))])
assert (find_obj(None, None, 'NestedParentA.NestedTypeA', 'type') ==
[('NestedParentA.NestedTypeA',
('roles', 'NestedParentA.NestedTypeA', 'type', False))])
assert (find_obj(None, None, 'NestedParentA.NestedChildA', 'class') ==
[('NestedParentA.NestedChildA',
('roles', 'NestedParentA.NestedChildA', 'class', False))])

View File

@@ -2,6 +2,7 @@
from __future__ import annotations
import pytest
from docutils import nodes
from sphinx import addnodes
@@ -362,6 +363,76 @@ def test_pyproperty(app):
assert domain.objects['Class.prop2'] == ('index', 'Class.prop2', 'property', False)
def test_py_type_alias(app):
text = (".. py:module:: example\n"
".. py:type:: Alias1\n"
" :canonical: list[str | int]\n"
"\n"
".. py:class:: Class\n"
"\n"
" .. py:type:: Alias2\n"
" :canonical: 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_annotation, ('type', desc_sig_space)],
[desc_addname, 'example.'],
[desc_name, 'Alias1'],
[desc_annotation, (desc_sig_space,
[desc_sig_punctuation, '='],
desc_sig_space,
[pending_xref, 'list'],
[desc_sig_punctuation, '['],
[pending_xref, 'str'],
desc_sig_space,
[desc_sig_punctuation, '|'],
desc_sig_space,
[pending_xref, 'int'],
[desc_sig_punctuation, ']'],
)])],
[desc_content, ()])],
addnodes.index,
[desc, ([desc_signature, ([desc_annotation, ('class', desc_sig_space)],
[desc_addname, 'example.'],
[desc_name, 'Class'])],
[desc_content, (addnodes.index,
desc)])]))
assert_node(doctree[5][1][0], addnodes.index,
entries=[('single', 'Alias2 (type alias in example.Class)', 'example.Class.Alias2', '', None)])
assert_node(doctree[5][1][1], ([desc_signature, ([desc_annotation, ('type', desc_sig_space)],
[desc_name, 'Alias2'],
[desc_annotation, (desc_sig_space,
[desc_sig_punctuation, '='],
desc_sig_space,
[pending_xref, 'int'])])],
[desc_content, ()]))
assert 'example.Alias1' in domain.objects
assert domain.objects['example.Alias1'] == ('index', 'example.Alias1', 'type', False)
assert 'example.Class.Alias2' in domain.objects
assert domain.objects['example.Class.Alias2'] == ('index', 'example.Class.Alias2', 'type', False)
@pytest.mark.sphinx('html', testroot='domain-py', freshenv=True)
def test_domain_py_type_alias(app, status, warning):
app.build(force_all=True)
content = (app.outdir / 'type_alias.html').read_text(encoding='utf8')
assert ('<em class="property"><span class="pre">type</span><span class="w"> </span></em>'
'<span class="sig-prename descclassname"><span class="pre">module_one.</span></span>'
'<span class="sig-name descname"><span class="pre">MyAlias</span></span>'
'<em class="property"><span class="w"> </span><span class="p"><span class="pre">=</span></span>'
'<span class="w"> </span><span class="pre">list</span>'
'<span class="p"><span class="pre">[</span></span>'
'<span class="pre">int</span><span class="w"> </span>'
'<span class="p"><span class="pre">|</span></span><span class="w"> </span>'
'<a class="reference internal" href="#module_two.SomeClass" title="module_two.SomeClass">'
'<span class="pre">module_two.SomeClass</span></a>'
'<span class="p"><span class="pre">]</span></span></em>' in content)
assert warning.getvalue() == ''
def test_pydecorator_signature(app):
text = ".. py:decorator:: deco"
domain = app.env.get_domain('py')