diff --git a/sphinx/ext/autodoc.py b/sphinx/ext/autodoc.py index a224fe61e..4d8a44fa6 100644 --- a/sphinx/ext/autodoc.py +++ b/sphinx/ext/autodoc.py @@ -28,7 +28,7 @@ from sphinx.application import ExtensionError from sphinx.util.nodes import nested_parse_with_titles from sphinx.util.compat import Directive from sphinx.util.inspect import getargspec, isdescriptor, safe_getmembers, \ - safe_getattr, safe_repr + safe_getattr, safe_repr, is_builtin_class_method from sphinx.util.pycompat import base_exception, class_types from sphinx.util.docstrings import prepare_docstring @@ -957,6 +957,10 @@ class FunctionDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): try: argspec = getargspec(self.object) except TypeError: + if (is_builtin_class_method(self.object, '__new__') and + is_builtin_class_method(self.object, '__init__')): + raise TypeError('%r is a builtin class' % self.object) + # if a class should be documented as function (yay duck # typing) we try to use the constructor signature as function # signature without the first argument. @@ -1009,8 +1013,9 @@ class ClassDocumenter(ModuleLevelDocumenter): initmeth = self.get_attr(self.object, '__init__', None) # classes without __init__ method, default __init__ or # __init__ written in C? - if initmeth is None or initmeth is object.__init__ or not \ - (inspect.ismethod(initmeth) or inspect.isfunction(initmeth)): + if initmeth is None or \ + is_builtin_class_method(self.object, '__init__') or \ + not(inspect.ismethod(initmeth) or inspect.isfunction(initmeth)): return None try: argspec = getargspec(initmeth) diff --git a/sphinx/util/inspect.py b/sphinx/util/inspect.py index 4c337ef91..61061a9af 100644 --- a/sphinx/util/inspect.py +++ b/sphinx/util/inspect.py @@ -137,3 +137,20 @@ def safe_repr(object): if isinstance(s, bytes): return force_decode(s, None).replace('\n', ' ') return s.replace('\n', ' ') + + +def is_builtin_class_method(obj, attr_name): + """If attr_name is implemented at builtin class, return True. + + >>> is_builtin_class_method(int, '__init__') + True + + Why this function needed? CPython implements int.__init__ by Descriptor + but PyPy implements it by pure Python code. + """ + classes = [c for c in inspect.getmro(obj) if attr_name in c.__dict__] + cls = classes[0] if classes else object + + if not hasattr(__builtins__, cls.__name__): + return False + return getattr(__builtins__, cls.__name__) is cls