From 768275466ab3a24c876b441e0e91291444b85786 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sat, 1 Feb 2020 10:47:08 +0900 Subject: [PATCH 1/2] autodoc: Fix crashed for objects having no module --- sphinx/ext/autodoc/typehints.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/sphinx/ext/autodoc/typehints.py b/sphinx/ext/autodoc/typehints.py index acdf6479c..d7b4ee96b 100644 --- a/sphinx/ext/autodoc/typehints.py +++ b/sphinx/ext/autodoc/typehints.py @@ -57,7 +57,10 @@ def merge_typehints(app: Sphinx, domain: str, objtype: str, contentnode: Element return signature = cast(addnodes.desc_signature, contentnode.parent[0]) - fullname = '.'.join([signature['module'], signature['fullname']]) + if signature['module']: + fullname = '.'.join([signature['module'], signature['fullname']]) + else: + fullname = signature['fullname'] annotations = app.env.temp_data.get('annotations', {}) if annotations.get(fullname, {}): field_lists = [n for n in contentnode if isinstance(n, nodes.field_list)] From 179a1f9cc2832f2abc24c0e574f86f4aa6e7e632 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Thu, 30 Jan 2020 13:09:10 +0900 Subject: [PATCH 2/2] py domain: Support type annotations for variables This adds ``:type:`` and ``:value:`` options to both ``py:data`` and ``py:attribute`` directives. It allows to describe its annotation in detail. --- CHANGES | 2 ++ doc/usage/restructuredtext/domains.rst | 24 ++++++++++++++++ sphinx/domains/python.py | 38 ++++++++++++++++++++++++++ tests/test_domain_py.py | 22 +++++++++++++-- 4 files changed, 84 insertions(+), 2 deletions(-) diff --git a/CHANGES b/CHANGES index 663dd160b..e07015afd 100644 --- a/CHANGES +++ b/CHANGES @@ -59,6 +59,8 @@ Features added * SphinxTranslator now calls visitor/departure method for super node class if visitor/departure method for original node class not found * #6418: Add new event: :event:`object-description-transform` +* py domain: :rst:dir:`py:data` and :rst:dir:`py:attribute` take new options + named ``:type:`` and ``:value:`` to describe its type and initial value Bugs fixed ---------- diff --git a/doc/usage/restructuredtext/domains.rst b/doc/usage/restructuredtext/domains.rst index c0ee3f230..870f1be63 100644 --- a/doc/usage/restructuredtext/domains.rst +++ b/doc/usage/restructuredtext/domains.rst @@ -195,6 +195,18 @@ The following directives are provided for module and class contents: as "defined constants." Class and object attributes are not documented using this environment. + .. rubric:: options + + .. rst:directive:option:: type: type of the variable + :type: text + + .. versionadded:: 2.4 + + .. rst:directive:option:: value: initial value of the variable + :type: text + + .. versionadded:: 2.4 + .. rst:directive:: .. py:exception:: name Describes an exception class. The signature can, but need not include @@ -229,6 +241,18 @@ The following directives are provided for module and class contents: information about the type of the data to be expected and whether it may be changed directly. + .. rubric:: options + + .. rst:directive:option:: type: type of the attribute + :type: text + + .. versionadded:: 2.4 + + .. rst:directive:option:: value: initial value of the attribute + :type: text + + .. versionadded:: 2.4 + .. rst:directive:: .. py:method:: name(parameters) Describes an object method. The parameters should not include the ``self`` diff --git a/sphinx/domains/python.py b/sphinx/domains/python.py index 777865b4b..b5ce9bdbf 100644 --- a/sphinx/domains/python.py +++ b/sphinx/domains/python.py @@ -446,6 +446,25 @@ class PyFunction(PyObject): class PyVariable(PyObject): """Description of a variable.""" + option_spec = PyObject.option_spec.copy() + option_spec.update({ + 'type': directives.unchanged, + 'value': 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) + + value = self.options.get('value') + if value: + signode += addnodes.desc_annotation(value, ' = ' + value) + + return fullname, prefix + def get_index_text(self, modname: str, name_cls: Tuple[str, str]) -> str: name, cls = name_cls if modname: @@ -638,6 +657,25 @@ class PyStaticMethod(PyMethod): class PyAttribute(PyObject): """Description of an attribute.""" + option_spec = PyObject.option_spec.copy() + option_spec.update({ + 'type': directives.unchanged, + 'value': 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) + + value = self.options.get('value') + if value: + signode += addnodes.desc_annotation(value, ' = ' + value) + + return fullname, prefix + def get_index_text(self, modname: str, name_cls: Tuple[str, str]) -> str: name, cls = name_cls try: diff --git a/tests/test_domain_py.py b/tests/test_domain_py.py index 3ff29cbb7..ec7b56f57 100644 --- a/tests/test_domain_py.py +++ b/tests/test_domain_py.py @@ -267,6 +267,20 @@ def test_exceptions_module_is_ignored(app): def test_pydata_signature(app): + text = (".. py:data:: version\n" + " :type: int\n" + " :value: 1\n") + doctree = restructuredtext.parse(app, text) + assert_node(doctree, (addnodes.index, + [desc, ([desc_signature, ([desc_name, "version"], + [desc_annotation, ": int"], + [desc_annotation, " = 1"])], + desc_content)])) + assert_node(doctree[1], addnodes.desc, desctype="data", + domain="py", objtype="data", noindex=False) + + +def test_pydata_signature_old(app): text = (".. py:data:: version\n" " :annotation: = 1\n") doctree = restructuredtext.parse(app, text) @@ -463,7 +477,9 @@ def test_pystaticmethod(app): def test_pyattribute(app): text = (".. py:class:: Class\n" "\n" - " .. py:attribute:: attr\n") + " .. py:attribute:: attr\n" + " :type: str\n" + " :value: ''\n") domain = app.env.get_domain('py') doctree = restructuredtext.parse(app, text) assert_node(doctree, (addnodes.index, @@ -473,7 +489,9 @@ def test_pyattribute(app): desc)])])) assert_node(doctree[1][1][0], addnodes.index, entries=[('single', 'attr (Class attribute)', 'Class.attr', '', None)]) - assert_node(doctree[1][1][1], ([desc_signature, desc_name, "attr"], + assert_node(doctree[1][1][1], ([desc_signature, ([desc_name, "attr"], + [desc_annotation, ": str"], + [desc_annotation, " = ''"])], [desc_content, ()])) assert 'Class.attr' in domain.objects assert domain.objects['Class.attr'] == ('index', 'attribute')