From def08a206fd885557381309604b7bbd776cdc03c Mon Sep 17 00:00:00 2001 From: "Erik M. Bray" Date: Thu, 9 Apr 2015 19:09:24 -0400 Subject: [PATCH] Fix documentation of descriptor classes that have a custom metaclass (i.e. a subclass of type). This avoids using string comparisons to do type checks, except in the case of the fix for #1155, since in Python 3 there really seems to be no way to export the instancemethod type, short of using ctypes. --- sphinx/ext/autodoc.py | 14 +++++++++++--- tests/test_autodoc.py | 15 +++++++++++++-- 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/sphinx/ext/autodoc.py b/sphinx/ext/autodoc.py index ff6a30ecc..1fab725d7 100644 --- a/sphinx/ext/autodoc.py +++ b/sphinx/ext/autodoc.py @@ -34,6 +34,11 @@ from sphinx.util.inspect import getargspec, isdescriptor, safe_getmembers, \ from sphinx.util.docstrings import prepare_docstring +# This type isn't exposed directly in any modules, but can be found +# here in most Python versions +MethodDescriptorType = type(type.__subclasses__) + + #: extended signature RE: with explicit module name separated by :: py_ext_sig_re = re.compile( r'''^ ([\w.]+::)? # explicit module name @@ -1309,10 +1314,13 @@ class AttributeDocumenter(DocstringStripSignatureMixin, ClassLevelDocumenter): @classmethod def can_document_member(cls, member, membername, isattr, parent): + non_attr_types = cls.method_types + (type, MethodDescriptorType) isdatadesc = isdescriptor(member) and not \ - isinstance(member, cls.method_types) and not \ - type(member).__name__ in ("type", "method_descriptor", - "instancemethod") + isinstance(member, non_attr_types) and not \ + type(member).__name__ == "instancemethod" + # That last condition addresses an obscure case of C-defined + # methods using a deprecated type in Python 3, that is not otherwise + # exported anywhere by Python return isdatadesc or (not isinstance(parent, ModuleDocumenter) and not inspect.isroutine(member) and not isinstance(member, class_types)) diff --git a/tests/test_autodoc.py b/tests/test_autodoc.py index 2d61edabd..a5d68b271 100644 --- a/tests/test_autodoc.py +++ b/tests/test_autodoc.py @@ -14,7 +14,7 @@ from util import TestApp, Struct, raises from nose.tools import with_setup -from six import StringIO +from six import StringIO, add_metaclass from docutils.statemachine import ViewList from sphinx.ext.autodoc import AutoDirective, add_documenter, \ @@ -784,11 +784,13 @@ def test_generate(): del directive.env.ref_context['py:module'] # test descriptor class documentation - options.members = ['CustomDataDescriptor'] + options.members = ['CustomDataDescriptor', 'CustomDataDescriptor2'] assert_result_contains('.. py:class:: CustomDataDescriptor(doc)', 'module', 'test_autodoc') assert_result_contains(' .. py:method:: CustomDataDescriptor.meth()', 'module', 'test_autodoc') + assert_result_contains('.. py:class:: CustomDataDescriptor2(doc)', + 'module', 'test_autodoc') # --- generate fodder ------------ @@ -818,6 +820,15 @@ class CustomDataDescriptor(object): """Function.""" return "The Answer" + +class CustomDataDescriptorMeta(type): + """Descriptor metaclass docstring.""" + +@add_metaclass(CustomDataDescriptorMeta) +class CustomDataDescriptor2(CustomDataDescriptor): + """Descriptor class with custom metaclass docstring.""" + + def _funky_classmethod(name, b, c, d, docstring=None): """Generates a classmethod for a class from a template by filling out some arguments."""