sphinx/tests/test_autodoc_py35.py

347 lines
11 KiB
Python
Raw Normal View History

# -*- coding: utf-8 -*-
"""
test_autodoc
~~~~~~~~~~~~
Test the autodoc extension. This tests mainly the Documenters; the auto
directives are tested in a test source file translated by test_build.
:copyright: Copyright 2007-2015 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
# "raises" imported for usage by autodoc
from util import TestApp, Struct, raises, SkipTest
from nose.tools import with_setup, eq_
from six import StringIO
from docutils.statemachine import ViewList
from sphinx.ext.autodoc import AutoDirective, add_documenter, \
ModuleLevelDocumenter, FunctionDocumenter, cut_lines, between, ALL
app = None
def setup_module():
global app
app = TestApp()
app.builder.env.app = app
app.builder.env.temp_data['docname'] = 'dummy'
app.connect('autodoc-process-docstring', process_docstring)
app.connect('autodoc-process-signature', process_signature)
app.connect('autodoc-skip-member', skip_member)
def teardown_module():
app.cleanup()
directive = options = None
def setup_test():
global options, directive
global processed_docstrings, processed_signatures, _warnings
options = Struct(
inherited_members = False,
undoc_members = False,
private_members = False,
special_members = False,
imported_members = False,
show_inheritance = False,
noindex = False,
annotation = None,
synopsis = '',
platform = '',
deprecated = False,
members = [],
member_order = 'alphabetic',
exclude_members = set(),
)
directive = Struct(
env = app.builder.env,
genopt = options,
result = ViewList(),
warn = warnfunc,
filename_set = set(),
)
processed_docstrings = []
processed_signatures = []
_warnings = []
_warnings = []
def warnfunc(msg):
_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 skip_member(app, what, name, obj, skip, options):
if name in ('__special1__', '__special2__'):
return skip
if name.startswith('_'):
return True
if name == 'skipmeth':
return True
@with_setup(setup_test)
def test_generate():
def assert_warns(warn_str, objtype, name, **kw):
inst = AutoDirective._registry[objtype](directive, name)
inst.generate(**kw)
assert len(directive.result) == 0, directive.result
assert len(_warnings) == 1, _warnings
assert warn_str in _warnings[0], _warnings
del _warnings[:]
def assert_works(objtype, name, **kw):
inst = AutoDirective._registry[objtype](directive, name)
inst.generate(**kw)
assert directive.result
#print '\n'.join(directive.result)
assert len(_warnings) == 0, _warnings
del directive.result[:]
def assert_processes(items, objtype, name, **kw):
del processed_docstrings[:]
del processed_signatures[:]
assert_works(objtype, name, **kw)
assert set(processed_docstrings) | set(processed_signatures) == \
set(items)
def assert_result_contains(item, objtype, name, **kw):
inst = AutoDirective._registry[objtype](directive, name)
inst.generate(**kw)
#print '\n'.join(directive.result)
assert len(_warnings) == 0, _warnings
assert item in directive.result
del directive.result[:]
def assert_order(items, objtype, name, member_order, **kw):
inst = AutoDirective._registry[objtype](directive, name)
inst.options.member_order = member_order
inst.generate(**kw)
assert len(_warnings) == 0, _warnings
items = list(reversed(items))
lineiter = iter(directive.result)
#for line in directive.result:
# if line.strip():
# print repr(line)
while items:
item = items.pop()
for line in lineiter:
if line == item:
break
else: # ran out of items!
assert False, 'item %r not found in result or not in the ' \
' correct order' % item
del directive.result[:]
options.members = []
# no module found?
assert_warns("import for autodocumenting 'foobar'",
'function', 'foobar', more_content=None)
# importing
assert_warns("failed to import module 'test_foobar'",
'module', 'test_foobar', more_content=None)
# attributes missing
assert_warns("failed to import function 'foobar' from module 'util'",
'function', 'util.foobar', more_content=None)
# method missing
assert_warns("failed to import method 'Class.foobar' from module 'test_autodoc_py35';",
'method', 'test_autodoc_py35.Class.foobar', more_content=None)
# test auto and given content mixing
directive.env.ref_context['py:module'] = 'test_autodoc_py35'
assert_result_contains(' Function.', 'method', 'Class.meth')
add_content = ViewList()
add_content.append('Content.', '', 0)
assert_result_contains(' Function.', 'method',
'Class.meth', more_content=add_content)
assert_result_contains(' Content.', 'method',
'Class.meth', more_content=add_content)
# test check_module
inst = FunctionDocumenter(directive, 'raises')
inst.generate(check_module=True)
assert len(directive.result) == 0
# assert that exceptions can be documented
assert_works('exception', 'test_autodoc_py35.CustomEx', all_members=True)
assert_works('exception', 'test_autodoc_py35.CustomEx')
# test diverse inclusion settings for members
should = [('class', 'test_autodoc_py35.Class')]
assert_processes(should, 'class', 'Class')
should.extend([('method', 'test_autodoc_py35.Class.meth')])
options.members = ['meth']
options.exclude_members = set(['excludemeth'])
assert_processes(should, 'class', 'Class')
should.extend([('attribute', 'test_autodoc_py35.Class.prop'),
('attribute', 'test_autodoc_py35.Class.descr'),
('attribute', 'test_autodoc_py35.Class.attr'),
('attribute', 'test_autodoc_py35.Class.docattr'),
('attribute', 'test_autodoc_py35.Class.udocattr'),
('attribute', 'test_autodoc_py35.Class.mdocattr'),
('attribute', 'test_autodoc_py35.Class.inst_attr_comment'),
('attribute', 'test_autodoc_py35.Class.inst_attr_inline'),
('attribute', 'test_autodoc_py35.Class.inst_attr_string'),
('method', 'test_autodoc_py35.Class.moore'),
])
if six.PY3 and sys.version_info[:2] >= (3, 5):
should.extend([
('method', 'test_autodoc_py35.Class.do_coroutine'),
])
options.members = ALL
assert_processes(should, 'class', 'Class')
options.undoc_members = True
should.extend((('attribute', 'test_autodoc_py35.Class.skipattr'),
('method', 'test_autodoc_py35.Class.undocmeth'),
('method', 'test_autodoc_py35.Class.roger')))
assert_processes(should, 'class', 'Class')
options.inherited_members = True
should.append(('method', 'test_autodoc_py35.Class.inheritedmeth'))
assert_processes(should, 'class', 'Class')
# test special members
options.special_members = ['__special1__']
should.append(('method', 'test_autodoc_py35.Class.__special1__'))
assert_processes(should, 'class', 'Class')
options.special_members = ALL
should.append(('method', 'test_autodoc_py35.Class.__special2__'))
assert_processes(should, 'class', 'Class')
options.special_members = False
# --- generate fodder ------------
import six, sys
__all__ = ['Class']
#: documentation for the integer
integer = 1
class CustomEx(Exception):
"""My custom exception."""
def f(self):
"""Exception method."""
class CustomDataDescriptor(object):
"""Descriptor class docstring."""
def __init__(self, doc):
self.__doc__ = doc
def __get__(self, obj, type=None):
if obj is None:
return self
return 42
def meth(self):
"""Function."""
return "The Answer"
def _funky_classmethod(name, b, c, d, docstring=None):
"""Generates a classmethod for a class from a template by filling out
some arguments."""
def template(cls, a, b, c, d=4, e=5, f=6):
return a, b, c, d, e, f
from functools import partial
function = partial(template, b=b, c=c, d=d)
function.__name__ = name
function.__doc__ = docstring
return classmethod(function)
class Base(object):
def inheritedmeth(self):
"""Inherited function."""
if six.PY3 and sys.version_info[:2] >= (3, 5):
async def _other_coro_func():
return "run"
class Class(Base):
"""Class to document."""
descr = CustomDataDescriptor("Descriptor instance docstring.")
def meth(self):
"""Function."""
def undocmeth(self):
pass
def skipmeth(self):
"""Method that should be skipped."""
def excludemeth(self):
"""Method that should be excluded."""
# should not be documented
skipattr = 'foo'
#: should be documented -- süß
attr = 'bar'
@property
def prop(self):
"""Property."""
docattr = 'baz'
"""should likewise be documented -- süß"""
udocattr = 'quux'
u"""should be documented as well - süß"""
# initialized to any class imported from another module
mdocattr = StringIO()
"""should be documented as well - süß"""
roger = _funky_classmethod("roger", 2, 3, 4)
moore = _funky_classmethod("moore", 9, 8, 7,
docstring="moore(a, e, f) -> happiness")
def __init__(self, arg):
self.inst_attr_inline = None #: an inline documented instance attr
#: a documented instance attribute
self.inst_attr_comment = None
self.inst_attr_string = None
"""a documented instance attribute"""
def __special1__(self):
"""documented special method"""
def __special2__(self):
# undocumented special method
pass
if six.PY3 and sys.version_info[:2] >= (3, 5):
async def do_coroutine(self):
"""A documented coroutine function"""
attr_coro_result = await _other_coro_func()