diff --git a/sphinx/ext/autodoc/__init__.py b/sphinx/ext/autodoc/__init__.py index 4c1032db5..be51c6e38 100644 --- a/sphinx/ext/autodoc/__init__.py +++ b/sphinx/ext/autodoc/__init__.py @@ -1269,6 +1269,7 @@ class MethodDocumenter(DocstringSignatureMixin, ClassLevelDocumenter): # type: Specialized Documenter subclass for methods (normal, static and class). """ objtype = 'method' + directivetype = 'method' member_order = 50 priority = 1 # must be more than FunctionDocumenter @@ -1289,16 +1290,11 @@ class MethodDocumenter(DocstringSignatureMixin, ClassLevelDocumenter): # type: if obj is None: obj = self.object - if inspect.isclassmethod(obj): - self.directivetype = 'classmethod' + if (inspect.isclassmethod(obj) or + inspect.isstaticmethod(obj, cls=self.parent, name=self.object_name)): # document class and static members before ordinary ones self.member_order = self.member_order - 1 - elif inspect.isstaticmethod(obj, cls=self.parent, name=self.object_name): - self.directivetype = 'staticmethod' - # document class and static members before ordinary ones - self.member_order = self.member_order - 1 - else: - self.directivetype = 'method' + return ret def format_args(self): @@ -1314,6 +1310,17 @@ class MethodDocumenter(DocstringSignatureMixin, ClassLevelDocumenter): # type: args = args.replace('\\', '\\\\') return args + def add_directive_header(self, sig): + # type: (str) -> None + super().add_directive_header(sig) + + sourcename = self.get_sourcename() + obj = self.parent.__dict__.get(self.object_name, self.object) + if inspect.isclassmethod(obj): + self.add_line(' :classmethod:', sourcename) + elif inspect.isstaticmethod(obj, cls=self.parent, name=self.object_name): + self.add_line(' :staticmethod:', sourcename) + def document_members(self, all_members=False): # type: (bool) -> None pass diff --git a/tests/test_autodoc.py b/tests/test_autodoc.py index 27412a9da..0bf69b74d 100644 --- a/tests/test_autodoc.py +++ b/tests/test_autodoc.py @@ -692,9 +692,9 @@ def test_autodoc_members(app): actual = do_autodoc(app, 'class', 'target.Base', options) assert list(filter(lambda l: '::' in l, actual)) == [ '.. py:class:: Base', - ' .. py:classmethod:: Base.inheritedclassmeth()', + ' .. py:method:: Base.inheritedclassmeth()', ' .. py:method:: Base.inheritedmeth()', - ' .. py:staticmethod:: Base.inheritedstaticmeth(cls)' + ' .. py:method:: Base.inheritedstaticmeth(cls)' ] # default specific-members @@ -703,7 +703,7 @@ def test_autodoc_members(app): assert list(filter(lambda l: '::' in l, actual)) == [ '.. py:class:: Base', ' .. py:method:: Base.inheritedmeth()', - ' .. py:staticmethod:: Base.inheritedstaticmeth(cls)' + ' .. py:method:: Base.inheritedstaticmeth(cls)' ] @@ -714,7 +714,7 @@ def test_autodoc_exclude_members(app): actual = do_autodoc(app, 'class', 'target.Base', options) assert list(filter(lambda l: '::' in l, actual)) == [ '.. py:class:: Base', - ' .. py:classmethod:: Base.inheritedclassmeth()' + ' .. py:method:: Base.inheritedclassmeth()' ] # members vs exclude-members @@ -742,9 +742,9 @@ def test_autodoc_undoc_members(app): ' .. py:attribute:: Class.inst_attr_string', ' .. py:attribute:: Class.mdocattr', ' .. py:method:: Class.meth()', - ' .. py:classmethod:: Class.moore(a, e, f) -> happiness', + ' .. py:method:: Class.moore(a, e, f) -> happiness', ' .. py:attribute:: Class.prop', - ' .. py:classmethod:: Class.roger(a, *, b=2, c=3, d=4, e=5, f=6)', + ' .. py:method:: Class.roger(a, *, b=2, c=3, d=4, e=5, f=6)', ' .. py:attribute:: Class.skipattr', ' .. py:method:: Class.skipmeth()', ' .. py:attribute:: Class.udocattr', @@ -759,11 +759,11 @@ def test_autodoc_inherited_members(app): actual = do_autodoc(app, 'class', 'target.Class', options) assert list(filter(lambda l: 'method::' in l, actual)) == [ ' .. py:method:: Class.excludemeth()', - ' .. py:classmethod:: Class.inheritedclassmeth()', + ' .. py:method:: Class.inheritedclassmeth()', ' .. py:method:: Class.inheritedmeth()', - ' .. py:staticmethod:: Class.inheritedstaticmeth(cls)', + ' .. py:method:: Class.inheritedstaticmeth(cls)', ' .. py:method:: Class.meth()', - ' .. py:classmethod:: Class.moore(a, e, f) -> happiness', + ' .. py:method:: Class.moore(a, e, f) -> happiness', ' .. py:method:: Class.skipmeth()' ] @@ -822,9 +822,9 @@ def test_autodoc_special_members(app): ' .. py:attribute:: Class.inst_attr_string', ' .. py:attribute:: Class.mdocattr', ' .. py:method:: Class.meth()', - ' .. py:classmethod:: Class.moore(a, e, f) -> happiness', + ' .. py:method:: Class.moore(a, e, f) -> happiness', ' .. py:attribute:: Class.prop', - ' .. py:classmethod:: Class.roger(a, *, b=2, c=3, d=4, e=5, f=6)', + ' .. py:method:: Class.roger(a, *, b=2, c=3, d=4, e=5, f=6)', ' .. py:attribute:: Class.skipattr', ' .. py:method:: Class.skipmeth()', ' .. py:attribute:: Class.udocattr', @@ -942,6 +942,34 @@ def test_autodoc_inner_class(app): ] +@pytest.mark.sphinx('html', testroot='ext-autodoc') +def test_autodoc_classmethod(app): + actual = do_autodoc(app, 'method', 'target.Base.inheritedclassmeth') + assert list(actual) == [ + '', + '.. py:method:: Base.inheritedclassmeth()', + ' :module: target', + ' :classmethod:', + '', + ' Inherited class method.', + ' ' + ] + + +@pytest.mark.sphinx('html', testroot='ext-autodoc') +def test_autodoc_staticmethod(app): + actual = do_autodoc(app, 'method', 'target.Base.inheritedstaticmeth') + assert list(actual) == [ + '', + '.. py:method:: Base.inheritedstaticmeth(cls)', + ' :module: target', + ' :staticmethod:', + '', + ' Inherited static method.', + ' ' + ] + + @pytest.mark.sphinx('html', testroot='ext-autodoc') def test_autodoc_descriptor(app): actual = do_autodoc(app, 'attribute', 'target.Class.descr') @@ -991,8 +1019,8 @@ def test_autodoc_member_order(app): ' .. py:attribute:: Class.docattr', ' .. py:attribute:: Class.udocattr', ' .. py:attribute:: Class.mdocattr', - ' .. py:classmethod:: Class.roger(a, *, b=2, c=3, d=4, e=5, f=6)', - ' .. py:classmethod:: Class.moore(a, e, f) -> happiness', + ' .. py:method:: Class.roger(a, *, b=2, c=3, d=4, e=5, f=6)', + ' .. py:method:: Class.moore(a, e, f) -> happiness', ' .. py:attribute:: Class.inst_attr_inline', ' .. py:attribute:: Class.inst_attr_comment', ' .. py:attribute:: Class.inst_attr_string', @@ -1009,8 +1037,8 @@ def test_autodoc_member_order(app): '.. py:class:: Class(arg)', ' .. py:method:: Class.excludemeth()', ' .. py:method:: Class.meth()', - ' .. py:classmethod:: Class.moore(a, e, f) -> happiness', - ' .. py:classmethod:: Class.roger(a, *, b=2, c=3, d=4, e=5, f=6)', + ' .. py:method:: Class.moore(a, e, f) -> happiness', + ' .. py:method:: Class.roger(a, *, b=2, c=3, d=4, e=5, f=6)', ' .. py:method:: Class.skipmeth()', ' .. py:method:: Class.undocmeth()', ' .. py:attribute:: Class._private_inst_attr', @@ -1043,9 +1071,9 @@ def test_autodoc_member_order(app): ' .. py:attribute:: Class.inst_attr_string', ' .. py:attribute:: Class.mdocattr', ' .. py:method:: Class.meth()', - ' .. py:classmethod:: Class.moore(a, e, f) -> happiness', + ' .. py:method:: Class.moore(a, e, f) -> happiness', ' .. py:attribute:: Class.prop', - ' .. py:classmethod:: Class.roger(a, *, b=2, c=3, d=4, e=5, f=6)', + ' .. py:method:: Class.roger(a, *, b=2, c=3, d=4, e=5, f=6)', ' .. py:attribute:: Class.skipattr', ' .. py:method:: Class.skipmeth()', ' .. py:attribute:: Class.udocattr', @@ -1642,7 +1670,7 @@ def test_autodoc_default_options_with_values(app): ' .. py:attribute:: Class.docattr', ' .. py:attribute:: Class.udocattr', ' .. py:attribute:: Class.mdocattr', - ' .. py:classmethod:: Class.moore(a, e, f) -> happiness', + ' .. py:method:: Class.moore(a, e, f) -> happiness', ' .. py:attribute:: Class.inst_attr_inline', ' .. py:attribute:: Class.inst_attr_comment', ' .. py:attribute:: Class.inst_attr_string',