mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
py domain: Add py:property directive to describe a property (refs: #7068)
This commit is contained in:
parent
3693ffe232
commit
204f86f736
1
CHANGES
1
CHANGES
@ -66,6 +66,7 @@ Features added
|
||||
the location where the object is defined
|
||||
* #7199: py domain: Add :confval:`python_use_unqualified_type_names` to suppress
|
||||
the module name of the python reference if it can be resolved (experimental)
|
||||
* #7068: py domain: Add :rst:dir:`py:property` directive to describe a property
|
||||
* #7784: i18n: The alt text for image is translated by default (without
|
||||
:confval:`gettext_additional_targets` setting)
|
||||
* #2018: html: :confval:`html_favicon` and :confval:`html_logo` now accept URL
|
||||
|
@ -316,6 +316,22 @@ The following directives are provided for module and class contents:
|
||||
|
||||
.. versionadded:: 4.0
|
||||
|
||||
.. rst:directive:: .. py:property:: name
|
||||
|
||||
Describes an object property.
|
||||
|
||||
.. versionadded:: 4.0
|
||||
|
||||
.. rubric:: options
|
||||
|
||||
.. rst:directive:option:: abstractmethod
|
||||
:type: no value
|
||||
|
||||
Indicate the property is abstract.
|
||||
|
||||
.. rst:directive:option:: type: type of the property
|
||||
:type: text
|
||||
|
||||
.. rst:directive:: .. py:method:: name(parameters)
|
||||
|
||||
Describes an object method. The parameters should not include the ``self``
|
||||
@ -368,6 +384,10 @@ The following directives are provided for module and class contents:
|
||||
|
||||
.. versionadded:: 2.1
|
||||
|
||||
.. deprecated:: 4.0
|
||||
|
||||
Use :rst:dir:`py:property` instead.
|
||||
|
||||
.. rst:directive:option:: staticmethod
|
||||
:type: no value
|
||||
|
||||
@ -584,6 +604,8 @@ a matching identifier is found:
|
||||
|
||||
Reference a data attribute of an object.
|
||||
|
||||
.. note:: The role is also able to refer to property.
|
||||
|
||||
.. rst:role:: py:exc
|
||||
|
||||
Reference an exception. A dotted name may be used.
|
||||
|
@ -825,6 +825,46 @@ class PyAttribute(PyObject):
|
||||
return _('%s (%s attribute)') % (attrname, clsname)
|
||||
|
||||
|
||||
class PyProperty(PyObject):
|
||||
"""Description of an attribute."""
|
||||
|
||||
option_spec = PyObject.option_spec.copy()
|
||||
option_spec.update({
|
||||
'abstractmethod': directives.flag,
|
||||
'type': directives.unchanged,
|
||||
})
|
||||
|
||||
def handle_signature(self, sig: str, signode: desc_signature) -> Tuple[str, str]:
|
||||
fullname, prefix = super().handle_signature(sig, signode)
|
||||
|
||||
typ = self.options.get('type')
|
||||
if typ:
|
||||
signode += addnodes.desc_annotation(typ, ': ' + typ)
|
||||
|
||||
return fullname, prefix
|
||||
|
||||
def get_signature_prefix(self, sig: str) -> str:
|
||||
prefix = ['property']
|
||||
if 'abstractmethod' in self.options:
|
||||
prefix.insert(0, 'abstract')
|
||||
|
||||
return ' '.join(prefix) + ' '
|
||||
|
||||
def get_index_text(self, modname: str, name_cls: Tuple[str, str]) -> str:
|
||||
name, cls = name_cls
|
||||
try:
|
||||
clsname, attrname = name.rsplit('.', 1)
|
||||
if modname and self.env.config.add_module_names:
|
||||
clsname = '.'.join([modname, clsname])
|
||||
except ValueError:
|
||||
if modname:
|
||||
return _('%s (in module %s)') % (name, modname)
|
||||
else:
|
||||
return name
|
||||
|
||||
return _('%s (%s property)') % (attrname, clsname)
|
||||
|
||||
|
||||
class PyDecoratorMixin:
|
||||
"""
|
||||
Mixin for decorator directives.
|
||||
@ -1056,6 +1096,7 @@ class PythonDomain(Domain):
|
||||
'classmethod': ObjType(_('class method'), 'meth', 'obj'),
|
||||
'staticmethod': ObjType(_('static method'), 'meth', 'obj'),
|
||||
'attribute': ObjType(_('attribute'), 'attr', 'obj'),
|
||||
'property': ObjType(_('property'), 'attr', '_prop', 'obj'),
|
||||
'module': ObjType(_('module'), 'mod', 'obj'),
|
||||
} # type: Dict[str, ObjType]
|
||||
|
||||
@ -1068,6 +1109,7 @@ class PythonDomain(Domain):
|
||||
'classmethod': PyClassMethod,
|
||||
'staticmethod': PyStaticMethod,
|
||||
'attribute': PyAttribute,
|
||||
'property': PyProperty,
|
||||
'module': PyModule,
|
||||
'currentmodule': PyCurrentModule,
|
||||
'decorator': PyDecoratorFunction,
|
||||
@ -1205,8 +1247,17 @@ class PythonDomain(Domain):
|
||||
type, searchmode)
|
||||
|
||||
if not matches and type == 'attr':
|
||||
# fallback to meth (for property)
|
||||
# fallback to meth (for property; Sphinx-2.4.x)
|
||||
# this ensures that `:attr:` role continues to refer to the old property entry
|
||||
# that defined by ``method`` directive in old reST files.
|
||||
matches = self.find_obj(env, modname, clsname, target, 'meth', searchmode)
|
||||
if not matches and type == 'meth':
|
||||
# fallback to attr (for property)
|
||||
# this ensures that `:meth:` in the old reST files can refer to the property
|
||||
# entry that defined by ``property`` directive.
|
||||
#
|
||||
# Note: _prop is a secret role only for internal look-up.
|
||||
matches = self.find_obj(env, modname, clsname, target, '_prop', searchmode)
|
||||
|
||||
if not matches:
|
||||
return None
|
||||
|
@ -18,8 +18,7 @@ module
|
||||
|
||||
* Link to :py:meth:`module_a.submodule.ModTopLevel.mod_child_1`
|
||||
|
||||
.. py:method:: ModTopLevel.prop
|
||||
:property:
|
||||
.. py:property:: ModTopLevel.prop
|
||||
|
||||
* Link to :py:attr:`prop attribute <.prop>`
|
||||
* Link to :py:meth:`prop method <.prop>`
|
||||
|
@ -201,6 +201,10 @@ def test_resolve_xref_for_properties(app, status, warning):
|
||||
' title="module_a.submodule.ModTopLevel.prop">'
|
||||
'<code class="xref py py-meth docutils literal notranslate"><span class="pre">'
|
||||
'prop</span> <span class="pre">method</span></code></a>' in content)
|
||||
assert ('Link to <a class="reference internal" href="#module_a.submodule.ModTopLevel.prop"'
|
||||
' title="module_a.submodule.ModTopLevel.prop">'
|
||||
'<code class="xref py py-attr docutils literal notranslate"><span class="pre">'
|
||||
'prop</span> <span class="pre">attribute</span></code></a>' in content)
|
||||
|
||||
|
||||
@pytest.mark.sphinx('dummy', testroot='domain-py')
|
||||
@ -798,6 +802,29 @@ def test_pyattribute(app):
|
||||
assert domain.objects['Class.attr'] == ('index', 'Class.attr', 'attribute', False)
|
||||
|
||||
|
||||
def test_pyproperty(app):
|
||||
text = (".. py:class:: Class\n"
|
||||
"\n"
|
||||
" .. py:property:: prop\n"
|
||||
" :abstractmethod:\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_name, "Class"])],
|
||||
[desc_content, (addnodes.index,
|
||||
desc)])]))
|
||||
assert_node(doctree[1][1][0], addnodes.index,
|
||||
entries=[('single', 'prop (Class property)', 'Class.prop', '', None)])
|
||||
assert_node(doctree[1][1][1], ([desc_signature, ([desc_annotation, "abstract property "],
|
||||
[desc_name, "prop"],
|
||||
[desc_annotation, ": str"])],
|
||||
[desc_content, ()]))
|
||||
assert 'Class.prop' in domain.objects
|
||||
assert domain.objects['Class.prop'] == ('index', 'Class.prop', 'property', False)
|
||||
|
||||
|
||||
def test_pydecorator_signature(app):
|
||||
text = ".. py:decorator:: deco"
|
||||
domain = app.env.get_domain('py')
|
||||
|
Loading…
Reference in New Issue
Block a user