From dd6c79476f9606258aa6d7f0ddfcd2db74e8aab2 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sun, 7 Apr 2019 21:09:57 +0900 Subject: [PATCH 1/4] Add PyMethod class; a directive for python method description --- sphinx/domains/python.py | 25 ++++++++++++++++++++++++- tests/test_domain_py.py | 21 +++++++++++++++++++++ 2 files changed, 45 insertions(+), 1 deletion(-) diff --git a/sphinx/domains/python.py b/sphinx/domains/python.py index 29c0c2536..e341a7869 100644 --- a/sphinx/domains/python.py +++ b/sphinx/domains/python.py @@ -523,6 +523,29 @@ class PyClassmember(PyObject): return '' +class PyMethod(PyObject): + """Description of a method.""" + + def needs_arglist(self): + # type: () -> bool + return True + + def get_index_text(self, modname, name_cls): + # type: (str, Tuple[str, str]) -> str + name, cls = name_cls + try: + clsname, methname = 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 '%s()' % name + + return _('%s() (%s method)') % (methname, clsname) + + class PyDecoratorMixin: """ Mixin for decorator directives. @@ -745,7 +768,7 @@ class PythonDomain(Domain): 'data': PyModulelevel, 'class': PyClasslike, 'exception': PyClasslike, - 'method': PyClassmember, + 'method': PyMethod, 'classmethod': PyClassmember, 'staticmethod': PyClassmember, 'attribute': PyClassmember, diff --git a/tests/test_domain_py.py b/tests/test_domain_py.py index fb6e70914..4ee3819de 100644 --- a/tests/test_domain_py.py +++ b/tests/test_domain_py.py @@ -290,3 +290,24 @@ def test_pyobject_prefix(app): desc)])])) assert doctree[1][1][1].astext().strip() == 'say' # prefix is stripped assert doctree[1][1][3].astext().strip() == 'FooBar.say' # not stripped + + +def test_pymethod(app): + text = (".. py:class:: Class\n" + "\n" + " .. py:method:: meth\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', 'meth() (Class method)', 'Class.meth', '', None)]) + assert_node(doctree[1][1][1], ([desc_signature, ([desc_name, "meth"], + [desc_parameterlist, ()])], + [desc_content, ()])) + assert 'Class.meth' in domain.objects + assert domain.objects['Class.meth'] == ('index', 'method') From c8334705ca27ea3541214db0fd3a8b477b98dcc3 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sun, 7 Apr 2019 21:38:11 +0900 Subject: [PATCH 2/4] Add PyClassMethod and PyStaticMethod; directives for python method description --- sphinx/domains/python.py | 50 ++++++++++++++++++++++++++++++++++++++-- tests/test_domain_py.py | 42 +++++++++++++++++++++++++++++++++ 2 files changed, 90 insertions(+), 2 deletions(-) diff --git a/sphinx/domains/python.py b/sphinx/domains/python.py index e341a7869..492610659 100644 --- a/sphinx/domains/python.py +++ b/sphinx/domains/python.py @@ -546,6 +546,52 @@ class PyMethod(PyObject): return _('%s() (%s method)') % (methname, clsname) +class PyClassMethod(PyMethod): + """Description of a classmethod.""" + + def get_signature_prefix(self, sig): + # type: (str) -> str + return 'classmethod ' + + def get_index_text(self, modname, name_cls): + # type: (str, Tuple[str, str]) -> str + name, cls = name_cls + try: + clsname, methname = 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 '%s()' % name + + return _('%s() (%s class method)') % (methname, clsname) + + +class PyStaticMethod(PyMethod): + """Description of a staticmethod.""" + + def get_signature_prefix(self, sig): + # type: (str) -> str + return 'static ' + + def get_index_text(self, modname, name_cls): + # type: (str, Tuple[str, str]) -> str + name, cls = name_cls + try: + clsname, methname = 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 '%s()' % name + + return _('%s() (%s static method)') % (methname, clsname) + + class PyDecoratorMixin: """ Mixin for decorator directives. @@ -769,8 +815,8 @@ class PythonDomain(Domain): 'class': PyClasslike, 'exception': PyClasslike, 'method': PyMethod, - 'classmethod': PyClassmember, - 'staticmethod': PyClassmember, + 'classmethod': PyClassMethod, + 'staticmethod': PyStaticMethod, 'attribute': PyClassmember, 'module': PyModule, 'currentmodule': PyCurrentModule, diff --git a/tests/test_domain_py.py b/tests/test_domain_py.py index 4ee3819de..a3836f523 100644 --- a/tests/test_domain_py.py +++ b/tests/test_domain_py.py @@ -311,3 +311,45 @@ def test_pymethod(app): [desc_content, ()])) assert 'Class.meth' in domain.objects assert domain.objects['Class.meth'] == ('index', 'method') + + +def test_pyclassmethod(app): + text = (".. py:class:: Class\n" + "\n" + " .. py:classmethod:: meth\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', 'meth() (Class class method)', 'Class.meth', '', None)]) + assert_node(doctree[1][1][1], ([desc_signature, ([desc_annotation, "classmethod "], + [desc_name, "meth"], + [desc_parameterlist, ()])], + [desc_content, ()])) + assert 'Class.meth' in domain.objects + assert domain.objects['Class.meth'] == ('index', 'classmethod') + + +def test_pystaticmethod(app): + text = (".. py:class:: Class\n" + "\n" + " .. py:staticmethod:: meth\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', 'meth() (Class static method)', 'Class.meth', '', None)]) + assert_node(doctree[1][1][1], ([desc_signature, ([desc_annotation, "static "], + [desc_name, "meth"], + [desc_parameterlist, ()])], + [desc_content, ()])) + assert 'Class.meth' in domain.objects + assert domain.objects['Class.meth'] == ('index', 'staticmethod') From fcc964b66f9f5e36c0ea023d24e48072b34dfb7d Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sun, 7 Apr 2019 21:54:19 +0900 Subject: [PATCH 3/4] Add PyAttribute class; a directive for python attribute description --- sphinx/domains/python.py | 21 ++++++++++++++++++++- tests/test_domain_py.py | 19 +++++++++++++++++++ 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/sphinx/domains/python.py b/sphinx/domains/python.py index 492610659..c7e9e1b68 100644 --- a/sphinx/domains/python.py +++ b/sphinx/domains/python.py @@ -592,6 +592,25 @@ class PyStaticMethod(PyMethod): return _('%s() (%s static method)') % (methname, clsname) +class PyAttribute(PyObject): + """Description of an attribute.""" + + def get_index_text(self, modname, name_cls): + # type: (str, 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 attribute)') % (attrname, clsname) + + class PyDecoratorMixin: """ Mixin for decorator directives. @@ -817,7 +836,7 @@ class PythonDomain(Domain): 'method': PyMethod, 'classmethod': PyClassMethod, 'staticmethod': PyStaticMethod, - 'attribute': PyClassmember, + 'attribute': PyAttribute, 'module': PyModule, 'currentmodule': PyCurrentModule, 'decorator': PyDecoratorFunction, diff --git a/tests/test_domain_py.py b/tests/test_domain_py.py index a3836f523..afc34a697 100644 --- a/tests/test_domain_py.py +++ b/tests/test_domain_py.py @@ -353,3 +353,22 @@ def test_pystaticmethod(app): [desc_content, ()])) assert 'Class.meth' in domain.objects assert domain.objects['Class.meth'] == ('index', 'staticmethod') + + +def test_pyattribute(app): + text = (".. py:class:: Class\n" + "\n" + " .. py:attribute:: attr\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', 'attr (Class attribute)', 'Class.attr', '', None)]) + assert_node(doctree[1][1][1], ([desc_signature, desc_name, "attr"], + [desc_content, ()])) + assert 'Class.attr' in domain.objects + assert domain.objects['Class.attr'] == ('index', 'attribute') From b0b3f5a677162f97f7e0fb62428fa09468b0f23c Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sun, 7 Apr 2019 21:58:15 +0900 Subject: [PATCH 4/4] deprecate PyClassmember class --- CHANGES | 1 + doc/extdev/deprecated.rst | 8 ++++++++ sphinx/domains/python.py | 12 +++++++++++- 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index 8d129f2e0..87510437e 100644 --- a/CHANGES +++ b/CHANGES @@ -33,6 +33,7 @@ Deprecated * ``sphinx.directives.TabularColumns`` * ``sphinx.directives.TocTree`` * ``sphinx.directives.VersionChange`` +* ``sphinx.domains.python.PyClassmember`` * ``sphinx.domains.std.StandardDomain._resolve_citation_xref()`` * ``sphinx.domains.std.StandardDomain.note_citations()`` * ``sphinx.domains.std.StandardDomain.note_citation_refs()`` diff --git a/doc/extdev/deprecated.rst b/doc/extdev/deprecated.rst index beeafab08..ffe0bdccb 100644 --- a/doc/extdev/deprecated.rst +++ b/doc/extdev/deprecated.rst @@ -116,6 +116,14 @@ The following is a list of deprecated interfaces. - 4.0 - ``sphinx.directives.other.VersionChange`` + * - ``sphinx.domains.python.PyClassmember`` + - 2.1 + - 4.0 + - ``sphinx.domains.python.PyAttribute``, + ``sphinx.domains.python.PyMethod``, + ``sphinx.domains.python.PyClassMethod`` and + ``sphinx.domains.python.PyStaticMethod`` + * - ``sphinx.domains.std.StandardDomain._resolve_citation_xref()`` - 2.1 - 4.0 diff --git a/sphinx/domains/python.py b/sphinx/domains/python.py index c7e9e1b68..de31eef00 100644 --- a/sphinx/domains/python.py +++ b/sphinx/domains/python.py @@ -9,13 +9,16 @@ """ import re +import warnings from typing import cast from docutils import nodes from docutils.parsers.rst import directives from sphinx import addnodes, locale -from sphinx.deprecation import DeprecatedDict, RemovedInSphinx30Warning +from sphinx.deprecation import ( + DeprecatedDict, RemovedInSphinx30Warning, RemovedInSphinx40Warning +) from sphinx.directives import ObjectDescription from sphinx.domains import Domain, ObjType, Index, IndexEntry from sphinx.locale import _, __ @@ -453,6 +456,13 @@ class PyClassmember(PyObject): Description of a class member (methods, attributes). """ + def run(self): + # type: () -> List[nodes.Node] + warnings.warn('PyClassmember is deprecated.', + RemovedInSphinx40Warning) + + return super().run() + def needs_arglist(self): # type: () -> bool return self.objtype.endswith('method')