diff --git a/CHANGES b/CHANGES index 03b0ea864..08cc0ad4c 100644 --- a/CHANGES +++ b/CHANGES @@ -14,6 +14,7 @@ Bugs fixed object to str implicitly" error when link target url has a hash part. Thanks to Jorge_C. * #1467: Exception on Python3 if nonexistent method is specified by automethod +* #1441: autosummary can't handle nested classes correctly. Release 1.2.2 (released Mar 2, 2014) ==================================== diff --git a/sphinx/ext/autosummary/__init__.py b/sphinx/ext/autosummary/__init__.py index 8a23da9ef..906b3c201 100644 --- a/sphinx/ext/autosummary/__init__.py +++ b/sphinx/ext/autosummary/__init__.py @@ -243,16 +243,21 @@ class Autosummary(Directive): display_name = name.split('.')[-1] try: - real_name, obj, parent = import_by_name(name, prefixes=prefixes) + real_name, obj, parent, modname = 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 - # handle module prefixes slightly differently self.result = ViewList() # initialize for each documenter - documenter = get_documenter(obj, parent)(self, real_name) + full_name = real_name + if full_name.startswith(modname + '.'): + # give explicitly separated module name, so that members + # of inner classes can be documented + full_name = modname + '::' + full_name[len(modname)+1:] + # NB. using full_name here is important, since Documenters + # handle module prefixes slightly differently + documenter = get_documenter(obj, parent)(self, full_name) if not documenter.parse_name(): self.warn('failed to parse name %s' % real_name) items.append((display_name, '', '', real_name)) @@ -447,8 +452,8 @@ def import_by_name(name, prefixes=[None]): prefixed_name = '.'.join([prefix, name]) else: prefixed_name = name - obj, parent = _import_by_name(prefixed_name) - return prefixed_name, obj, parent + obj, parent, modname = _import_by_name(prefixed_name) + return prefixed_name, obj, parent, modname except ImportError: tried.append(prefixed_name) raise ImportError('no module named %s' % ' or '.join(tried)) @@ -464,7 +469,7 @@ def _import_by_name(name): try: __import__(modname) mod = sys.modules[modname] - return getattr(mod, name_parts[-1]), mod + return getattr(mod, name_parts[-1]), mod, modname except (ImportError, IndexError, AttributeError): pass @@ -487,9 +492,9 @@ def _import_by_name(name): for obj_name in name_parts[last_j:]: parent = obj obj = getattr(obj, obj_name) - return obj, parent + return obj, parent, modname else: - return sys.modules[modname], None + return sys.modules[modname], None, modname except (ValueError, ImportError, AttributeError, KeyError), e: raise ImportError(*e.args) @@ -510,7 +515,7 @@ def autolink_role(typ, rawtext, etext, lineno, inliner, prefixes = get_import_prefixes_from_env(env) try: - name, obj, parent = import_by_name(pnode['reftarget'], prefixes) + name, obj, parent, modname = import_by_name(pnode['reftarget'], prefixes) except ImportError: content = pnode[0] r[0][0] = nodes.emphasis(rawtext, content[0].astext(), diff --git a/sphinx/ext/autosummary/generate.py b/sphinx/ext/autosummary/generate.py index c4f150a0e..20e7fc916 100644 --- a/sphinx/ext/autosummary/generate.py +++ b/sphinx/ext/autosummary/generate.py @@ -126,7 +126,7 @@ def generate_autosummary_docs(sources, output_dir=None, suffix='.rst', ensuredir(path) try: - name, obj, parent = import_by_name(name) + name, obj, parent, mod_name = import_by_name(name) except ImportError, e: warn('[autosummary] failed to import %r: %s' % (name, e)) continue @@ -235,7 +235,7 @@ def find_autosummary_in_docstring(name, module=None, filename=None): See `find_autosummary_in_lines`. """ try: - real_name, obj, parent = import_by_name(name) + real_name, obj, parent, modname = import_by_name(name) lines = pydoc.getdoc(obj).splitlines() return find_autosummary_in_lines(lines, module=name, filename=filename) except AttributeError: diff --git a/tests/roots/test-autosummary/dummy_module.py b/tests/roots/test-autosummary/dummy_module.py index a150188d5..a64035d69 100644 --- a/tests/roots/test-autosummary/dummy_module.py +++ b/tests/roots/test-autosummary/dummy_module.py @@ -5,6 +5,7 @@ C.class_attr C.prop_attr1 C.prop_attr2 + C.C2 """ def withSentence(): @@ -63,3 +64,8 @@ class C: value is string. """ + + class C2: + ''' + This is a nested inner class docstring + ''' diff --git a/tests/test_autosummary.py b/tests/test_autosummary.py index e6018e3d7..de26a0546 100644 --- a/tests/test_autosummary.py +++ b/tests/test_autosummary.py @@ -10,11 +10,14 @@ """ import sys from functools import wraps +from StringIO import StringIO from sphinx.ext.autosummary import mangle_signature from util import test_roots, TestApp +html_warnfile = StringIO() + def with_autosummary_app(*args, **kw): default_kw = { @@ -75,7 +78,7 @@ def test_mangle_signature(): assert res == outp, (u"'%s' -> '%s' != '%s'" % (inp, res, outp)) -@with_autosummary_app(buildername='html') +@with_autosummary_app(buildername='html', warning=html_warnfile) def test_get_items_summary(app): app.builddir.rmtree(True) @@ -98,6 +101,9 @@ def test_get_items_summary(app): finally: sphinx.ext.autosummary.Autosummary.get_items = orig_get_items + html_warnings = html_warnfile.getvalue() + assert html_warnings == '' + expected_values = { 'withSentence': 'I have a sentence which spans multiple lines.', 'noSentence': "this doesn't start with a", @@ -106,6 +112,7 @@ def test_get_items_summary(app): 'C.class_attr': 'This is a class attribute', 'C.prop_attr1': 'This is a function docstring', 'C.prop_attr2': 'This is a attribute docstring', + 'C.C2': 'This is a nested inner class docstring', } for key, expected in expected_values.iteritems(): assert autosummary_items[key][2] == expected, 'Summary for %s was %r -'\