Skip to content

Commit

Permalink
Close sphinx-doc#7143: py domain: Add :final: option to py:*: directives
Browse files Browse the repository at this point in the history
  • Loading branch information
tk0miya committed Apr 26, 2020
1 parent b039c02 commit 1953d47
Show file tree
Hide file tree
Showing 4 changed files with 82 additions and 2 deletions.
2 changes: 2 additions & 0 deletions CHANGES
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ Features added
* #7542: html theme: Make admonition/topic/sidebar scrollable
* C and C++: allow semicolon in the end of declarations.
* 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
----------
Expand Down
25 changes: 25 additions & 0 deletions doc/usage/restructuredtext/domains.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
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
.. py:class:: name(parameters)
Expand All @@ -235,6 +244,15 @@ The following directives are provided for module and class contents:

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
Describes an object data attribute. The description should include
Expand Down Expand Up @@ -283,6 +301,13 @@ The following directives are provided for module and class contents:
.. versionadded:: 2.1
.. rst:directive:option:: final
:type: no value
Indicate the class is a final method.
.. versionadded:: 3.1
.. rst:directive:option:: property
:type: no value
Expand Down
13 changes: 12 additions & 1 deletion sphinx/domains/python.py
Original file line number Diff line number Diff line change
Expand Up @@ -641,10 +641,18 @@ class PyClasslike(PyObject):
Description of a class-like object (classes, interfaces, exceptions).
"""

option_spec = PyObject.option_spec.copy()
option_spec.update({
'final': directives.flag,
})

allow_nesting = True

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:
if self.objtype == 'class':
Expand Down Expand Up @@ -749,6 +757,7 @@ class PyMethod(PyObject):
'abstractmethod': directives.flag,
'async': directives.flag,
'classmethod': directives.flag,
'final': directives.flag,
'property': directives.flag,
'staticmethod': directives.flag,
})
Expand All @@ -761,6 +770,8 @@ def needs_arglist(self) -> bool:

def get_signature_prefix(self, sig: str) -> str:
prefix = []
if 'final' in self.options:
prefix.append('final')
if 'abstractmethod' in self.options:
prefix.append('abstract')
if 'async' in self.options:
Expand Down
44 changes: 43 additions & 1 deletion tests/test_domain_py.py
Original file line number Diff line number Diff line change
Expand Up @@ -499,6 +499,34 @@ def test_pyfunction(app):
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):
text = (".. py:class:: Class\n"
"\n"
Expand All @@ -512,7 +540,9 @@ def test_pymethod_options(app):
" .. py:method:: meth5\n"
" :property:\n"
" .. py:method:: meth6\n"
" :abstractmethod:\n")
" :abstractmethod:\n"
" .. py:method:: meth7\n"
" :final:\n")
domain = app.env.get_domain('py')
doctree = restructuredtext.parse(app, text)
assert_node(doctree, (addnodes.index,
Expand All @@ -529,6 +559,8 @@ def test_pymethod_options(app):
addnodes.index,
desc,
addnodes.index,
desc,
addnodes.index,
desc)])]))

# method
Expand Down Expand Up @@ -589,6 +621,16 @@ def test_pymethod_options(app):
assert 'Class.meth6' in domain.objects
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):
text = (".. py:class:: Class\n"
Expand Down

0 comments on commit 1953d47

Please sign in to comment.