~ubuntu-branches/ubuntu/precise/python-numpy/precise

« back to all changes in this revision

Viewing changes to doc/sphinxext/autosummary.py

  • Committer: Package Import Robot
  • Author(s): Julian Taylor
  • Date: 2012-02-11 12:55:21 UTC
  • mfrom: (7.1.9 experimental) (7.2.3 sid)
  • Revision ID: package-import@ubuntu.com-20120211125521-31q3am7pp3mvt1ho
Tags: 1:1.6.1-5ubuntu1
* debian/patches/search-multiarch-paths.patch: (LP: #818867)
  - add multiarch libdirs to numpy.distutils.system_info
* Merge from Debian unstable, remaining changes:
  - debian/patches/20_disable-plot-extension.patch
     Disable plot_directive extension, and catch ImportErrors when
     matplotlib cannot be imported, which allows us to remove
     python-matplotlib from dependencies.  This is required because
     python-numpy is in main, while python-matplotlib is in universe.
  - Build using dh_python2
    add bin/f2py* to .install files
  - keep Replaces: python-numpy (<< 1:1.3.0-4) in python-numpy-dbg
    for lucid upgrades

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
"""
2
 
===========
3
 
autosummary
4
 
===========
5
 
 
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.
9
 
 
10
 
An :autolink: role is also provided.
11
 
 
12
 
autosummary directive
13
 
---------------------
14
 
 
15
 
The autosummary directive has the form::
16
 
 
17
 
    .. autosummary::
18
 
       :nosignatures:
19
 
       :toctree: generated/
20
 
       
21
 
       module.function_1
22
 
       module.function_2
23
 
       ...
24
 
 
25
 
and it generates an output table (containing signatures, optionally)
26
 
 
27
 
    ========================  =============================================
28
 
    module.function_1(args)   Summary line from the docstring of function_1
29
 
    module.function_2(args)   Summary line from the docstring
30
 
    ...
31
 
    ========================  =============================================
32
 
 
33
 
If the :toctree: option is specified, files matching the function names
34
 
are inserted to the toctree with the given prefix:
35
 
 
36
 
    generated/module.function_1
37
 
    generated/module.function_2
38
 
    ...
39
 
 
40
 
Note: The file names contain the module:: or currentmodule:: prefixes.
41
 
 
42
 
.. seealso:: autosummary_generate.py
43
 
 
44
 
 
45
 
autolink role
46
 
-------------
47
 
 
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'.
51
 
 
52
 
"""
53
 
import sys, os, posixpath, re
54
 
 
55
 
from docutils.parsers.rst import directives
56
 
from docutils.statemachine import ViewList
57
 
from docutils import nodes
58
 
 
59
 
import sphinx.addnodes, sphinx.roles
60
 
from sphinx.util import patfilter
61
 
 
62
 
from docscrape_sphinx import get_doc_object
63
 
 
64
 
import warnings
65
 
warnings.warn(
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)
71
 
 
72
 
def setup(app):
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)
77
 
    
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)
82
 
 
83
 
#------------------------------------------------------------------------------
84
 
# autosummary_toc node
85
 
#------------------------------------------------------------------------------
86
 
 
87
 
class autosummary_toc(nodes.comment):
88
 
    pass
89
 
 
90
 
def process_autosummary_toc(app, doctree):
91
 
    """
92
 
    Insert items described in autosummary:: to the TOC tree, but do
93
 
    not generate the toctree:: list.
94
 
 
95
 
    """
96
 
    env = app.builder.env
97
 
    crawled = {}
98
 
    def crawl_toc(node, depth=1):
99
 
        crawled[node] = True
100
 
        for j, subnode in enumerate(node):
101
 
            try:
102
 
                if (isinstance(subnode, autosummary_toc)
103
 
                    and isinstance(subnode[0], sphinx.addnodes.toctree)):
104
 
                    env.note_toctree(env.docname, subnode[0])
105
 
                    continue
