[Python-checkins] r65503 - in doctools/trunk: sphinx/ext/autodoc.py tests/root/conf.py tests/test_autodoc.py tests/test_quickstart.py tests/util.py

georg.brandl python-checkins at python.org
Mon Aug 4 21:39:06 CEST 2008


Author: georg.brandl
Date: Mon Aug  4 21:39:05 2008
New Revision: 65503

Log:
Add a test suite for autodoc.


Added:
   doctools/trunk/tests/test_autodoc.py   (contents, props changed)
Modified:
   doctools/trunk/sphinx/ext/autodoc.py
   doctools/trunk/tests/root/conf.py
   doctools/trunk/tests/test_quickstart.py
   doctools/trunk/tests/util.py

Modified: doctools/trunk/sphinx/ext/autodoc.py
==============================================================================
--- doctools/trunk/sphinx/ext/autodoc.py	(original)
+++ doctools/trunk/sphinx/ext/autodoc.py	Mon Aug  4 21:39:05 2008
@@ -284,7 +284,7 @@
         if what == 'module':
             if args or retann:
                 self.warn('ignoring signature arguments and return annotation '
-                          'for automodule %s' % mod)
+                          'for automodule %s' % fullname)
             return fullname, fullname, [], None, None
 
         elif what in ('class', 'exception', 'function'):
@@ -335,18 +335,21 @@
         else:
             # try to introspect the signature
             try:
+                args = None
+                getargs = True
                 if what == 'class':
                     # for classes, the relevant signature is the __init__ method's
                     obj = getattr(obj, '__init__', None)
                     # classes without __init__ method?
                     if obj is None or obj is object.__init__ or not \
                        (inspect.ismethod(obj) or inspect.isfunction(obj)):
-                        return ''
-                argspec = inspect.getargspec(obj)
-                if what in ('class', 'method') and argspec[0] and \
-                   argspec[0][0] in ('cls', 'self'):
-                    del argspec[0][0]
-                args = inspect.formatargspec(*argspec)
+                        getargs = False
+                if getargs:
+                    argspec = inspect.getargspec(obj)
+                    if what in ('class', 'method') and argspec[0] and \
+                           argspec[0][0] in ('cls', 'self'):
+                        del argspec[0][0]
+                    args = inspect.formatargspec(*argspec)
             except Exception, e:
                 args = None
                 err = e
@@ -392,7 +395,7 @@
             for part in objpath:
                 todoc = getattr(todoc, part)
         except (ImportError, AttributeError), err:
-            self.warn('autodoc can\'t import/find %s %r, it reported error: "%s",'
+            self.warn('autodoc can\'t import/find %s %r, it reported error: "%s", '
                       'please check your spelling and sys.path' %
                       (what, str(fullname), err))
             return

Modified: doctools/trunk/tests/root/conf.py
==============================================================================
--- doctools/trunk/tests/root/conf.py	(original)
+++ doctools/trunk/tests/root/conf.py	Mon Aug  4 21:39:05 2008
@@ -23,7 +23,7 @@
 
 # Add any Sphinx extension module names here, as strings. They can be extensions
 # coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
-extensions = ['ext']
+extensions = ['ext', 'sphinx.ext.autodoc']
 
 # Add any paths that contain templates here, relative to this directory.
 templates_path = ['_templates']

Added: doctools/trunk/tests/test_autodoc.py
==============================================================================
--- (empty file)
+++ doctools/trunk/tests/test_autodoc.py	Mon Aug  4 21:39:05 2008
@@ -0,0 +1,331 @@
+# -*- coding: utf-8 -*-
+"""
+    test_autodoc
+    ~~~~~~~~~~~~
+
+    Test the autodoc extension.  This tests mainly the RstGenerator; the auto
+    directives are tested in a test source file translated by test_build.
+
+    :copyright: 2008 by Georg Brandl.
+    :license: BSD.
+"""
+
+from util import *
+
+from docutils.statemachine import ViewList
+
+from sphinx.ext.autodoc import RstGenerator
+
+
+def setup_module():
+    global app, lid, options, gen
+
+    app = TestApp()
+    app.builder.env.app = app
+    app.connect('autodoc-process-docstring', process_docstring)
+    app.connect('autodoc-process-signature', process_signature)
+
+    options = Struct(
+        inherited_members = False,
+        undoc_members = False,
+        show_inheritance = False,
+        noindex = False,
+        synopsis = '',
+        platform = '',
+        deprecated = False,
+    )
+
+    gen = TestGenerator(options, app)
+
+def teardown_module():
+    app.cleanup()
+
+
+class TestGenerator(RstGenerator):
+    """Generator that handles warnings without a reporter."""
+
+    def __init__(self, options, app):
+        self.options = options
+        self.env = app.builder.env
+        self.lineno = 42
+        self.filename_set = set()
+        self.warnings = []
+        self.result = ViewList()
+
+    def warn(self, msg):
+        self.warnings.append(msg)
+
+
+processed_docstrings = []
+
+def process_docstring(app, what, name, obj, options, lines):
+    processed_docstrings.append((what, name))
+    if name == 'bar':
+        lines.extend(['42', ''])
+
+processed_signatures = []
+
+def process_signature(app, what, name, obj, options, args, retann):
+    processed_signatures.append((what, name))
+    if name == 'bar':
+        return '42', None
+
+
+def test_resolve_name():
+    # for modules
+    assert gen.resolve_name('module', 'test_autodoc') == \
+           ('test_autodoc', 'test_autodoc', [], None, None)
+    assert gen.resolve_name('module', 'test.test_autodoc') == \
+           ('test.test_autodoc', 'test.test_autodoc', [], None, None)
+
+    assert gen.resolve_name('module', 'test(arg)') == \
+           ('test', 'test', [], None, None)
+    assert 'ignoring signature arguments' in gen.warnings[0]
+    del gen.warnings[:]
+
+    # for functions/classes
+    assert gen.resolve_name('function', 'util.raises') == \
+           ('util.raises', 'util', ['raises'], None, None)
+    assert gen.resolve_name('function', 'util.raises(exc) -> None') == \
+           ('util.raises', 'util', ['raises'], 'exc', ' -> None')
+    gen.env.autodoc_current_module = 'util'
+    assert gen.resolve_name('function', 'raises') == \
+           ('raises', 'util', ['raises'], None, None)
+    gen.env.autodoc_current_module = None
+    gen.env.currmodule = 'util'
+    assert gen.resolve_name('function', 'raises') == \
+           ('raises', 'util', ['raises'], None, None)
+    assert gen.resolve_name('class', 'TestApp') == \
+           ('TestApp', 'util', ['TestApp'], None, None)
+
+    # for members
+    gen.env.currmodule = 'foo'
+    assert gen.resolve_name('method', 'util.TestApp.cleanup') == \
+           ('util.TestApp.cleanup', 'util', ['TestApp', 'cleanup'], None, None)
+    gen.env.currmodule = 'util'
+    gen.env.currclass = 'Foo'
+    gen.env.autodoc_current_class = 'TestApp'
+    assert gen.resolve_name('method', 'cleanup') == \
+           ('cleanup', 'util', ['TestApp', 'cleanup'], None, None)
+    assert gen.resolve_name('method', 'TestApp.cleanup') == \
+           ('TestApp.cleanup', 'util', ['TestApp', 'cleanup'], None, None)
+
+    # and clean up
+    gen.env.currmodule = None
+    gen.env.currclass = None
+    gen.env.autodoc_current_class = None
+
+
+def test_format_signature():
+    # no signatures for modules
+    assert gen.format_signature('module', 'test', None, None, None) == ''
+
+    # test for functions
+    def f(a, b, c=1, **d):
+        pass
+    assert gen.format_signature('function', 'f', f, None, None) == '(a, b, c=1, **d)'
+    assert gen.format_signature('function', 'f', f, 'a, b, c, d', None) == \
+           '(a, b, c, d)'
+    assert gen.format_signature('function', 'f', f, None, ' -> None') == \
+           '(a, b, c=1, **d) -> None'
+
+    # test for classes
+    class D:
+        pass
+    class E(object):
+        pass
+    # no signature for classes without __init__
+    for C in (D, E):
+        assert gen.format_signature('class', 'D', C, None, None) == ''
+    class F:
+        def __init__(self, a, b=None):
+            pass
+    class G(F, object):
+        pass
+    for C in (F, G):
+        assert gen.format_signature('class', 'C', C, None, None) == '(a, b=None)'
+    assert gen.format_signature('class', 'C', D, 'a, b', ' -> X') == '(a, b) -> X'
+
+    # test for methods
+    class H:
+        def foo1(self, b, *c):
+            pass
+        def foo2(b, *c):
+            pass
+    assert gen.format_signature('method', 'H.foo', H.foo1, None, None) == '(b, *c)'
+    assert gen.format_signature('method', 'H.foo', H.foo1, 'a', None) == '(a)'
+    assert gen.format_signature('method', 'H.foo', H.foo2, None, None) == '(b, *c)'
+
+    # test exception handling
+    raises(RuntimeError, gen.format_signature, 'function', 'int', int, None, None)
+
+    # test processing by event handler
+    assert gen.format_signature('method', 'bar', H.foo1, None, None) == '42'
+
+
+def test_get_doc():
+    def getdocl(*args):
+        # strip the empty line at the end
+        return list(gen.get_doc(*args))[:-1]
+
+    # objects without docstring
+    def f():
+        pass
+    assert getdocl('function', 'f', f) == []
+
+    # standard function, diverse docstring styles...
+    def f():
+        """Docstring"""
+    def g():
+        """
+        Docstring
+        """
+    for func in (f, g):
+        assert getdocl('function', 'f', func) == ['Docstring']
+
+    # first line vs. other lines indentation
+    def f():
+        """First line
+
+        Other
+          lines
+        """
+    assert getdocl('function', 'f', f) == ['First line', '', 'Other', '  lines']
+
+    # charset guessing (this module is encoded in utf-8)
+    def f():
+        """Döcstring"""
+    assert getdocl('function', 'f', f) == [u'Döcstring']
+
+    # already-unicode docstrings must be taken literally
+    def f():
+        u"""Döcstring"""
+    assert getdocl('function', 'f', f) == [u'Döcstring']
+
+    # class docstring: depends on config value which one is taken
+    class C:
+        """Class docstring"""
+        def __init__(self):
+            """Init docstring"""
+    gen.env.config.autoclass_content = 'class'
+    assert getdocl('class', 'C', C) == ['Class docstring']
+    gen.env.config.autoclass_content = 'init'
+    assert getdocl('class', 'C', C) == ['Init docstring']
+    gen.env.config.autoclass_content = 'both'
+    assert getdocl('class', 'C', C) == ['Class docstring', '', 'Init docstring']
+
+    class D:
+        def __init__(self):
+            """Init docstring"""
+
+    # docstring processing by event handler
+    assert getdocl('class', 'bar', D) == ['Init docstring', '', '42']
+
+
+def test_generate():
+    def assert_warns(warn_str, *args):
+        gen.generate(*args)
+        assert len(gen.result) == 0, gen.result
+        assert len(gen.warnings) == 1, gen.warnings
+        assert warn_str in gen.warnings[0], gen.warnings
+        del gen.warnings[:]
+
+    def assert_works(*args):
+        gen.generate(*args)
+        assert gen.result
+        assert len(gen.warnings) == 0, gen.warnings
+        del gen.result[:]
+
+    def assert_processes(items, *args):
+        del processed_docstrings[:]
+        del processed_signatures[:]
+        assert_works(*args)
+        assert set(processed_docstrings) | set(processed_signatures) == set(items)
+
+    def assert_result_contains(item, *args):
+        gen.generate(*args)
+        assert len(gen.warnings) == 0, gen.warnings
+        assert item in gen.result
+        del gen.result[:]
+
+    # no module found?
+    assert_warns("import for autodocumenting 'foobar'",
+                 'function', 'foobar', None, None)
+    # importing
+    assert_warns("import/find module 'test_foobar'",
+                 'module', 'test_foobar', None, None)
+    # attributes missing
+    assert_warns("import/find function 'util.foobar'",
+                 'function', 'util.foobar', None, None)
+
+    # test auto and given content mixing
+    gen.env.currmodule = 'test_autodoc'
+    assert_result_contains('   Function.', 'method', 'Class.meth', [], None)
+    add_content = ViewList()
+    add_content.append('Content.', '', 0)
+    assert_result_contains('   Function.', 'method', 'Class.meth', [], add_content)
+    assert_result_contains('   Content.', 'method', 'Class.meth', [], add_content)
+
+    # test check_module
+    gen.generate('function', 'raises', None, None, check_module=True)
+    assert len(gen.result) == 0
+
+    # assert that exceptions can be documented
+    assert_works('exception', 'test_autodoc.CustomEx', ['__all__'], None)
+    assert_works('exception', 'test_autodoc.CustomEx', [], None)
+
+    # test diverse inclusion settings for members
+    should = [('class', 'Class')]
+    assert_processes(should, 'class', 'Class', [], None)
+    should.extend([('method', 'Class.meth')])
+    assert_processes(should, 'class', 'Class', ['meth'], None)
+    should.extend([('attribute', 'Class.prop')])
+    assert_processes(should, 'class', 'Class', ['__all__'], None)
+    options.undoc_members = True
+    should.append(('method', 'Class.undocmeth'))
+    assert_processes(should, 'class', 'Class', ['__all__'], None)
+    options.inherited_members = True
+    should.append(('method', 'Class.inheritedmeth'))
+    assert_processes(should, 'class', 'Class', ['__all__'], None)
+
+    # test module flags
+    assert_result_contains('.. module:: test_autodoc', 'module',
+                           'test_autodoc', [], None)
+    options.synopsis = 'Synopsis'
+    assert_result_contains('   :synopsis: Synopsis', 'module', 'test_autodoc', [], None)
+    options.deprecated = True
+    assert_result_contains('   :deprecated:', 'module', 'test_autodoc', [], None)
+    options.platform = 'Platform'
+    assert_result_contains('   :platform: Platform', 'module', 'test_autodoc', [], None)
+
+    # test noindex flag
+    options.noindex = True
+    assert_result_contains('   :noindex:', 'module', 'test_autodoc', [], None)
+    assert_result_contains('   :noindex:', 'class', 'Base', [], None)
+
+
+# --- generate fodder ------------
+
+class CustomEx(Exception):
+    """My custom exception."""
+
+    def f(self):
+        """Exception method."""
+
+
+class Base(object):
+    def inheritedmeth(self):
+        """Inherited function."""
+
+class Class(Base):
+    """Class to document."""
+
+    def meth(self):
+        """Function."""
+
+    def undocmeth(self):
+        pass
+
+    @property
+    def prop(self):
+        """Property."""

Modified: doctools/trunk/tests/test_quickstart.py
==============================================================================
--- doctools/trunk/tests/test_quickstart.py	(original)
+++ doctools/trunk/tests/test_quickstart.py	Mon Aug  4 21:39:05 2008
@@ -117,6 +117,7 @@
         'Name of your master document': 'contents',
         'autodoc': 'y',
         'doctest': 'yes',
+        'intersphinx': 'no',
         'Create Makefile': 'no',
     }
     qs.raw_input = mock_raw_input(answers, needanswer=True)

Modified: doctools/trunk/tests/util.py
==============================================================================
--- doctools/trunk/tests/util.py	(original)
+++ doctools/trunk/tests/util.py	Mon Aug  4 21:39:05 2008
@@ -24,7 +24,7 @@
 
 __all__ = [
     'test_root',
-    'raises', 'raises_msg',
+    'raises', 'raises_msg', 'Struct',
     'ListOutput', 'TestApp', 'with_testapp',
     'path', 'with_tempdir', 'write_file',
     'sprint',
@@ -66,6 +66,11 @@
                              (func.__name__, _excstr(exc)))
 
 
+class Struct(object):
+    def __init__(self, **kwds):
+        self.__dict__.update(kwds)
+
+
 class ListOutput(object):
     """
     File-like object that collects written text in a list.


More information about the Python-checkins mailing list