From 295323d5aac2127f222f2efbe065250f5c5c3111 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Fri, 19 Jan 2018 14:27:14 +0900 Subject: [PATCH] Fix #4415: autodoc classifies inherited staticmethods as regular methods --- CHANGES | 1 + sphinx/ext/autodoc/__init__.py | 4 ++-- sphinx/util/inspect.py | 20 +++++++++++++++++++ .../roots/test-ext-autodoc/target/__init__.py | 4 ++++ tests/test_autodoc.py | 2 ++ 5 files changed, 29 insertions(+), 2 deletions(-) diff --git a/CHANGES b/CHANGES index 8e0726701..e874a49e9 100644 --- a/CHANGES +++ b/CHANGES @@ -17,6 +17,7 @@ Bugs fixed ---------- * #4415: autodoc classifies inherited classmethods as regular methods +* #4415: autodoc classifies inherited staticmethods as regular methods Testing -------- diff --git a/sphinx/ext/autodoc/__init__.py b/sphinx/ext/autodoc/__init__.py index 599492bab..1597d0c1b 100644 --- a/sphinx/ext/autodoc/__init__.py +++ b/sphinx/ext/autodoc/__init__.py @@ -32,7 +32,7 @@ from sphinx.application import ExtensionError from sphinx.util import logging from sphinx.util.inspect import Signature, isdescriptor, safe_getmembers, \ safe_getattr, object_description, is_builtin_class_method, \ - isenumattribute, isclassmethod, getdoc + isenumattribute, isclassmethod, isstaticmethod, getdoc from sphinx.util.docstrings import prepare_docstring if False: @@ -1268,7 +1268,7 @@ class MethodDocumenter(DocstringSignatureMixin, ClassLevelDocumenter): # type: self.directivetype = 'classmethod' # document class and static members before ordinary ones self.member_order = self.member_order - 1 - elif isinstance(obj, staticmethod): + elif 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 diff --git a/sphinx/util/inspect.py b/sphinx/util/inspect.py index 0ddfa8213..5ed39906e 100644 --- a/sphinx/util/inspect.py +++ b/sphinx/util/inspect.py @@ -168,6 +168,26 @@ def isclassmethod(obj): return False +def isstaticmethod(obj, cls=None, name=None): + # type: (Any, Any, unicode) -> bool + """Check if the object is staticmethod.""" + if isinstance(obj, staticmethod): + return True + elif cls and name: + # trace __mro__ if the method is defined in parent class + # + # .. note:: This only works with new style classes. + for basecls in getattr(cls, '__mro__', []): + meth = basecls.__dict__.get(name) + if meth: + if isinstance(meth, staticmethod): + return True + else: + return False + + return False + + def isdescriptor(x): # type: (Any) -> bool """Check if the object is some kind of descriptor.""" diff --git a/tests/roots/test-ext-autodoc/target/__init__.py b/tests/roots/test-ext-autodoc/target/__init__.py index d0d668b8e..b657f595f 100644 --- a/tests/roots/test-ext-autodoc/target/__init__.py +++ b/tests/roots/test-ext-autodoc/target/__init__.py @@ -68,6 +68,10 @@ class Base(object): def inheritedclassmeth(cls): """Inherited class method.""" + @staticmethod + def inheritedstaticmeth(cls): + """Inherited static method.""" + class Derived(Base): def inheritedmeth(self): diff --git a/tests/test_autodoc.py b/tests/test_autodoc.py index f36a78046..55c4b8847 100644 --- a/tests/test_autodoc.py +++ b/tests/test_autodoc.py @@ -716,6 +716,7 @@ def test_generate(): options.inherited_members = True should.append(('method', 'target.Class.inheritedmeth')) should.append(('method', 'target.Class.inheritedclassmeth')) + should.append(('method', 'target.Class.inheritedstaticmeth')) assert_processes(should, 'class', 'Class') # test special members @@ -801,6 +802,7 @@ def test_generate(): ' .. py:attribute:: Class._private_inst_attr', ' .. py:classmethod:: Class.inheritedclassmeth()', ' .. py:method:: Class.inheritedmeth()', + ' .. py:staticmethod:: Class.inheritedstaticmeth()', ], 'class', 'Class', member_order='bysource', all_members=True) del directive.env.ref_context['py:module']