mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
#187: Added support for source ordering of members in autodoc, with `autodoc_member_order = 'bysource'
`.
This commit is contained in:
parent
9a316a21a0
commit
a31e7e2f5d
3
CHANGES
3
CHANGES
@ -10,6 +10,9 @@ Release 1.0 (in development)
|
|||||||
|
|
||||||
* Added a manual page builder.
|
* 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,
|
* In HTML output, inline roles now get a CSS class with their name,
|
||||||
allowing styles to customize their appearance. Domain-specific
|
allowing styles to customize their appearance. Domain-specific
|
||||||
roles get two classes, ``domain`` and ``domain-rolename``.
|
roles get two classes, ``domain`` and ``domain-rolename``.
|
||||||
|
@ -223,10 +223,16 @@ There are also new config values that you can set:
|
|||||||
.. confval:: autodoc_member_order
|
.. confval:: autodoc_member_order
|
||||||
|
|
||||||
This value selects if automatically documented members are sorted
|
This value selects if automatically documented members are sorted
|
||||||
alphabetical (value ``'alphabetical'``) or by member type (value
|
alphabetical (value ``'alphabetical'``), by member type (value
|
||||||
``'groupwise'``). The default is alphabetical.
|
``'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
|
.. versionadded:: 0.6
|
||||||
|
.. versionchanged:: 1.0
|
||||||
|
Support for ``'bysource'``.
|
||||||
|
|
||||||
.. confval:: autodoc_default_flags
|
.. confval:: autodoc_default_flags
|
||||||
|
|
||||||
|
@ -599,12 +599,19 @@ class Documenter(object):
|
|||||||
'.'.join(self.objpath + [mname])
|
'.'.join(self.objpath + [mname])
|
||||||
memberdocumenters.append(
|
memberdocumenters.append(
|
||||||
classes[-1](self.directive, full_mname, self.indent))
|
classes[-1](self.directive, full_mname, self.indent))
|
||||||
|
member_order = self.options.member_order or \
|
||||||
if (self.options.member_order or self.env.config.autodoc_member_order) \
|
self.env.config.autodoc_member_order
|
||||||
== 'groupwise':
|
if member_order == 'groupwise':
|
||||||
# sort by group; relies on stable sort to keep items in the
|
# sort by group; relies on stable sort to keep items in the
|
||||||
# same group sorted alphabetically
|
# same group sorted alphabetically
|
||||||
memberdocumenters.sort(key=lambda d: d.member_order)
|
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:
|
for documenter in memberdocumenters:
|
||||||
documenter.generate(all_members=True,
|
documenter.generate(all_members=True,
|
||||||
|
@ -58,9 +58,17 @@ class AttrDocVisitor(nodes.NodeVisitor):
|
|||||||
self.encoding = encoding
|
self.encoding = encoding
|
||||||
self.namespace = []
|
self.namespace = []
|
||||||
self.collected = {}
|
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):
|
def visit_classdef(self, node):
|
||||||
"""Visit a class."""
|
"""Visit a class."""
|
||||||
|
self.add_tag(node[1].value)
|
||||||
self.namespace.append(node[1].value)
|
self.namespace.append(node[1].value)
|
||||||
self.generic_visit(node)
|
self.generic_visit(node)
|
||||||
self.namespace.pop()
|
self.namespace.pop()
|
||||||
@ -68,6 +76,7 @@ class AttrDocVisitor(nodes.NodeVisitor):
|
|||||||
def visit_funcdef(self, node):
|
def visit_funcdef(self, node):
|
||||||
"""Visit a function (or method)."""
|
"""Visit a function (or method)."""
|
||||||
# usually, don't descend into functions -- nothing interesting there
|
# usually, don't descend into functions -- nothing interesting there
|
||||||
|
self.add_tag(node[1].value)
|
||||||
if node[1].value == '__init__':
|
if node[1].value == '__init__':
|
||||||
# however, collect attributes set in __init__ methods
|
# however, collect attributes set in __init__ methods
|
||||||
self.in_init += 1
|
self.in_init += 1
|
||||||
@ -91,8 +100,7 @@ class AttrDocVisitor(nodes.NodeVisitor):
|
|||||||
prefix = pnode.get_prefix()
|
prefix = pnode.get_prefix()
|
||||||
prefix = prefix.decode(self.encoding)
|
prefix = prefix.decode(self.encoding)
|
||||||
docstring = prepare_commentdoc(prefix)
|
docstring = prepare_commentdoc(prefix)
|
||||||
if docstring:
|
self.add_docstring(node, docstring)
|
||||||
self.add_docstring(node, docstring)
|
|
||||||
|
|
||||||
def visit_simple_stmt(self, node):
|
def visit_simple_stmt(self, node):
|
||||||
"""Visit a docstring statement which may have an assignment before."""
|
"""Visit a docstring statement which may have an assignment before."""
|
||||||
@ -133,9 +141,11 @@ class AttrDocVisitor(nodes.NodeVisitor):
|
|||||||
continue
|
continue
|
||||||
else:
|
else:
|
||||||
name = target.value
|
name = target.value
|
||||||
namespace = '.'.join(self.namespace)
|
self.add_tag(name)
|
||||||
if namespace.startswith(self.scope):
|
if docstring:
|
||||||
self.collected[namespace, name] = docstring
|
namespace = '.'.join(self.namespace)
|
||||||
|
if namespace.startswith(self.scope):
|
||||||
|
self.collected[namespace, name] = docstring
|
||||||
|
|
||||||
|
|
||||||
class ModuleAnalyzer(object):
|
class ModuleAnalyzer(object):
|
||||||
@ -197,6 +207,7 @@ class ModuleAnalyzer(object):
|
|||||||
self.parsetree = None
|
self.parsetree = None
|
||||||
# will be filled by find_attr_docs()
|
# will be filled by find_attr_docs()
|
||||||
self.attr_docs = None
|
self.attr_docs = None
|
||||||
|
self.tagorder = None
|
||||||
# will be filled by find_tags()
|
# will be filled by find_tags()
|
||||||
self.tags = None
|
self.tags = None
|
||||||
|
|
||||||
@ -234,6 +245,7 @@ class ModuleAnalyzer(object):
|
|||||||
attr_visitor = AttrDocVisitor(number2name, scope, self.encoding)
|
attr_visitor = AttrDocVisitor(number2name, scope, self.encoding)
|
||||||
attr_visitor.visit(self.parsetree)
|
attr_visitor.visit(self.parsetree)
|
||||||
self.attr_docs = attr_visitor.collected
|
self.attr_docs = attr_visitor.collected
|
||||||
|
self.tagorder = attr_visitor.tagorder
|
||||||
# now that we found everything we could in the tree, throw it away
|
# now that we found everything we could in the tree, throw it away
|
||||||
# (it takes quite a bit of memory for large modules)
|
# (it takes quite a bit of memory for large modules)
|
||||||
self.parsetree = None
|
self.parsetree = None
|
||||||
|
@ -341,6 +341,26 @@ def test_generate():
|
|||||||
assert item in directive.result
|
assert item in directive.result
|
||||||
del 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 = []
|
options.members = []
|
||||||
|
|
||||||
# no module found?
|
# no module found?
|
||||||
@ -442,6 +462,22 @@ def test_generate():
|
|||||||
assert_processes([('function', 'time.asctime')], 'function', 'asctime')
|
assert_processes([('function', 'time.asctime')], 'function', 'asctime')
|
||||||
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 ------------
|
# --- generate fodder ------------
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user