6
Sphinx extension that adds an autosummary:: directive, which can be
7
used to generate function/method/attribute/etc. summary lists, similar
8
to those output eg. by Epydoc and other API doc generation tools.
10
An :autolink: role is also provided.
15
The autosummary directive has the form::
25
and it generates an output table (containing signatures, optionally)
27
======================== =============================================
28
module.function_1(args) Summary line from the docstring of function_1
29
module.function_2(args) Summary line from the docstring
31
======================== =============================================
33
If the :toctree: option is specified, files matching the function names
34
are inserted to the toctree with the given prefix:
36
generated/module.function_1
37
generated/module.function_2
40
Note: The file names contain the module:: or currentmodule:: prefixes.
42
.. seealso:: autosummary_generate.py
48
The autolink role functions as ``:obj:`` when the name referred can be
49
resolved to a Python object, and otherwise it becomes simple emphasis.
50
This can be used as the default role to make links 'smart'.
53
import sys, os, posixpath, re
55
from docutils.parsers.rst import directives
56
from docutils.statemachine import ViewList
57
from docutils import nodes
59
import sphinx.addnodes, sphinx.roles
60
from sphinx.util import patfilter
62
from docscrape_sphinx import get_doc_object
66
"The numpydoc.autosummary extension can also be found as "
67
"sphinx.ext.autosummary in Sphinx >= 0.6, and the version in "
68
"Sphinx >= 0.7 is superior to the one in numpydoc. This numpydoc "
69
"version of autosummary is no longer maintained.",
70
DeprecationWarning, stacklevel=2)
73
app.add_directive('autosummary', autosummary_directive, True, (0, 0, False),
74
toctree=directives.unchanged,
75
nosignatures=directives.flag)
76
app.add_role('autolink', autolink_role)
78
app.add_node(autosummary_toc,
79
html=(autosummary_toc_visit_html, autosummary_toc_depart_noop),
80
latex=(autosummary_toc_visit_latex, autosummary_toc_depart_noop))
81
app.connect('doctree-read', process_autosummary_toc)
83
#------------------------------------------------------------------------------
84
# autosummary_toc node
85
#------------------------------------------------------------------------------
87
class autosummary_toc(nodes.comment):
90
def process_autosummary_toc(app, doctree):
92
Insert items described in autosummary:: to the TOC tree, but do
93
not generate the toctree:: list.
98
def crawl_toc(node, depth=1):
100
for j, subnode in enumerate(node):
102
if (isinstance(subnode, autosummary_toc)
103
and isinstance(subnode[0], sphinx.addnodes.toctree)):
104
env.note_toctree(env.docname, subnode[0])
108
if not isinstance(subnode, nodes.section):
110
if subnode not in crawled:
111
crawl_toc(subnode, depth+1)
114
def autosummary_toc_visit_html(self, node):
115
"""Hide autosummary toctree list in HTML output"""
118
def autosummary_toc_visit_latex(self, node):
119
"""Show autosummary toctree (= put the referenced pages here) in Latex"""
122
def autosummary_toc_depart_noop(self, node):
125
#------------------------------------------------------------------------------
127
#------------------------------------------------------------------------------
129
def autosummary_directive(dirname, arguments, options, content, lineno,
130
content_offset, block_text, state, state_machine):
132
Pretty table containing short signatures and summaries of functions etc.
134
autosummary also generates a (hidden) toctree:: node.
139
names += [x.strip().split()[0] for x in content
140
if x.strip() and re.search(r'^[a-zA-Z_]', x.strip()[0])]
142
table, warnings, real_names = get_autosummary(names, state,
143
'nosignatures' in options)
146
env = state.document.settings.env
147
suffix = env.config.source_suffix
148
all_docnames = env.found_docs.copy()
149
dirname = posixpath.dirname(env.docname)
151
if 'toctree' in options:
152
tree_prefix = options['toctree'].strip()
155
name = real_names.get(name, name)
157
docname = tree_prefix + name
158
if docname.endswith(suffix):
159
docname = docname[:-len(suffix)]
160
docname = posixpath.normpath(posixpath.join(dirname, docname))
161
if docname not in env.found_docs:
162
warnings.append(state.document.reporter.warning(
163
'toctree references unknown document %r' % docname,
165
docnames.append(docname)
167
tocnode = sphinx.addnodes.toctree()
168
tocnode['includefiles'] = docnames
169
tocnode['maxdepth'] = -1
170
tocnode['glob'] = None
171
tocnode['entries'] = [(None, docname) for docname in docnames]
173
tocnode = autosummary_toc('', '', tocnode)
174
return warnings + [node] + [tocnode]
176
return warnings + [node]
178
def get_autosummary(names, state, no_signatures=False):
180
Generate a proper table node for autosummary:: directive.
185
Names of Python objects to be imported and added to the table.
187
Docutils document object
190
document = state.document
196
prefixes.insert(0, document.settings.env.currmodule)
198
table = nodes.table('')
199
group = nodes.tgroup('', cols=2)
201
group.append(nodes.colspec('', colwidth=10))
202
group.append(nodes.colspec('', colwidth=90))
203
body = nodes.tbody('')
206
def append_row(*column_texts):
208
for text in column_texts:
209
node = nodes.paragraph('')
211
vl.append(text, '<autosummary>')
212
state.nested_parse(vl, 0, node)
214
if isinstance(node[0], nodes.paragraph):
218
row.append(nodes.entry('', node))
223
obj, real_name = import_by_name(name, prefixes=prefixes)
225
warnings.append(document.reporter.warning(
226
'failed to import %s' % name))
227
append_row(":obj:`%s`" % name, "")
230
real_names[name] = real_name
232
doc = get_doc_object(obj)
235
title = " ".join(doc['Summary'])
239
col1 = u":obj:`%s <%s>`" % (name, real_name)
241
sig = re.sub('^[^(\[]*', '', doc['Signature'].strip())
243
# abbreviate optional arguments
244
sig = re.sub(r', ([a-zA-Z0-9_]+)=', r'[, \1=', sig, count=1)
245
sig = re.sub(r'\(([a-zA-Z0-9_]+)=', r'([\1=', sig, count=1)
246
sig = re.sub(r'=[^,)]+,', ',', sig)
247
sig = re.sub(r'=[^,)]+\)$', '])', sig)
248
# shorten long strings
249
sig = re.sub(r'(\[.{16,16}[^,]*?),.*?\]\)', r'\1, ...])', sig)
251
sig = re.sub(r'(\(.{16,16}[^,]*?),.*?\)', r'\1, ...)', sig)
252
# make signature contain non-breaking spaces
253
col1 += u"\\ \u00a0" + unicode(sig).replace(u" ", u"\u00a0")
255
append_row(col1, col2)
257
return table, warnings, real_names
259
def import_by_name(name, prefixes=[None]):
261
Import a Python object that has the given name, under one of the prefixes.
266
Name of a Python object, eg. 'numpy.ndarray.view'
267
prefixes : list of (str or None), optional
268
Prefixes to prepend to the name (None implies no prefix).
269
The first prefixed name that results to successful import is used.
276
Name of the imported object (useful if `prefixes` was used)
279
for prefix in prefixes:
282
prefixed_name = '.'.join([prefix, name])
285
return _import_by_name(prefixed_name), prefixed_name
290
def _import_by_name(name):
291
"""Import a Python object given its full name"""
293
# try first interpret `name` as MODNAME.OBJ
294
name_parts = name.split('.')
296
modname = '.'.join(name_parts[:-1])
298
return getattr(sys.modules[modname], name_parts[-1])
299
except (ImportError, IndexError, AttributeError):
302
# ... then as MODNAME, MODNAME.OBJ1, MODNAME.OBJ1.OBJ2, ...
305
for j in reversed(range(1, len(name_parts)+1)):
307
modname = '.'.join(name_parts[:j])
312
if modname in sys.modules:
315
if last_j < len(name_parts):
316
obj = sys.modules[modname]
317
for obj_name in name_parts[last_j:]:
318
obj = getattr(obj, obj_name)
321
return sys.modules[modname]
322
except (ValueError, ImportError, AttributeError, KeyError), e:
325
#------------------------------------------------------------------------------
326
# :autolink: (smart default role)
327
#------------------------------------------------------------------------------
329
def autolink_role(typ, rawtext, etext, lineno, inliner,
330
options={}, content=[]):
334
Expands to ":obj:`text`" if `text` is an object that can be imported;
335
otherwise expands to "*text*".
337
r = sphinx.roles.xfileref_role('obj', rawtext, etext, lineno, inliner,
342
#prefixes.insert(0, inliner.document.settings.env.currmodule)
344
obj, name = import_by_name(pnode['reftarget'], prefixes)
347
r[0][0] = nodes.emphasis(rawtext, content[0].astext(),
348
classes=content['classes'])