From 73ae2f9c940db3be706197971807e7f1355b6234 Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Sat, 21 Mar 2009 00:34:46 +0200 Subject: [PATCH 01/15] Some autosummary fixes --- doc/ext/autosummary.rst | 4 ++++ sphinx/ext/autosummary/__init__.py | 36 +++++++++++++++++------------- sphinx/ext/autosummary/generate.py | 4 ++-- 3 files changed, 27 insertions(+), 17 deletions(-) diff --git a/doc/ext/autosummary.rst b/doc/ext/autosummary.rst index a9255857b..7ad46d272 100644 --- a/doc/ext/autosummary.rst +++ b/doc/ext/autosummary.rst @@ -6,4 +6,8 @@ .. module:: sphinx.ext.autosummary :synopsis: Generate autodoc summaries +This extension can be used to generate function/method/attribute +summary lists, similar to those output eg. by Epydoc and other API doc +generation tools. + TBW. diff --git a/sphinx/ext/autosummary/__init__.py b/sphinx/ext/autosummary/__init__.py index 3d260fd20..df46b71cb 100644 --- a/sphinx/ext/autosummary/__init__.py +++ b/sphinx/ext/autosummary/__init__.py @@ -68,6 +68,8 @@ from sphinx import addnodes, roles from sphinx.util import patfilter from sphinx.util.compat import Directive +from sphinx.ext.autodoc import FunctionDocumenter + # -- autosummary_toc node ------------------------------------------------------ @@ -132,7 +134,7 @@ class Autosummary(Directive): names += [x.strip() for x in self.content if x.strip()] table, warnings, real_names = get_autosummary( - names, self.state, 'nosignatures' in self.options) + names, self, 'nosignatures' in self.options) node = table env = self.state.document.settings.env @@ -168,7 +170,7 @@ class Autosummary(Directive): return warnings + [node] -def get_autosummary(names, state, no_signatures=False): +def get_autosummary(names, directive, no_signatures=False): """ Generate a proper table node for autosummary:: directive. @@ -176,14 +178,12 @@ def get_autosummary(names, state, no_signatures=False): table. *document* is the Docutils document object. """ + state = directive.state document = state.document real_names = {} warnings = [] - prefixes = [''] - prefixes.insert(0, document.settings.env.currmodule) - table = nodes.table('') group = nodes.tgroup('', cols=2) table.append(group) @@ -203,19 +203,25 @@ def get_autosummary(names, state, no_signatures=False): body.append(row) for name in names: - try: - obj, real_name = import_by_name(name, prefixes=prefixes) - except ImportError: - warnings.append(document.reporter.warning( - 'failed to import %s' % name)) - append_row(':obj:`%s`' % name, '') - continue + documenter = FunctionDocumenter(self, name) + documenter.parse_name() - real_names[name] = real_name + real_names[name] = documenter.fullname + + sig = documenter.format_signature() + if sig: + pass + else: + sig = '' + + doc = list(documenter.process_doc([documenter.get_doc()])) + if doc: + title = doc[0] + else: + title = '' - title = '' qualifier = 'obj' - col1 = ':'+qualifier+':`%s <%s>`' % (name, real_name) + col1 = ':' + qualifier + r':`%s <%s>`\ %s' % (name, real_name, sig) col2 = title append_row(col1, col2) diff --git a/sphinx/ext/autosummary/generate.py b/sphinx/ext/autosummary/generate.py index c21d71ac5..0026d21dd 100644 --- a/sphinx/ext/autosummary/generate.py +++ b/sphinx/ext/autosummary/generate.py @@ -211,10 +211,10 @@ def get_documented(filenames): return documented -def main(argv): +def main(): usage = 'usage: %s [-o output_dir] [-s suffix] sourcefile ...' % sys.argv[0] try: - opts, args = getopt.getopt(argv[1:], 'o:s:') + opts, args = getopt.getopt(sys.argv[1:], 'o:s:') except getopt.error: print >>sys.stderr, usage return 1 From d9ae7c30cb0725e5d5c1e6dfbcd8dadf4ab5ecb3 Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Sat, 4 Apr 2009 22:05:04 +0300 Subject: [PATCH 02/15] autosummary: write documentation --- doc/conf.py | 3 +- doc/ext/autosummary.rst | 103 ++++++++++++++++++++++++++++++++++++++-- 2 files changed, 101 insertions(+), 5 deletions(-) diff --git a/doc/conf.py b/doc/conf.py index e1a48aa20..be79c8f9b 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -6,7 +6,8 @@ import sys, os, re # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.addons.*') or your custom ones. -extensions = ['sphinx.ext.autodoc', 'sphinx.ext.doctest', 'sphinx.ext.todo'] +extensions = ['sphinx.ext.autodoc', 'sphinx.ext.doctest', 'sphinx.ext.todo', + 'sphinx.ext.autosummary'] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] diff --git a/doc/ext/autosummary.rst b/doc/ext/autosummary.rst index 7ad46d272..692eb911a 100644 --- a/doc/ext/autosummary.rst +++ b/doc/ext/autosummary.rst @@ -6,8 +6,103 @@ .. module:: sphinx.ext.autosummary :synopsis: Generate autodoc summaries -This extension can be used to generate function/method/attribute -summary lists, similar to those output eg. by Epydoc and other API doc -generation tools. +.. versionadded: 0.6 -TBW. +This extension generates function/method/attribute summary lists, +similar to those output eg. by Epydoc and other API doc generation +tools. This is especially useful when your docstrings are long and +detailed, and putting each one of them on a separate page makes +them easier to read. + +The :mod:`sphinx.ext.autosummary` extension does this in two parts: + + 1. There is an :dir:`autosummary` directive for generating summary + listings that contain links to the documented items, and short + summary blurbs extracted from their docstrings. + + 2. The convenience script :program:`sphinx-autogen` can be used to + generate short "stub" files for the entries listed in the + :dir:`autosummary` directives. These by default contain only the + corresponding :mod:`sphinx.ext.autodoc` directive. + +.. directive:: autosummary + + Insert a table that contains links to documented items, and a short + summary blurb (the first sentence of the docstring) for each of them. + The :dir:`autosummary` directive can also optionally serve as + a :dir:`toctree` entry for the included items. + + For example,:: + + .. currentmodule:: sphinx + + .. autosummary:: + + environment.BuildEnvironment + util.relative_uri + + produces a table like this: + + .. currentmodule:: sphinx + + .. autosummary:: + + environment.BuildEnvironment + util.relative_uri + + .. currentmodule:: sphinx.ext.autosummary + + Autosummary preprocesses the docstrings and signatures with the same + :event:`autodoc-process-docstring` and + :event:`autodoc-process-signature` hooks as *autodoc*. + + + **Options** + + * If you want the :dir:`autosummary` table to also serve as a + :dir:`toctree` entry, use the ``toctree`` option, for example:: + + .. autosummary:: + :toctree: DIRNAME + + sphinx.environment.BuildEnvironment + sphinx.util.relative_uri + + The ``toctree`` option also signals to the :program:`sphinx-autogen` + script that stub pages should be generated for the entries listed + in this directive. The option accepts a directory name as an + argument; :program:`sphinx-autogen` will by default place its output + in this directory. + + * If you don't want the :dir:`autosummary` to show function signatures + in the listing, include the ``nosignatures`` option:: + + .. autosummary:: + :nosignatures: + + sphinx.environment.BuildEnvironment + sphinx.util.relative_uri + +:program:`sphinx-autogen` -- generate autodoc stub pages +-------------------------------------------------------- + +The :program:`sphinx-autogen` script can be used to conveniently +generate stub documentation pages for items included in +:dir:`autosummary` listings. + +For example, the command:: + + $ sphinx-autogen -o generated *.rst + +will read all :dir:`autosummary` tables in the :file:`*.rst` files +that have the ``:toctree:`` option set, and output corresponding stub +pages in directory ``generated`` for all documented items. +The generated pages by default contain text of the form:: + + sphinx.util.relative_uri + ======================== + + .. autofunction:: sphinx.util.relative_uri + +If the ``-o`` option is not given, the script will place the output +files to the directories specified in the ``:toctree:`` options. From 0510af7672cb9b6df155ba0171fb95d1adf8d3e0 Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Sat, 4 Apr 2009 22:05:32 +0300 Subject: [PATCH 03/15] autosummary: fix bugs, and include features from the Numpy version --- sphinx/ext/autosummary/__init__.py | 199 ++++++++++++++++++++--------- sphinx/ext/autosummary/generate.py | 40 +++--- tests/test_autosummary.py | 32 +++++ 3 files changed, 190 insertions(+), 81 deletions(-) create mode 100644 tests/test_autosummary.py diff --git a/sphinx/ext/autosummary/__init__.py b/sphinx/ext/autosummary/__init__.py index df46b71cb..b5e2e143c 100644 --- a/sphinx/ext/autosummary/__init__.py +++ b/sphinx/ext/autosummary/__init__.py @@ -68,7 +68,7 @@ from sphinx import addnodes, roles from sphinx.util import patfilter from sphinx.util.compat import Directive -from sphinx.ext.autodoc import FunctionDocumenter +import sphinx.ext.autodoc # -- autosummary_toc node ------------------------------------------------------ @@ -110,6 +110,27 @@ def autosummary_toc_visit_latex(self, node): def autosummary_noop(self, node): pass +# -- autodoc integration ------------------------------------------------------- + +def get_documenter(obj): + """ + Get an autodoc.Documenter class suitable for documenting the given object + """ + reg = sphinx.ext.autodoc.AutoDirective._registry + if inspect.isclass(obj): + if issubclass(obj, Exception): + return reg.get('exception') + return reg.get('class') + elif inspect.ismodule(obj): + return reg.get('module') + elif inspect.ismethod(obj) or inspect.ismethoddescriptor(obj): + return reg.get('method') + elif inspect.ismemberdescriptor(obj) or inspect.isgetsetdescriptor(obj): + return reg.get('attribute') + elif inspect.isroutine(obj): + return reg.get('function') + else: + return reg.get('data') # -- .. autosummary:: ---------------------------------------------------------- @@ -129,15 +150,21 @@ class Autosummary(Directive): 'nosignatures': directives.flag, } + def warn(self, msg): + self.warnings.append(self.state.document.reporter.warning( + msg, line=self.lineno)) + def run(self): + self.env = env = self.state.document.settings.env + self.genopt = {} + self.warnings = [] + names = [] names += [x.strip() for x in self.content if x.strip()] - table, warnings, real_names = get_autosummary( - names, self, 'nosignatures' in self.options) - node = table + table, real_names = self.get_table(names) + nodes = [table] - env = self.state.document.settings.env suffix = env.config.source_suffix all_docnames = env.found_docs.copy() dirname = posixpath.dirname(env.docname) @@ -153,9 +180,8 @@ class Autosummary(Directive): docname = docname[:-len(suffix)] docname = posixpath.normpath(posixpath.join(dirname, docname)) if docname not in env.found_docs: - warnings.append(self.state.document.reporter.warning( - 'toctree references unknown document %r' % docname, - line=self.lineno)) + self.warn('toctree references unknown document %r' + % docname) docnames.append(docname) tocnode = addnodes.toctree() @@ -165,67 +191,124 @@ class Autosummary(Directive): tocnode['glob'] = None tocnode = autosummary_toc('', '', tocnode) - return warnings + [node] + [tocnode] - else: - return warnings + [node] + nodes.append(tocnode) + return self.warnings + nodes -def get_autosummary(names, directive, no_signatures=False): + def get_table(self, names, no_signatures=False): + """ + Generate a proper table node for autosummary:: directive. + + *names* is a list of names of Python objects to be imported + and added to the table. + + """ + state = self.state + document = state.document + + prefixes = [''] + prefixes.insert(0, document.settings.env.currmodule) + + real_names = {} + + table = nodes.table('') + group = nodes.tgroup('', cols=2) + table.append(group) + group.append(nodes.colspec('', colwidth=10)) + group.append(nodes.colspec('', colwidth=90)) + body = nodes.tbody('') + group.append(body) + + def append_row(*column_texts): + row = nodes.row('') + for text in column_texts: + node = nodes.paragraph('') + vl = ViewList() + vl.append(text, '') + state.nested_parse(vl, 0, node) + row.append(nodes.entry('', node)) + body.append(row) + + for name in names: + try: + obj, real_name = import_by_name(name, prefixes=prefixes) + except ImportError: + self.warn('failed to import %s' % name) + append_row(':obj:`%s`' % name, '') + continue + + documenter = get_documenter(obj)(self, real_name) + if not documenter.parse_name(): + append_row(':obj:`%s`' % name, '') + continue + if not documenter.import_object(): + append_row(':obj:`%s`' % name, '') + continue + + real_names[name] = documenter.fullname + + sig = documenter.format_signature() + if not sig or 'nosignatures' in self.options: + sig = '' + else: + sig = mangle_signature(sig) + + doc = list(documenter.process_doc(documenter.get_doc())) + if doc: + # grab the summary + while doc and not doc[0].strip(): + doc.pop(0) + m = re.search(r"^([A-Z].*?\.\s)", " ".join(doc).strip()) + if m: + summary = m.group(1).strip() + else: + summary = doc[0].strip() + else: + summary = '' + + qualifier = 'obj' + col1 = ':' + qualifier + r':`%s <%s>`\ %s' % (name, real_name, sig) + col2 = summary + append_row(col1, col2) + + return table, real_names + +def mangle_signature(sig, max_chars=30): """ - Generate a proper table node for autosummary:: directive. - - *names* is a list of names of Python objects to be imported and added to the - table. *document* is the Docutils document object. + Reformat function signature to a more compact form. """ - state = directive.state - document = state.document + sig = re.sub(r"^\((.*)\)$", r"\1", sig) + ", " + r = re.compile(r"(?P[a-zA_Z0-9_*]+)(?P=.*?)?, ") + items = r.findall(sig) - real_names = {} - warnings = [] + args = [] + opts = [] - table = nodes.table('') - group = nodes.tgroup('', cols=2) - table.append(group) - group.append(nodes.colspec('', colwidth=30)) - group.append(nodes.colspec('', colwidth=70)) - body = nodes.tbody('') - group.append(body) - - def append_row(*column_texts): - row = nodes.row('') - for text in column_texts: - node = nodes.paragraph('') - vl = ViewList() - vl.append(text, '') - state.nested_parse(vl, 0, node) - row.append(nodes.entry('', node)) - body.append(row) - - for name in names: - documenter = FunctionDocumenter(self, name) - documenter.parse_name() - - real_names[name] = documenter.fullname - - sig = documenter.format_signature() - if sig: - pass + total_len = 4 + for name, default in items: + if default: + opts.append(name) else: - sig = '' + args.append(name) + total_len += len(name) + 2 - doc = list(documenter.process_doc([documenter.get_doc()])) - if doc: - title = doc[0] - else: - title = '' + if total_len > max_chars: + if opts: + opts.append('...') + else: + args.append('...') + break - qualifier = 'obj' - col1 = ':' + qualifier + r':`%s <%s>`\ %s' % (name, real_name, sig) - col2 = title - append_row(col1, col2) + if opts: + sig = ", ".join(args) + "[, " + ", ".join(opts) + "]" + else: + sig = ", ".join(args) - return table, warnings, real_names + sig = unicode(sig).replace(u" ", u"\u00a0") + return u"(%s)" % sig + +# -- Importing items ----------------------------------------------------------- def import_by_name(name, prefixes=[None]): """ diff --git a/sphinx/ext/autosummary/generate.py b/sphinx/ext/autosummary/generate.py index 0026d21dd..ccc7eab8d 100644 --- a/sphinx/ext/autosummary/generate.py +++ b/sphinx/ext/autosummary/generate.py @@ -20,7 +20,7 @@ import os import re import sys -import getopt +import optparse import inspect from jinja2 import Environment, PackageLoader @@ -38,7 +38,7 @@ def _simple_info(msg): def _simple_warn(msg): print >>sys.stderr, 'WARNING: ' + msg -def generate_autosummary_docs(sources, output_dir=None, suffix=None, +def generate_autosummary_docs(sources, output_dir=None, suffix='.rst', warn=_simple_warn, info=_simple_info): info('generating autosummary for: %s' % ', '.join(sources)) if output_dir: @@ -62,7 +62,7 @@ def generate_autosummary_docs(sources, output_dir=None, suffix=None, warn('failed to import %r: %s' % (name, e)) continue - fn = os.path.join(path, name + (suffix or '.rst')) + fn = os.path.join(path, name + suffix) # skip it if it exists if os.path.isfile(fn): continue @@ -211,28 +211,22 @@ def get_documented(filenames): return documented -def main(): - usage = 'usage: %s [-o output_dir] [-s suffix] sourcefile ...' % sys.argv[0] - try: - opts, args = getopt.getopt(sys.argv[1:], 'o:s:') - except getopt.error: - print >>sys.stderr, usage - return 1 - - output_dir = None - suffix = None - for opt, val in opts: - if opt == '-o': - output_dir = val - elif opt == '-s': - suffix = val +def main(argv): + usage = """%prog [OPTIONS] SOURCEFILE ...""" + p = optparse.OptionParser(usage.strip()) + p.add_option("-o", "--output-dir", action="store", type="string", + dest="output_dir", default=None, + help="Directory to place all output in") + p.add_option("-s", "--suffix", action="store", type="string", + dest="suffix", default="rst", + help="Default suffix for files (default: %default)") + options, args = p.parse_args(argv[1:]) if len(args) < 1: - print >>sys.stderr, usage - return 1 - - generate_autosummary_docs(args, output_dir, suffix) + p.error('no input files given') + generate_autosummary_docs(args, options.output_dir, + "." + options.suffix) if __name__ == '__main__': - main(sys.argv) + main() diff --git a/tests/test_autosummary.py b/tests/test_autosummary.py new file mode 100644 index 000000000..a748b2b73 --- /dev/null +++ b/tests/test_autosummary.py @@ -0,0 +1,32 @@ +# -*- coding: utf-8 -*- +""" + test_autosummary + ~~~~~~~~~~~~~~~~ + + Test the autosummary extension. + + :copyright: Copyright 2007-2009 by the Sphinx team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" +import string + +from util import * + +from sphinx.ext.autosummary import mangle_signature + + +def test_mangle_signature(): + TEST = """ + () :: () + (a, b, c, d, e) :: (a, b, c, d, e) + (a, b, c=1, d=2, e=3) :: (a, b[, c, d, e]) + (a, b, aaa=1, bbb=1, ccc=1, eee=1, fff=1, ggg=1, hhh=1, iii=1, jjj=1) :: (a, b[, aaa, bbb, ccc, eee, fff, ...]) + (a, b, c=(), d=) :: (a, b[, c, d]) + (a, b, c='foobar()', d=123) :: (a, b[, c, d]) + """ + + TEST = [map(string.strip, x.split("::")) for x in TEST.split("\n") + if '::' in x] + for inp, outp in TEST: + res = mangle_signature(inp).strip().replace(u"\u00a0", " ") + assert res == outp, (u"'%s' -> '%s' != '%s'" % (inp, res, outp)) From 939012eb1ed98d550b5b2c9ecdb59c4efc4e0821 Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Sat, 4 Apr 2009 22:22:16 +0300 Subject: [PATCH 04/15] autosummary: fix bugs in summary extraction, and attribute detection --- sphinx/ext/autosummary/__init__.py | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/sphinx/ext/autosummary/__init__.py b/sphinx/ext/autosummary/__init__.py index b5e2e143c..ff7e8d9d6 100644 --- a/sphinx/ext/autosummary/__init__.py +++ b/sphinx/ext/autosummary/__init__.py @@ -125,7 +125,8 @@ def get_documenter(obj): return reg.get('module') elif inspect.ismethod(obj) or inspect.ismethoddescriptor(obj): return reg.get('method') - elif inspect.ismemberdescriptor(obj) or inspect.isgetsetdescriptor(obj): + elif (inspect.ismemberdescriptor(obj) or inspect.isgetsetdescriptor(obj) + or inspect.isdatadescriptor(obj)): return reg.get('attribute') elif inspect.isroutine(obj): return reg.get('function') @@ -245,7 +246,7 @@ class Autosummary(Directive): append_row(':obj:`%s`' % name, '') continue - real_names[name] = documenter.fullname + real_names[name] = real_name sig = documenter.format_signature() if not sig or 'nosignatures' in self.options: @@ -254,15 +255,15 @@ class Autosummary(Directive): sig = mangle_signature(sig) doc = list(documenter.process_doc(documenter.get_doc())) - if doc: - # grab the summary - while doc and not doc[0].strip(): - doc.pop(0) - m = re.search(r"^([A-Z].*?\.\s)", " ".join(doc).strip()) - if m: - summary = m.group(1).strip() - else: - summary = doc[0].strip() + + # grab the summary + while doc and not doc[0].strip(): + doc.pop(0) + m = re.search(r"^([A-Z].*?\.\s)", " ".join(doc).strip()) + if m: + summary = m.group(1).strip() + elif doc: + summary = doc[0].strip() else: summary = '' From bffc8741aa16ccba04682ae3a15e66cba58d29c9 Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Sat, 4 Apr 2009 22:30:29 +0300 Subject: [PATCH 05/15] autosummary: refactor generate.py a bit --- sphinx/ext/autosummary/__init__.py | 19 +++++++-------- sphinx/ext/autosummary/generate.py | 39 ++++++++++++------------------ 2 files changed, 24 insertions(+), 34 deletions(-) diff --git a/sphinx/ext/autosummary/__init__.py b/sphinx/ext/autosummary/__init__.py index ff7e8d9d6..1c94bae08 100644 --- a/sphinx/ext/autosummary/__init__.py +++ b/sphinx/ext/autosummary/__init__.py @@ -68,8 +68,6 @@ from sphinx import addnodes, roles from sphinx.util import patfilter from sphinx.util.compat import Directive -import sphinx.ext.autodoc - # -- autosummary_toc node ------------------------------------------------------ @@ -116,22 +114,23 @@ def get_documenter(obj): """ Get an autodoc.Documenter class suitable for documenting the given object """ - reg = sphinx.ext.autodoc.AutoDirective._registry + import sphinx.ext.autodoc as autodoc + if inspect.isclass(obj): if issubclass(obj, Exception): - return reg.get('exception') - return reg.get('class') + return autodoc.ExceptionDocumenter + return autodoc.ClassDocumenter elif inspect.ismodule(obj): - return reg.get('module') + return autodoc.ModuleDocumenter elif inspect.ismethod(obj) or inspect.ismethoddescriptor(obj): - return reg.get('method') + return autodoc.MethodDocumenter elif (inspect.ismemberdescriptor(obj) or inspect.isgetsetdescriptor(obj) or inspect.isdatadescriptor(obj)): - return reg.get('attribute') + return autodoc.AttributeDocumenter elif inspect.isroutine(obj): - return reg.get('function') + return autodoc.FunctionDocumenter else: - return reg.get('data') + return autodoc.DataDocumenter # -- .. autosummary:: ---------------------------------------------------------- diff --git a/sphinx/ext/autosummary/generate.py b/sphinx/ext/autosummary/generate.py index ccc7eab8d..61e41392b 100644 --- a/sphinx/ext/autosummary/generate.py +++ b/sphinx/ext/autosummary/generate.py @@ -25,7 +25,7 @@ import inspect from jinja2 import Environment, PackageLoader -from sphinx.ext.autosummary import import_by_name +from sphinx.ext.autosummary import import_by_name, get_documenter from sphinx.util import ensuredir # create our own templating environment, for module template only @@ -73,17 +73,16 @@ def generate_autosummary_docs(sources, output_dir=None, suffix='.rst', if inspect.ismodule(obj): # XXX replace this with autodoc's API? tmpl = env.get_template('module') - functions = [getattr(obj, item).__name__ - for item in dir(obj) - if inspect.isfunction(getattr(obj, item))] - classes = [getattr(obj, item).__name__ - for item in dir(obj) - if inspect.isclass(getattr(obj, item)) - and not issubclass(getattr(obj, item), Exception)] - exceptions = [getattr(obj, item).__name__ - for item in dir(obj) - if inspect.isclass(getattr(obj, item)) - and issubclass(getattr(obj, item), Exception)] + + def get_items(mod, typ): + return [getattr(mod, name).__name__ + for name in dir(mod) + if get_documenter(getattr(mod,name)).objtype==typ] + + functions = get_items(obj, 'function') + classes = get_items(obj, 'class') + exceptions = get_items(obj, 'exception') + rendered = tmpl.render(name=name, underline='='*len(name), functions=functions, @@ -96,19 +95,11 @@ def generate_autosummary_docs(sources, output_dir=None, suffix='.rst', else: f.write('%s\n%s\n\n' % (name, '='*len(name))) - if inspect.isclass(obj): - if issubclass(obj, Exception): - f.write(format_modulemember(name, 'autoexception')) - else: - f.write(format_modulemember(name, 'autoclass')) - elif inspect.ismethod(obj) or inspect.ismethoddescriptor(obj): - f.write(format_classmember(name, 'automethod')) - elif callable(obj): - f.write(format_modulemember(name, 'autofunction')) - elif hasattr(obj, '__get__'): - f.write(format_classmember(name, 'autoattribute')) + doc = get_documenter(obj) + if doc.objtype in ('method', 'attribute'): + f.write(format_classmember(name, 'auto%s' % doc.objtype)) else: - f.write(format_modulemember(name, 'autofunction')) + f.write(format_modulemember(name, 'auto%s' % doc.objtype)) finally: f.close() From ab1f917cfaa9be5a051334f04ee7ae57f8e159d1 Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Sat, 4 Apr 2009 22:48:26 +0300 Subject: [PATCH 06/15] autosummary: allow trailing comments in autosummary lists --- sphinx/ext/autosummary/__init__.py | 4 ++-- sphinx/ext/autosummary/generate.py | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/sphinx/ext/autosummary/__init__.py b/sphinx/ext/autosummary/__init__.py index 1c94bae08..16dbd0bf0 100644 --- a/sphinx/ext/autosummary/__init__.py +++ b/sphinx/ext/autosummary/__init__.py @@ -159,8 +159,8 @@ class Autosummary(Directive): self.genopt = {} self.warnings = [] - names = [] - names += [x.strip() for x in self.content if x.strip()] + names = [x.strip().split()[0] for x in self.content + if x.strip() and re.search(r'^[a-zA-Z_]', x.strip()[0])] table, real_names = self.get_table(names) nodes = [table] diff --git a/sphinx/ext/autosummary/generate.py b/sphinx/ext/autosummary/generate.py index 61e41392b..c21a7b452 100644 --- a/sphinx/ext/autosummary/generate.py +++ b/sphinx/ext/autosummary/generate.py @@ -43,6 +43,7 @@ def generate_autosummary_docs(sources, output_dir=None, suffix='.rst', info('generating autosummary for: %s' % ', '.join(sources)) if output_dir: info('writing to %s' % output_dir) + # read names = {} for name, loc in get_documented(sources).items(): @@ -121,7 +122,7 @@ autodoc_re = re.compile(r'.. auto(function|method|attribute|class|exception' '|module)::\s*([A-Za-z0-9_.]+)\s*$') autosummary_re = re.compile(r'^\.\.\s+autosummary::\s*') module_re = re.compile(r'^\.\.\s+(current)?module::\s*([a-zA-Z0-9_.]+)\s*$') -autosummary_item_re = re.compile(r'^\s+([_a-zA-Z][a-zA-Z0-9_.]*)\s*') +autosummary_item_re = re.compile(r'^\s+([_a-zA-Z][a-zA-Z0-9_.]*)\s*.*?') toctree_arg_re = re.compile(r'^\s+:toctree:\s*(.*?)\s*$') def get_documented(filenames): From c6434509c089f640c3503bd8399af68c87b6425d Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Sat, 4 Apr 2009 23:01:11 +0300 Subject: [PATCH 07/15] autosummary: make generate.py recurse into automodule:: docstrings --- sphinx/ext/autosummary/generate.py | 225 +++++++++++++++++------------ 1 file changed, 132 insertions(+), 93 deletions(-) diff --git a/sphinx/ext/autosummary/generate.py b/sphinx/ext/autosummary/generate.py index c21a7b452..6b00f45f9 100644 --- a/sphinx/ext/autosummary/generate.py +++ b/sphinx/ext/autosummary/generate.py @@ -22,14 +22,29 @@ import re import sys import optparse import inspect +import pydoc from jinja2 import Environment, PackageLoader from sphinx.ext.autosummary import import_by_name, get_documenter from sphinx.util import ensuredir -# create our own templating environment, for module template only -env = Environment(loader=PackageLoader('sphinx.ext.autosummary', 'templates')) +def main(argv): + usage = """%prog [OPTIONS] SOURCEFILE ...""" + p = optparse.OptionParser(usage.strip()) + p.add_option("-o", "--output-dir", action="store", type="string", + dest="output_dir", default=None, + help="Directory to place all output in") + p.add_option("-s", "--suffix", action="store", type="string", + dest="suffix", default="rst", + help="Default suffix for files (default: %default)") + options, args = p.parse_args(argv[1:]) + + if len(args) < 1: + p.error('no input files given') + + generate_autosummary_docs(args, options.output_dir, + "." + options.suffix) def _simple_info(msg): @@ -38,6 +53,13 @@ def _simple_info(msg): def _simple_warn(msg): print >>sys.stderr, 'WARNING: ' + msg +#------------------------------------------------------------------------------ +# Generating output +#------------------------------------------------------------------------------ + +# create our own templating environment, for module template only +env = Environment(loader=PackageLoader('sphinx.ext.autosummary', 'templates')) + def generate_autosummary_docs(sources, output_dir=None, suffix='.rst', warn=_simple_warn, info=_simple_info): info('generating autosummary for: %s' % ', '.join(sources)) @@ -46,7 +68,7 @@ def generate_autosummary_docs(sources, output_dir=None, suffix='.rst', # read names = {} - for name, loc in get_documented(sources).items(): + for name, loc in get_documented_in_files(sources).items(): for (filename, sec_title, keyword, toctree) in loc: if toctree is not None: path = os.path.join(os.path.dirname(filename), toctree) @@ -117,108 +139,125 @@ def format_classmember(name, directive): return '.. currentmodule:: %s\n\n.. %s:: %s\n' % (mod, directive, name) -title_underline_re = re.compile('^[-=*_^#]{3,}\s*$') -autodoc_re = re.compile(r'.. auto(function|method|attribute|class|exception' - '|module)::\s*([A-Za-z0-9_.]+)\s*$') -autosummary_re = re.compile(r'^\.\.\s+autosummary::\s*') -module_re = re.compile(r'^\.\.\s+(current)?module::\s*([a-zA-Z0-9_.]+)\s*$') -autosummary_item_re = re.compile(r'^\s+([_a-zA-Z][a-zA-Z0-9_.]*)\s*.*?') -toctree_arg_re = re.compile(r'^\s+:toctree:\s*(.*?)\s*$') +#------------------------------------------------------------------------------ +# Finding documented entries in files +#------------------------------------------------------------------------------ -def get_documented(filenames): +def get_documented_in_files(filenames): """ - Find out what items are documented in the given filenames. + Find out what items are documented in source/*.rst + See `get_documented_in_lines`. - Returns a dict of list of (filename, title, keyword, toctree) Keys are - documented names of objects. The value is a list of locations where the - object was documented. Each location is a tuple of filename, the current - section title, the name of the directive, and the value of the :toctree: - argument (if present) of the directive. """ - documented = {} - for filename in filenames: - current_title = [] - last_line = None - toctree = None - current_module = None - in_autosummary = False - f = open(filename, 'r') - for line in f: - try: - if in_autosummary: - m = toctree_arg_re.match(line) - if m: - toctree = m.group(1) - continue - - if line.strip().startswith(':'): - continue # skip options - - m = autosummary_item_re.match(line) - - if m: - name = m.group(1).strip() - if current_module and \ - not name.startswith(current_module + '.'): - name = '%s.%s' % (current_module, name) - documented.setdefault(name, []).append( - (filename, current_title, 'autosummary', toctree)) - continue - if line.strip() == '': - continue - in_autosummary = False - - m = autosummary_re.match(line) - if m: - in_autosummary = True - continue - - m = autodoc_re.search(line) - if m: - name = m.group(2).strip() - # XXX look in newer generate.py - if current_module and \ - not name.startswith(current_module + '.'): - name = '%s.%s' % (current_module, name) - if m.group(1) == 'module': - current_module = name - documented.setdefault(name, []).append( - (filename, current_title, 'auto' + m.group(1), None)) - continue - - m = title_underline_re.match(line) - if m and last_line: - current_title = last_line.strip() - continue - - m = module_re.match(line) - if m: - current_module = m.group(2) - continue - finally: - last_line = line + lines = f.read().splitlines() + documented.update(get_documented_in_lines(lines, filename=filename)) + f.close() return documented +def get_documented_in_docstring(name, module=None, filename=None): + """ + Find out what items are documented in the given object's docstring. + See `get_documented_in_lines`. + + """ + try: + obj, real_name = import_by_name(name) + lines = pydoc.getdoc(obj).splitlines() + return get_documented_in_lines(lines, module=name, filename=filename) + except AttributeError: + pass + except ImportError, e: + print "Failed to import '%s': %s" % (name, e) + return {} -def main(argv): - usage = """%prog [OPTIONS] SOURCEFILE ...""" - p = optparse.OptionParser(usage.strip()) - p.add_option("-o", "--output-dir", action="store", type="string", - dest="output_dir", default=None, - help="Directory to place all output in") - p.add_option("-s", "--suffix", action="store", type="string", - dest="suffix", default="rst", - help="Default suffix for files (default: %default)") - options, args = p.parse_args(argv[1:]) +def get_documented_in_lines(lines, module=None, filename=None): + """ + Find out what items are documented in the given lines + + Returns + ------- + documented : dict of list of (filename, title, keyword, toctree) + Dictionary whose keys are documented names of objects. + The value is a list of locations where the object was documented. + Each location is a tuple of filename, the current section title, + the name of the directive, and the value of the :toctree: argument + (if present) of the directive. - if len(args) < 1: - p.error('no input files given') + """ + title_underline_re = re.compile("^[-=*_^#]{3,}\s*$") + autodoc_re = re.compile(".. auto(function|method|attribute|class|exception|module)::\s*([A-Za-z0-9_.]+)\s*$") + autosummary_re = re.compile(r'^\.\.\s+autosummary::\s*') + module_re = re.compile(r'^\.\.\s+(current)?module::\s*([a-zA-Z0-9_.]+)\s*$') + autosummary_item_re = re.compile(r'^\s+([_a-zA-Z][a-zA-Z0-9_.]*)\s*.*?') + toctree_arg_re = re.compile(r'^\s+:toctree:\s*(.*?)\s*$') + + documented = {} + + current_title = [] + last_line = None + toctree = None + current_module = module + in_autosummary = False + + for line in lines: + try: + if in_autosummary: + m = toctree_arg_re.match(line) + if m: + toctree = m.group(1) + continue - generate_autosummary_docs(args, options.output_dir, - "." + options.suffix) + if line.strip().startswith(':'): + continue # skip options + + m = autosummary_item_re.match(line) + if m: + name = m.group(1).strip() + if current_module and not name.startswith(current_module + '.'): + name = "%s.%s" % (current_module, name) + documented.setdefault(name, []).append( + (filename, current_title, 'autosummary', toctree)) + continue + if line.strip() == '': + continue + in_autosummary = False + + m = autosummary_re.match(line) + if m: + in_autosummary = True + continue + + m = autodoc_re.search(line) + if m: + name = m.group(2).strip() + if m.group(1) == "module": + current_module = name + documented.update(get_documented_in_docstring( + name, filename=filename)) + elif current_module and not name.startswith(current_module+'.'): + name = "%s.%s" % (current_module, name) + documented.setdefault(name, []).append( + (filename, current_title, "auto" + m.group(1), None)) + continue + + m = title_underline_re.match(line) + if m and last_line: + current_title = last_line.strip() + continue + + m = module_re.match(line) + if m: + current_module = m.group(2) + continue + finally: + last_line = line + + return documented + +#------------------------------------------------------------------------------ if __name__ == '__main__': main() From d769e7b616bb9413a3dabd6920919f1b019cc43d Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Sat, 4 Apr 2009 23:26:37 +0300 Subject: [PATCH 08/15] autosummary: refactoring, split table generation from input parsing --- sphinx/ext/autosummary/__init__.py | 136 ++++++++++++++++------------- 1 file changed, 74 insertions(+), 62 deletions(-) diff --git a/sphinx/ext/autosummary/__init__.py b/sphinx/ext/autosummary/__init__.py index 16dbd0bf0..d82c7088b 100644 --- a/sphinx/ext/autosummary/__init__.py +++ b/sphinx/ext/autosummary/__init__.py @@ -161,21 +161,18 @@ class Autosummary(Directive): names = [x.strip().split()[0] for x in self.content if x.strip() and re.search(r'^[a-zA-Z_]', x.strip()[0])] - - table, real_names = self.get_table(names) - nodes = [table] - - suffix = env.config.source_suffix - all_docnames = env.found_docs.copy() - dirname = posixpath.dirname(env.docname) + items = self.get_items(names) + nodes = [self.get_table(items)] if 'toctree' in self.options: + suffix = env.config.source_suffix + all_docnames = env.found_docs.copy() + dirname = posixpath.dirname(env.docname) + tree_prefix = self.options['toctree'].strip() docnames = [] - for name in names: - name = real_names.get(name, name) - - docname = posixpath.join(tree_prefix, name) + for name, sig, summary, real_name in items: + docname = posixpath.join(tree_prefix, real_name) if docname.endswith(suffix): docname = docname[:-len(suffix)] docname = posixpath.normpath(posixpath.join(dirname, docname)) @@ -195,22 +192,70 @@ class Autosummary(Directive): return self.warnings + nodes - def get_table(self, names, no_signatures=False): + def get_items(self, names): + """ + Try to import the given names, and return a list of + ``[(name, signature, summary_string, real_name), ...]`` + + """ + prefixes = [''] + prefixes.insert(0, self.state.document.settings.env.currmodule) + + items = [] + + for name in names: + try: + obj, real_name = import_by_name(name, prefixes=prefixes) + except ImportError: + self.warn('failed to import %s' % name) + items.append((name, '', '', name)) + continue + + # NB. using real_name here is important, since Documenters + # don't handle module prefixes slightly differently + documenter = get_documenter(obj)(self, real_name) + if not documenter.parse_name(): + self.warn('failed to parse name %s' % real_name) + items.append((name, '', '', real_name)) + continue + if not documenter.import_object(): + self.warn('failed to import object %s' % real_name) + items.append((name, '', '', real_name)) + continue + + # -- Grab the signature + + sig = documenter.format_signature() + if not sig: + sig = '' + else: + sig = mangle_signature(sig) + + # -- Grab the summary + + doc = list(documenter.process_doc(documenter.get_doc())) + + while doc and not doc[0].strip(): + doc.pop(0) + m = re.search(r"^([A-Z][^A-Z]*?\.\s)", " ".join(doc).strip()) + if m: + summary = m.group(1).strip() + elif doc: + summary = doc[0].strip() + else: + summary = '' + + items.append((name, sig, summary, real_name)) + + return items + + def get_table(self, items): """ Generate a proper table node for autosummary:: directive. - *names* is a list of names of Python objects to be imported - and added to the table. + *items* is a list produced by :meth:`get_items` """ - state = self.state - document = state.document - - prefixes = [''] - prefixes.insert(0, document.settings.env.currmodule) - - real_names = {} - table = nodes.table('') group = nodes.tgroup('', cols=2) table.append(group) @@ -225,53 +270,20 @@ class Autosummary(Directive): node = nodes.paragraph('') vl = ViewList() vl.append(text, '') - state.nested_parse(vl, 0, node) + self.state.nested_parse(vl, 0, node) row.append(nodes.entry('', node)) body.append(row) - for name in names: - try: - obj, real_name = import_by_name(name, prefixes=prefixes) - except ImportError: - self.warn('failed to import %s' % name) - append_row(':obj:`%s`' % name, '') - continue - - documenter = get_documenter(obj)(self, real_name) - if not documenter.parse_name(): - append_row(':obj:`%s`' % name, '') - continue - if not documenter.import_object(): - append_row(':obj:`%s`' % name, '') - continue - - real_names[name] = real_name - - sig = documenter.format_signature() - if not sig or 'nosignatures' in self.options: - sig = '' - else: - sig = mangle_signature(sig) - - doc = list(documenter.process_doc(documenter.get_doc())) - - # grab the summary - while doc and not doc[0].strip(): - doc.pop(0) - m = re.search(r"^([A-Z].*?\.\s)", " ".join(doc).strip()) - if m: - summary = m.group(1).strip() - elif doc: - summary = doc[0].strip() - else: - summary = '' - + for name, sig, summary, real_name in items: qualifier = 'obj' - col1 = ':' + qualifier + r':`%s <%s>`\ %s' % (name, real_name, sig) + if 'nosignatures' not in self.options: + col1 = ':%s:`%s <%s>`\ %s' % (qualifier, name, real_name, sig) + else: + col1 = ':%s:`%s <%s>`' % (qualifier, name, real_name) col2 = summary append_row(col1, col2) - return table, real_names + return table def mangle_signature(sig, max_chars=30): """ From 3fecd0b3b64df671e7da4154999f956465e42385 Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Sat, 4 Apr 2009 23:47:58 +0300 Subject: [PATCH 09/15] autodoc: call autodoc-process-signature hook even if introspecting item signature failed --- sphinx/ext/autodoc.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/sphinx/ext/autodoc.py b/sphinx/ext/autodoc.py index 53f533979..caf34345a 100644 --- a/sphinx/ext/autodoc.py +++ b/sphinx/ext/autodoc.py @@ -372,8 +372,7 @@ class Documenter(object): else: # try to introspect the signature args = self.format_args() - if args is None: - return '' + retann = self.retann result = self.env.app.emit_firstresult( From f9272dc77713d95e424e925fe008c126cdd996ee Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Sun, 5 Apr 2009 00:01:49 +0300 Subject: [PATCH 10/15] autosummary: small signature formatting fixes --- sphinx/ext/autosummary/__init__.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/sphinx/ext/autosummary/__init__.py b/sphinx/ext/autosummary/__init__.py index d82c7088b..00e4307eb 100644 --- a/sphinx/ext/autosummary/__init__.py +++ b/sphinx/ext/autosummary/__init__.py @@ -229,7 +229,7 @@ class Autosummary(Directive): if not sig: sig = '' else: - sig = mangle_signature(sig) + sig = mangle_signature(sig).replace('*', r'\*') # -- Grab the summary @@ -312,8 +312,10 @@ def mangle_signature(sig, max_chars=30): args.append('...') break - if opts: + if opts and args: sig = ", ".join(args) + "[, " + ", ".join(opts) + "]" + elif opts and not args: + sig = "[" + ", ".join(opts) + "]" else: sig = ", ".join(args) From a888b2497928ad69e08c8c062c4763a77aeee48c Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Sun, 5 Apr 2009 00:10:28 +0300 Subject: [PATCH 11/15] autosummary: don't insert UTF-8 non-breaking spaces to signatures; these seem to cause problems for latex builds --- sphinx/ext/autosummary/__init__.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/sphinx/ext/autosummary/__init__.py b/sphinx/ext/autosummary/__init__.py index 00e4307eb..b1db7863f 100644 --- a/sphinx/ext/autosummary/__init__.py +++ b/sphinx/ext/autosummary/__init__.py @@ -259,8 +259,8 @@ class Autosummary(Directive): table = nodes.table('') group = nodes.tgroup('', cols=2) table.append(group) - group.append(nodes.colspec('', colwidth=10)) - group.append(nodes.colspec('', colwidth=90)) + group.append(nodes.colspec('', colwidth=30)) + group.append(nodes.colspec('', colwidth=70)) body = nodes.tbody('') group.append(body) @@ -319,8 +319,7 @@ def mangle_signature(sig, max_chars=30): else: sig = ", ".join(args) - sig = unicode(sig).replace(u" ", u"\u00a0") - return u"(%s)" % sig + return "(%s)" % sig # -- Importing items ----------------------------------------------------------- From 9cfdf9b13eaf5cc70e1f83f11e8d9155b2677671 Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Sun, 5 Apr 2009 00:17:47 +0300 Subject: [PATCH 12/15] latex: add support for UTF-8 non-breaking spaces, and reintroduce them in autosummary --- sphinx/ext/autosummary/__init__.py | 7 ++++--- sphinx/writers/latex.py | 2 ++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/sphinx/ext/autosummary/__init__.py b/sphinx/ext/autosummary/__init__.py index b1db7863f..00e4307eb 100644 --- a/sphinx/ext/autosummary/__init__.py +++ b/sphinx/ext/autosummary/__init__.py @@ -259,8 +259,8 @@ class Autosummary(Directive): table = nodes.table('') group = nodes.tgroup('', cols=2) table.append(group) - group.append(nodes.colspec('', colwidth=30)) - group.append(nodes.colspec('', colwidth=70)) + group.append(nodes.colspec('', colwidth=10)) + group.append(nodes.colspec('', colwidth=90)) body = nodes.tbody('') group.append(body) @@ -319,7 +319,8 @@ def mangle_signature(sig, max_chars=30): else: sig = ", ".join(args) - return "(%s)" % sig + sig = unicode(sig).replace(u" ", u"\u00a0") + return u"(%s)" % sig # -- Importing items ----------------------------------------------------------- diff --git a/sphinx/writers/latex.py b/sphinx/writers/latex.py index 37281fa9f..a0ea19614 100644 --- a/sphinx/writers/latex.py +++ b/sphinx/writers/latex.py @@ -30,6 +30,7 @@ from sphinx.util.smartypants import educateQuotesLatex HEADER = r'''%% Generated by Sphinx. \documentclass[%(papersize)s,%(pointsize)s%(classoptions)s]{%(docclass)s} %(inputenc)s +%(utf8extra)s %(fontenc)s %(babel)s %(fontpkg)s @@ -136,6 +137,7 @@ class LaTeXTranslator(nodes.NodeVisitor): 'pointsize': '10pt', 'classoptions': '', 'inputenc': '\\usepackage[utf8]{inputenc}', + 'utf8extra': '\\DeclareUnicodeCharacter{00A0}{\nobreakspace}', 'fontenc': '\\usepackage[T1]{fontenc}', 'babel': '\\usepackage{babel}', 'fontpkg': '\\usepackage{times}', From 866c015635502aa4b241497a52f2fd432445be99 Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Sun, 5 Apr 2009 00:26:32 +0300 Subject: [PATCH 13/15] latex: fix bad escaping in 3e1f33ab8245 --- sphinx/writers/latex.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sphinx/writers/latex.py b/sphinx/writers/latex.py index a0ea19614..339e827e2 100644 --- a/sphinx/writers/latex.py +++ b/sphinx/writers/latex.py @@ -137,7 +137,7 @@ class LaTeXTranslator(nodes.NodeVisitor): 'pointsize': '10pt', 'classoptions': '', 'inputenc': '\\usepackage[utf8]{inputenc}', - 'utf8extra': '\\DeclareUnicodeCharacter{00A0}{\nobreakspace}', + 'utf8extra': '\\DeclareUnicodeCharacter{00A0}{\\nobreakspace}', 'fontenc': '\\usepackage[T1]{fontenc}', 'babel': '\\usepackage{babel}', 'fontpkg': '\\usepackage{times}', From 0b402028f4733d54445b1f5c043d06a26d65b880 Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Sun, 5 Apr 2009 01:06:03 +0300 Subject: [PATCH 14/15] autosummary: don't push nested paragraphs in table entries --- sphinx/ext/autosummary/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sphinx/ext/autosummary/__init__.py b/sphinx/ext/autosummary/__init__.py index 00e4307eb..ef53394b8 100644 --- a/sphinx/ext/autosummary/__init__.py +++ b/sphinx/ext/autosummary/__init__.py @@ -271,7 +271,7 @@ class Autosummary(Directive): vl = ViewList() vl.append(text, '') self.state.nested_parse(vl, 0, node) - row.append(nodes.entry('', node)) + row.append(nodes.entry('', node[0])) body.append(row) for name, sig, summary, real_name in items: From 2ac809da8b803fcbde7d1e0db66c33f130aa169a Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Sun, 5 Apr 2009 01:21:52 +0300 Subject: [PATCH 15/15] autosummary: fix bug introduced in last commit --- sphinx/ext/autosummary/__init__.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/sphinx/ext/autosummary/__init__.py b/sphinx/ext/autosummary/__init__.py index ef53394b8..b34d35263 100644 --- a/sphinx/ext/autosummary/__init__.py +++ b/sphinx/ext/autosummary/__init__.py @@ -271,7 +271,12 @@ class Autosummary(Directive): vl = ViewList() vl.append(text, '') self.state.nested_parse(vl, 0, node) - row.append(nodes.entry('', node[0])) + try: + if isinstance(node[0], nodes.paragraph): + node = node[0] + except IndexError: + pass + row.append(nodes.entry('', node)) body.append(row) for name, sig, summary, real_name in items: