Close #7143: py domain: Add :final: option to py:*: directives

This commit is contained in:
Takeshi KOMIYA 2020-04-26 22:30:49 +09:00
parent 9c98b92c6a
commit 87308d9607
4 changed files with 82 additions and 2 deletions

View File

@ -64,6 +64,8 @@ Features added
* #7543: html theme: Add top and bottom margins to tables * #7543: html theme: Add top and bottom margins to tables
* C and C++: allow semicolon in the end of declarations. * C and C++: allow semicolon in the end of declarations.
* C++, parse parameterized noexcept specifiers. * C++, parse parameterized noexcept specifiers.
* #7143: py domain: Add ``:final:`` option to :rst:dir:`py:class:`,
:rst:dir:`py:exception:` and :rst:dir:`py:method:` directives
Bugs fixed Bugs fixed
---------- ----------

View File

@ -212,6 +212,15 @@ The following directives are provided for module and class contents:
Describes an exception class. The signature can, but need not include Describes an exception class. The signature can, but need not include
parentheses with constructor arguments. parentheses with constructor arguments.
.. rubric:: options
.. rst:directive:option:: final
:type: no value
Indicate the class is a final class.
.. versionadded:: 3.1
.. rst:directive:: .. py:class:: name .. rst:directive:: .. py:class:: name
.. py:class:: name(parameters) .. py:class:: name(parameters)
@ -235,6 +244,15 @@ The following directives are provided for module and class contents:
The first way is the preferred one. The first way is the preferred one.
.. rubric:: options
.. rst:directive:option:: final
:type: no value
Indicate the class is a final class.
.. versionadded:: 3.1
.. rst:directive:: .. py:attribute:: name .. rst:directive:: .. py:attribute:: name
Describes an object data attribute. The description should include Describes an object data attribute. The description should include
@ -283,6 +301,13 @@ The following directives are provided for module and class contents:
.. versionadded:: 2.1 .. versionadded:: 2.1
.. rst:directive:option:: final
:type: no value
Indicate the class is a final method.
.. versionadded:: 3.1
.. rst:directive:option:: property .. rst:directive:option:: property
:type: no value :type: no value

View File

@ -641,10 +641,18 @@ class PyClasslike(PyObject):
Description of a class-like object (classes, interfaces, exceptions). Description of a class-like object (classes, interfaces, exceptions).
""" """
option_spec = PyObject.option_spec.copy()
option_spec.update({
'final': directives.flag,
})
allow_nesting = True allow_nesting = True
def get_signature_prefix(self, sig: str) -> str: def get_signature_prefix(self, sig: str) -> str:
return self.objtype + ' ' if 'final' in self.options:
return 'final %s ' % self.objtype
else:
return '%s ' % self.objtype
def get_index_text(self, modname: str, name_cls: Tuple[str, str]) -> str: def get_index_text(self, modname: str, name_cls: Tuple[str, str]) -> str:
if self.objtype == 'class': if self.objtype == 'class':
@ -749,6 +757,7 @@ class PyMethod(PyObject):
'abstractmethod': directives.flag, 'abstractmethod': directives.flag,
'async': directives.flag, 'async': directives.flag,
'classmethod': directives.flag, 'classmethod': directives.flag,
'final': directives.flag,
'property': directives.flag, 'property': directives.flag,
'staticmethod': directives.flag, 'staticmethod': directives.flag,
}) })
@ -761,6 +770,8 @@ class PyMethod(PyObject):
def get_signature_prefix(self, sig: str) -> str: def get_signature_prefix(self, sig: str) -> str:
prefix = [] prefix = []
if 'final' in self.options:
prefix.append('final')
if 'abstractmethod' in self.options: if 'abstractmethod' in self.options:
prefix.append('abstract') prefix.append('abstract')
if 'async' in self.options: if 'async' in self.options:

View File

@ -499,6 +499,34 @@ def test_pyfunction(app):
assert domain.objects['example.func2'] == ('index', 'example.func2', 'function') assert domain.objects['example.func2'] == ('index', 'example.func2', 'function')
def test_pyclass_options(app):
text = (".. py:class:: Class1\n"
".. py:class:: Class2\n"
" :final:\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, "Class1"])],
[desc_content, ()])],
addnodes.index,
[desc, ([desc_signature, ([desc_annotation, "final class "],
[desc_name, "Class2"])],
[desc_content, ()])]))
# class
assert_node(doctree[0], addnodes.index,
entries=[('single', 'Class1 (built-in class)', 'Class1', '', None)])
assert 'Class1' in domain.objects
assert domain.objects['Class1'] == ('index', 'Class1', 'class')
# :final:
assert_node(doctree[2], addnodes.index,
entries=[('single', 'Class2 (built-in class)', 'Class2', '', None)])
assert 'Class2' in domain.objects
assert domain.objects['Class2'] == ('index', 'Class2', 'class')
def test_pymethod_options(app): def test_pymethod_options(app):
text = (".. py:class:: Class\n" text = (".. py:class:: Class\n"
"\n" "\n"
@ -512,7 +540,9 @@ def test_pymethod_options(app):
" .. py:method:: meth5\n" " .. py:method:: meth5\n"
" :property:\n" " :property:\n"
" .. py:method:: meth6\n" " .. py:method:: meth6\n"
" :abstractmethod:\n") " :abstractmethod:\n"
" .. py:method:: meth7\n"
" :final:\n")
domain = app.env.get_domain('py') domain = app.env.get_domain('py')
doctree = restructuredtext.parse(app, text) doctree = restructuredtext.parse(app, text)
assert_node(doctree, (addnodes.index, assert_node(doctree, (addnodes.index,
@ -529,6 +559,8 @@ def test_pymethod_options(app):
addnodes.index, addnodes.index,
desc, desc,
addnodes.index, addnodes.index,
desc,
addnodes.index,
desc)])])) desc)])]))
# method # method
@ -589,6 +621,16 @@ def test_pymethod_options(app):
assert 'Class.meth6' in domain.objects assert 'Class.meth6' in domain.objects
assert domain.objects['Class.meth6'] == ('index', 'Class.meth6', 'method') assert domain.objects['Class.meth6'] == ('index', 'Class.meth6', 'method')
# :final:
assert_node(doctree[1][1][12], addnodes.index,
entries=[('single', 'meth7() (Class method)', 'Class.meth7', '', None)])
assert_node(doctree[1][1][13], ([desc_signature, ([desc_annotation, "final "],
[desc_name, "meth7"],
[desc_parameterlist, ()])],
[desc_content, ()]))
assert 'Class.meth7' in domain.objects
assert domain.objects['Class.meth7'] == ('index', 'Class.meth7', 'method')
def test_pyclassmethod(app): def test_pyclassmethod(app):
text = (".. py:class:: Class\n" text = (".. py:class:: Class\n"