Commits

Jon Waltman committed 5b8bcfa

autodoc: Handle explicit instance attributes in :members: (re #904)

Comments (0)

Files changed (3)

sphinx/ext/autodoc.py

         If *want_all* is True, return all members.  Else, only return those
         members given by *self.options.members* (which may also be none).
         """
+        analyzed_member_names = set()
+        if self.analyzer:
+            attr_docs = self.analyzer.find_attr_docs()
+            namespace = '.'.join(self.objpath)
+            for item in attr_docs.iteritems():
+                if item[0][0] == namespace:
+                    analyzed_member_names.add(item[0][1])
         if not want_all:
             if not self.options.members:
                 return False, []
             # specific members given
-            ret = []
+            members = []
             for mname in self.options.members:
                 try:
-                    ret.append((mname, self.get_attr(self.object, mname)))
+                    members.append((mname, self.get_attr(self.object, mname)))
                 except AttributeError:
-                    self.directive.warn('missing attribute %s in object %s'
-                                        % (mname, self.fullname))
-            return False, ret
-
-        if self.options.inherited_members:
+                    if mname not in analyzed_member_names:
+                        self.directive.warn('missing attribute %s in object %s'
+                                            % (mname, self.fullname))
+        elif self.options.inherited_members:
             # safe_getmembers() uses dir() which pulls in members from all
             # base classes
             members = safe_getmembers(self.object)
                            for mname in obj_dict.keys()]
         membernames = set(m[0] for m in members)
         # add instance attributes from the analyzer
-        if self.analyzer:
-            attr_docs = self.analyzer.find_attr_docs()
-            namespace = '.'.join(self.objpath)
-            for item in attr_docs.iteritems():
-                if item[0][0] == namespace:
-                    if item[0][1] not in membernames:
-                        members.append((item[0][1], INSTANCEATTR))
+        for aname in analyzed_member_names:
+            if aname not in membernames and \
+               (want_all or aname in self.options.members):
+                members.append((aname, INSTANCEATTR))
         return False, sorted(members)
 
     def filter_members(self, members, want_all):

tests/root/autodoc.txt

    :noindex:
 
    .. autoclass:: MarkupError
+
+
+.. currentmodule:: test_autodoc
+
+.. autoclass:: InstAttCls
+   :members:
+
+   All members (5 total)
+
+.. autoclass:: InstAttCls
+   :members: ca1, ia1
+
+   Specific members (2 total)

tests/test_autodoc.py

     assert_result_contains(
         '   :annotation: = None', 'attribute', 'AttCls.a2')
 
+    # test explicit members with instance attributes
+    del directive.env.temp_data['autodoc:class']
+    del directive.env.temp_data['autodoc:module']
+    directive.env.temp_data['py:module'] = 'test_autodoc'
+    options.inherited_members = False
+    options.undoc_members = False
+    options.members = ALL
+    assert_processes([
+        ('class', 'test_autodoc.InstAttCls'),
+        ('attribute', 'test_autodoc.InstAttCls.ca1'),
+        ('attribute', 'test_autodoc.InstAttCls.ca2'),
+        ('attribute', 'test_autodoc.InstAttCls.ca3'),
+        ('attribute', 'test_autodoc.InstAttCls.ia1'),
+        ('attribute', 'test_autodoc.InstAttCls.ia2'),
+    ], 'class', 'InstAttCls')
+    del directive.env.temp_data['autodoc:class']
+    del directive.env.temp_data['autodoc:module']
+    options.members = ['ca1', 'ia1']
+    assert_processes([
+        ('class', 'test_autodoc.InstAttCls'),
+        ('attribute', 'test_autodoc.InstAttCls.ca1'),
+        ('attribute', 'test_autodoc.InstAttCls.ia1'),
+    ], 'class', 'InstAttCls')
+    del directive.env.temp_data['autodoc:class']
+    del directive.env.temp_data['autodoc:module']
+    del directive.env.temp_data['py:module']
 
 # --- generate fodder ------------
 
 class AttCls(object):
     a1 = StrRepr('hello\nworld')
     a2 = None
+
+class InstAttCls(object):
+    """Class with documented class and instance attributes."""
+
+    #: Doc comment for class attribute InstAttCls.ca1.
+    #: It can have multiple lines.
+    ca1 = 'a'
+
+    ca2 = 'b'    #: Doc comment for InstAttCls.ca2. One line only.
+
+    ca3 = 'c'
+    """Docstring for class attribute InstAttCls.ca3."""
+
+    def __init__(self):
+        #: Doc comment for instance attribute InstAttCls.ia1
+        self.ia1 = 'd'
+
+        self.ia2 = 'e'
+        """Docstring for instance attribute InstAttCls.ia2."""
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.