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):