diff --git a/CHANGES b/CHANGES index 1466548e4..b5d34289d 100644 --- a/CHANGES +++ b/CHANGES @@ -41,6 +41,7 @@ Features added is required) * SphinxTranslator now calls visitor/departure method for super node class if visitor/departure method for original node class not found +* #6785: py domain: ``:py:attr:`` is able to refer properties again Bugs fixed ---------- diff --git a/sphinx/domains/python.py b/sphinx/domains/python.py index 777865b4b..242ef83c0 100644 --- a/sphinx/domains/python.py +++ b/sphinx/domains/python.py @@ -1011,6 +1011,11 @@ class PythonDomain(Domain): searchmode = 1 if node.hasattr('refspecific') else 0 matches = self.find_obj(env, modname, clsname, target, type, searchmode) + + if not matches and type == 'attr': + # fallback to meth (for property) + matches = self.find_obj(env, modname, clsname, target, 'meth', searchmode) + if not matches: return None elif len(matches) > 1: diff --git a/tests/roots/test-domain-py/module.rst b/tests/roots/test-domain-py/module.rst index 64601bc95..c01032b26 100644 --- a/tests/roots/test-domain-py/module.rst +++ b/tests/roots/test-domain-py/module.rst @@ -18,6 +18,12 @@ module * Link to :py:meth:`module_a.submodule.ModTopLevel.mod_child_1` +.. py:method:: ModTopLevel.prop + :property: + + * Link to :py:attr:`prop attribute <.prop>` + * Link to :py:meth:`prop method <.prop>` + .. py:currentmodule:: None .. py:class:: ModNoModule diff --git a/tests/test_domain_py.py b/tests/test_domain_py.py index 3ff29cbb7..913ef8f4b 100644 --- a/tests/test_domain_py.py +++ b/tests/test_domain_py.py @@ -105,19 +105,22 @@ def test_domain_py_xrefs(app, status, warning): 'mod_child_2', 'meth') assert_refnode(refnodes[4], 'module_a.submodule', 'ModTopLevel', 'module_a.submodule.ModTopLevel.mod_child_1', 'meth') - assert_refnode(refnodes[5], 'module_b.submodule', None, + assert_refnode(refnodes[5], 'module_a.submodule', 'ModTopLevel', + 'prop', 'attr') + assert_refnode(refnodes[6], 'module_a.submodule', 'ModTopLevel', + 'prop', 'meth') + assert_refnode(refnodes[7], 'module_b.submodule', None, 'ModTopLevel', 'class') - assert_refnode(refnodes[6], 'module_b.submodule', 'ModTopLevel', + assert_refnode(refnodes[8], 'module_b.submodule', 'ModTopLevel', 'ModNoModule', 'class') - assert_refnode(refnodes[7], False, False, 'int', 'class') - assert_refnode(refnodes[8], False, False, 'tuple', 'class') - assert_refnode(refnodes[9], False, False, 'str', 'class') - assert_refnode(refnodes[10], False, False, 'float', 'class') - assert_refnode(refnodes[11], False, False, 'list', 'class') - assert_refnode(refnodes[11], False, False, 'list', 'class') - assert_refnode(refnodes[12], False, False, 'ModTopLevel', 'class') - assert_refnode(refnodes[13], False, False, 'index', 'doc', domain='std') - assert len(refnodes) == 14 + assert_refnode(refnodes[9], False, False, 'int', 'class') + assert_refnode(refnodes[10], False, False, 'tuple', 'class') + assert_refnode(refnodes[11], False, False, 'str', 'class') + assert_refnode(refnodes[12], False, False, 'float', 'class') + assert_refnode(refnodes[13], False, False, 'list', 'class') + assert_refnode(refnodes[14], False, False, 'ModTopLevel', 'class') + assert_refnode(refnodes[15], False, False, 'index', 'doc', domain='std') + assert len(refnodes) == 16 doctree = app.env.get_doctree('module_option') refnodes = list(doctree.traverse(addnodes.pending_xref)) @@ -161,6 +164,21 @@ def test_domain_py_objects(app, status, warning): assert objects['NestedParentB.child_1'] == ('roles', '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 ' + '' + 'prop attribute' in content) + assert ('Link to ' + '' + 'prop method' in content) + + @pytest.mark.sphinx('dummy', testroot='domain-py') def test_domain_py_find_obj(app, status, warning):