106
 
            except IndexError:
107
 
                continue
108
 
            if not isinstance(subnode, nodes.section):
109
 
                continue
110
 
            if subnode not in crawled:
111
 
                crawl_toc(subnode, depth+1)
112
 
    crawl_toc(doctree)
113
 
 
114
 
def autosummary_toc_visit_html(self, node):
115
 
    """Hide autosummary toctree list in HTML output"""
116
 
    raise nodes.SkipNode
117
 
 
118
 
def autosummary_toc_visit_latex(self, node):
119
 
    """Show autosummary toctree (= put the referenced pages here) in Latex"""
120
 
    pass
121
 
 
122
 
def autosummary_toc_depart_noop(self, node):
123
 
    pass
124
 
 
125
 
#------------------------------------------------------------------------------
126
 
# .. autosummary::
127
 
#------------------------------------------------------------------------------
128
 
 
129
 
def autosummary_directive(dirname, arguments, options, content, lineno,
130
 
                          content_offset, block_text, state, state_machine):
131
 
    """
132
 
    Pretty table containing short signatures and summaries of functions etc.
133
 
 
134
 
    autosummary also generates a (hidden) toctree:: node.
135
 
 
136
 
    """
137
 
 
138
 
    names = []
139
 
    names += [x.strip().split()[0] for x in content
140
 
              if x.strip() and re.search(r'^[a-zA-Z_]', x.strip()[0])]
141
 
 
142
 
    table, warnings, real_names = get_autosummary(names, state,
143
 
                                                  'nosignatures' in options)
144
 
    node = table
145
 
 
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)
150
 
 
151
 
    if 'toctree' in options:
152
 
        tree_prefix = options['toctree'].strip()
153
 
        docnames = []
154
 
        for name in names:
155
 
            name = real_names.get(name, name)
156
 
 
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,
164
 
                    line=lineno))
165
 
            docnames.append(docname)
166
 
 
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]
172
 
 
173
 
        tocnode = autosummary_toc('', '', tocnode)
174
 
        return warnings + [node] + [tocnode]
175
 
    else:
176
 
        return warnings + [node]
177
 
 
178
 
def get_autosummary(names, state, no_signatures=False):
179
 
    """
180
 
    Generate a proper table node for autosummary:: directive.
181
 
 
182
 
    Parameters
183
 
    ----------
184
 
    names : list of str
185
 
        Names of Python objects to be imported and added to the table.
186
 
    document : document
187
 
        Docutils document object
188
 
    
189
 
    """
190
 
    document = state.document
191
 
    
192
 
    real_names = {}
193
 
    warnings = []
194
 
 
195
 
    prefixes = ['']
196
 
    prefixes.insert(0, document.settings.env.currmodule)
197
 
 
198
 
    table = nodes.table('')
199
 
    group = nodes.tgroup('', cols=2)
200
 
    table.append(group)
201
 
    group.append(nodes.colspec('', colwidth=10))
202
 
    group.append(nodes.colspec('', colwidth=90))
203
 
    body = nodes.tbody('')
204
 
    group.append(body)
205
 
 
206
 
    def append_row(*column_texts):
207
 
        row = nodes.row('')
208
 
        for text in column_texts:
209
 
            node = nodes.paragraph('')
210
 
            vl = ViewList()
211
 
            vl.append(text, '<autosummary>')
212
 
            state.nested_parse(vl, 0, node)
213
 
            try:
214
 
                if isinstance(node[0], nodes.paragraph):
215
 
                    node = node[0]
216
 
            except IndexError:
217
 
                pass
218
 
            row.append(nodes.entry('', node))
219
 
        body.append(row)
220
 
 
221
 
    for name in names:
222
 
        try:
223
 
            obj, real_name = import_by_name(name, prefixes=prefixes)
224
 
        except ImportError:
225
 
            warnings.append(document.reporter.warning(
226
 
                'failed to import %s' % name))
