Georg Brandl avatar Georg Brandl committed 3474d53

#187: Added support for source ordering of members in autodoc, with ``autodoc_member_order = 'bysource'``.

Comments (0)

Files changed (5)

 
 * Added a manual page builder.
 
+* Added support for source ordering of members in autodoc, with
+  ``autodoc_member_order = 'bysource'``.
+
 * In HTML output, inline roles now get a CSS class with their name,
   allowing styles to customize their appearance.  Domain-specific
   roles get two classes, ``domain`` and ``domain-rolename``.

doc/ext/autodoc.rst

 .. confval:: autodoc_member_order
 
    This value selects if automatically documented members are sorted
-   alphabetical (value ``'alphabetical'``) or by member type (value
-   ``'groupwise'``).  The default is alphabetical.
+   alphabetical (value ``'alphabetical'``), by member type (value
+   ``'groupwise'``) or by source order (value ``'bysource'``).  The default is
+   alphabetical.
+
+   Note that for source order, the module must be a Python module with the
+   source code available.
 
    .. versionadded:: 0.6
+   .. versionchanged:: 1.0
+      Support for ``'bysource'``.
 
 .. confval:: autodoc_default_flags
 

sphinx/ext/autodoc.py

                               '.'.join(self.objpath + [mname])
             memberdocumenters.append(
                 classes[-1](self.directive, full_mname, self.indent))
-
-        if (self.options.member_order or self.env.config.autodoc_member_order) \
-               == 'groupwise':
+        member_order = self.options.member_order or \
+                       self.env.config.autodoc_member_order
+        if member_order == 'groupwise':
             # sort by group; relies on stable sort to keep items in the
             # same group sorted alphabetically
             memberdocumenters.sort(key=lambda d: d.member_order)
+        elif member_order == 'bysource' and self.analyzer:
+            # sort by source order, by virtue of the module analyzer
+            tagorder = self.analyzer.tagorder
+            def keyfunc(documenter):
+                fullname = documenter.name.split('::')[1]
+                return tagorder.get(fullname, len(tagorder))
+            memberdocumenters.sort(key=keyfunc)
 
         for documenter in memberdocumenters:
             documenter.generate(all_members=True,

sphinx/pycode/__init__.py

         self.encoding = encoding
         self.namespace = []
         self.collected = {}
+        self.tagnumber = 0
+        self.tagorder = {}
+
+    def add_tag(self, name):
+        name = '.'.join(self.namespace + [name])
+        self.tagorder[name] = self.tagnumber
+        self.tagnumber += 1
 
     def visit_classdef(self, node):
         """Visit a class."""
+        self.add_tag(node[1].value)
         self.namespace.append(node[1].value)
         self.generic_visit(node)
         self.namespace.pop()
     def visit_funcdef(self, node):
         """Visit a function (or method)."""
         # usually, don't descend into functions -- nothing interesting there
+        self.add_tag(node[1].value)
         if node[1].value == '__init__':
             # however, collect attributes set in __init__ methods
             self.in_init += 1
             prefix = pnode.get_prefix()
         prefix = prefix.decode(self.encoding)
         docstring = prepare_commentdoc(prefix)
-        if docstring:
-            self.add_docstring(node, docstring)
+        self.add_docstring(node, docstring)
 
     def visit_simple_stmt(self, node):
         """Visit a docstring statement which may have an assignment before."""
                 continue
             else:
                 name = target.value
-            namespace = '.'.join(self.namespace)
-            if namespace.startswith(self.scope):
-                self.collected[namespace, name] = docstring
+            self.add_tag(name)
+            if docstring:
+                namespace = '.'.join(self.namespace)
+                if namespace.startswith(self.scope):
+                    self.collected[namespace, name] = docstring
 
 
 class ModuleAnalyzer(object):
         self.parsetree = None
         # will be filled by find_attr_docs()
         self.attr_docs = None
+        self.tagorder = None
         # will be filled by find_tags()
         self.tags = None
 
         attr_visitor = AttrDocVisitor(number2name, scope, self.encoding)
         attr_visitor.visit(self.parsetree)
         self.attr_docs = attr_visitor.collected
+        self.tagorder = attr_visitor.tagorder
         # now that we found everything we could in the tree, throw it away
         # (it takes quite a bit of memory for large modules)
         self.parsetree = None

tests/test_autodoc.py

         assert item in directive.result
         del directive.result[:]
 
+    def assert_order(items, objtype, name, member_order, **kw):
+        inst = AutoDirective._registry[objtype](directive, name)
+        inst.options.member_order = member_order
+        inst.generate(**kw)
+        assert len(_warnings) == 0, _warnings
+        items = list(reversed(items))
+        lineiter = iter(directive.result)
+        #for line in directive.result:
+        #    if line.strip():
+        #        print repr(line)
+        while items:
+            item = items.pop()
+            for line in lineiter:
+                if line == item:
+                    break
+            else:  # ran out of items!
+                assert False, 'item %r not found in result or not in the ' \
+                       ' correct order' % item
+        del directive.result[:]
+
     options.members = []
 
     # no module found?
     assert_processes([('function', 'time.asctime')], 'function', 'asctime')
     assert_processes([('function', 'time.asctime')], 'function', 'asctime')
 
+    # test autodoc_member_order == 'source'
+    directive.env.temp_data['py:module'] = 'test_autodoc'
+    assert_order(['.. py:class:: Class(arg)',
+                  '   .. py:attribute:: Class.descr',
+                  '   .. py:method:: Class.meth()',
+                  '   .. py:method:: Class.undocmeth()',
+                  '   .. py:attribute:: Class.attr',
+                  '   .. py:attribute:: Class.prop',
+                  '   .. py:attribute:: Class.docattr',
+                  '   .. py:attribute:: Class.udocattr',
+                  '   .. py:attribute:: Class.inst_attr_comment',
+                  '   .. py:attribute:: Class.inst_attr_string',
+                  '   .. py:method:: Class.inheritedmeth()',
+                  ],
+                 'class', 'Class', member_order='bysource', all_members=True)
+
 
 # --- generate fodder ------------
 
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.