mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
Merge pull request #7298 from tk0miya/7068_pyproperty_directive
py domain: Add py:property directive to describe a property (refs: #7068)
This commit is contained in:
commit
17bb78997c
1
CHANGES
1
CHANGES
@ -67,6 +67,7 @@ Features added
|
|||||||
the location where the object is defined
|
the location where the object is defined
|
||||||
* #7199: py domain: Add :confval:`python_use_unqualified_type_names` to suppress
|
* #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)
|
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
|
* #7784: i18n: The alt text for image is translated by default (without
|
||||||
:confval:`gettext_additional_targets` setting)
|
:confval:`gettext_additional_targets` setting)
|
||||||
* #2018: html: :confval:`html_favicon` and :confval:`html_logo` now accept URL
|
* #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
|
.. 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)
|
.. rst:directive:: .. py:method:: name(parameters)
|
||||||
|
|
||||||
Describes an object method. The parameters should not include the ``self``
|
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
|
.. versionadded:: 2.1
|
||||||
|
|
||||||
|
.. deprecated:: 4.0
|
||||||
|
|
||||||
|
Use :rst:dir:`py:property` instead.
|
||||||
|
|
||||||
.. rst:directive:option:: staticmethod
|
.. rst:directive:option:: staticmethod
|
||||||
:type: no value
|
:type: no value
|
||||||
|
|
||||||
@ -584,6 +604,8 @@ a matching identifier is found:
|
|||||||
|
|
||||||
Reference a data attribute of an object.
|
Reference a data attribute of an object.
|
||||||
|
|
||||||
|
.. note:: The role is also able to refer to property.
|
||||||
|
|
||||||
.. rst:role:: py:exc
|
.. rst:role:: py:exc
|
||||||
|
|
||||||
Reference an exception. A dotted name may be used.
|
Reference an exception. A dotted name may be used.
|
||||||
|
@ -825,6 +825,46 @@ class PyAttribute(PyObject):
|
|||||||
return _('%s (%s attribute)') % (attrname, clsname)
|
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:
|
class PyDecoratorMixin:
|
||||||
"""
|
"""
|
||||||
Mixin for decorator directives.
|
Mixin for decorator directives.
|
||||||
@ -1056,6 +1096,7 @@ class PythonDomain(Domain):
|
|||||||
'classmethod': ObjType(_('class method'), 'meth', 'obj'),
|
'classmethod': ObjType(_('class method'), 'meth', 'obj'),
|
||||||
'staticmethod': ObjType(_('static method'), 'meth', 'obj'),
|
'staticmethod': ObjType(_('static method'), 'meth', 'obj'),
|
||||||
'attribute': ObjType(_('attribute'), 'attr', 'obj'),
|
'attribute': ObjType(_('attribute'), 'attr', 'obj'),
|
||||||
|
'property': ObjType(_('property'), 'attr', '_prop', 'obj'),
|
||||||
'module': ObjType(_('module'), 'mod', 'obj'),
|
'module': ObjType(_('module'), 'mod', 'obj'),
|
||||||
} # type: Dict[str, ObjType]
|
} # type: Dict[str, ObjType]
|
||||||
|
|
||||||
@ -1068,6 +1109,7 @@ class PythonDomain(Domain):
|
|||||||
'classmethod': PyClassMethod,
|
'classmethod': PyClassMethod,
|
||||||
'staticmethod': PyStaticMethod,
|
'staticmethod': PyStaticMethod,
|
||||||
'attribute': PyAttribute,
|
'attribute': PyAttribute,
|
||||||
|
'property': PyProperty,
|
||||||
'module': PyModule,
|
'module': PyModule,
|
||||||
'currentmodule': PyCurrentModule,
|
'currentmodule': PyCurrentModule,
|
||||||
'decorator': PyDecoratorFunction,
|
'decorator': PyDecoratorFunction,
|
||||||
@ -1205,8 +1247,17 @@ class PythonDomain(Domain):
|
|||||||
type, searchmode)
|
type, searchmode)
|
||||||
|
|
||||||
if not matches and type == 'attr':
|
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)
|
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:
|
if not matches:
|
||||||
return None
|
return None
|
||||||
|
@ -18,8 +18,7 @@ module
|
|||||||
|
|
||||||
* Link to :py:meth:`module_a.submodule.ModTopLevel.mod_child_1`
|
* Link to :py:meth:`module_a.submodule.ModTopLevel.mod_child_1`
|
||||||
|
|
||||||
.. py:method:: ModTopLevel.prop
|
.. py:property:: ModTopLevel.prop
|
||||||
:property:
|
|
||||||
|
|
||||||
* Link to :py:attr:`prop attribute <.prop>`
|
* Link to :py:attr:`prop attribute <.prop>`
|
||||||
* Link to :py:meth:`prop method <.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">'
|
' title="module_a.submodule.ModTopLevel.prop">'
|
||||||
'<code class="xref py py-meth docutils literal notranslate"><span class="pre">'
|
'<code class="xref py py-meth docutils literal notranslate"><span class="pre">'
|
||||||
'prop</span> <span class="pre">method</span></code></a>' in content)
|
'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')
|
@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)
|
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):
|
def test_pydecorator_signature(app):
|
||||||
text = ".. py:decorator:: deco"
|
text = ".. py:decorator:: deco"
|
||||||
domain = app.env.get_domain('py')
|
domain = app.env.get_domain('py')
|
||||||
|
Loading…
Reference in New Issue
Block a user