227
 
            append_row(":obj:`%s`" % name, "")
228
 
            continue
229
 
 
230
 
        real_names[name] = real_name
231
 
 
232
 
        doc = get_doc_object(obj)
233
 
 
234
 
        if doc['Summary']:
235
 
            title = " ".join(doc['Summary'])
236
 
        else:
237
 
            title = ""
238
 
        
239
 
        col1 = u":obj:`%s <%s>`" % (name, real_name)
240
 
        if doc['Signature']:
241
 
            sig = re.sub('^[^(\[]*', '', doc['Signature'].strip())
242
 
            if '=' in sig:
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)
250
 
            else:
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")
254
 
        col2 = title
255
 
        append_row(col1, col2)
256
 
 
257
 
    return table, warnings, real_names
258
 
 
259
 
def import_by_name(name, prefixes=[None]):
260
 
    """
261
 
    Import a Python object that has the given name, under one of the prefixes.
262
 
 
263
 
    Parameters
264
 
    ----------
265
 
    name : str
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.
270
 
 
271
 
    Returns
272
 
    -------
273
 
    obj
274
 
        The imported object
275
 
    name
276
 
        Name of the imported object (useful if `prefixes` was used)
277
 
    
278
 
    """
279
 
    for prefix in prefixes:
280
 
        try:
281
 
            if prefix:
282
 
                prefixed_name = '.'.join([prefix, name])
283
 
            else:
284
 
                prefixed_name = name
285
 
            return _import_by_name(prefixed_name), prefixed_name
286
 
        except ImportError:
287
 
            pass
288
 
    raise ImportError
289
 
 
290
 
def _import_by_name(name):
291
 
    """Import a Python object given its full name"""
292
 
    try:
293
 
        # try first interpret `name` as MODNAME.OBJ
294
 
        name_parts = name.split('.')
295
 
        try:
296
 
            modname = '.'.join(name_parts[:-1])
297
 
            __import__(modname)
298
 
            return getattr(sys.modules[modname], name_parts[-1])
299
 
        except (ImportError, IndexError, AttributeError):
300
 
            pass
301
 
       
302
 
        # ... then as MODNAME, MODNAME.OBJ1, MODNAME.OBJ1.OBJ2, ...
303
 
        last_j = 0
304
 
        modname = None
305
 
        for j in reversed(range(1, len(name_parts)+1)):
306
 
            last_j = j
307
 
            modname = '.'.join(name_parts[:j])
308
 
            try:
309
 
                __import__(modname)
310
 
            except ImportError:
311
 
                continue
312
 
            if modname in sys.modules:
313
 
                break
314
 
 
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)
319
 
            return obj
320
 
        else:
321
 
            return sys.modules[modname]
322
 
    except (ValueError, ImportError, AttributeError, KeyError), e:
323
 
        raise ImportError(e)
324
 
 
325
 
#------------------------------------------------------------------------------
326
 
# :autolink: (smart default role)
327
 
#------------------------------------------------------------------------------
328
 
 
329
 
def autolink_role(typ, rawtext, etext, lineno, inliner,
330
 
                  options={}, content=[]):
331
 
    """
332
 
    Smart linking role.
333
 
 
334
 
    Expands to ":obj:`text`" if `text` is an object that can be imported;
335
 
    otherwise expands to "*text*".
336
 
    """
337
 
    r = sphinx.roles.xfileref_role('obj', rawtext, etext, lineno, inliner,
338
 
                                   options, content)
339
 
    pnode = r[0][0]
340
 
 
341
 
    prefixes = [None]
342
 
    #prefixes.insert(0, inliner.document.settings.env.currmodule)
343
 
    try:
344
 
        obj, name = import_by_name(pnode['reftarget'], prefixes)
345
 
    except ImportError:
346
 
        content = pnode[0]
347
 
        r[0][0] = nodes.emphasis(rawtext, content[0].astext(),
348
 
                                 classes=content['classes'])
349
 
    return r