refactor: test_autodoc

This commit is contained in:
Takeshi KOMIYA 2018-07-31 20:14:18 +09:00
parent e5a3f67cf7
commit de73ee5902

View File

@ -19,7 +19,7 @@ from docutils.statemachine import ViewList
from six import PY3 from six import PY3
from sphinx.ext.autodoc import ( from sphinx.ext.autodoc import (
AutoDirective, ModuleLevelDocumenter, FunctionDocumenter, cut_lines, between, ALL AutoDirective, ModuleLevelDocumenter, cut_lines, between, ALL
) )
from sphinx.ext.autodoc.directive import DocumenterBridge, process_documenter_options from sphinx.ext.autodoc.directive import DocumenterBridge, process_documenter_options
from sphinx.testing.util import SphinxTestApp, Struct # NOQA from sphinx.testing.util import SphinxTestApp, Struct # NOQA
@ -28,6 +28,11 @@ from sphinx.util.docutils import LoggingReporter
app = None app = None
if PY3:
ROGER_METHOD = ' .. py:classmethod:: Class.roger(a, *, b=2, c=3, d=4, e=5, f=6)'
else:
ROGER_METHOD = ' .. py:classmethod:: Class.roger(a, e=5, f=6)'
def do_autodoc(app, objtype, name, options={}): def do_autodoc(app, objtype, name, options={}):
doccls = app.registry.documenters[objtype] doccls = app.registry.documenters[objtype]
@ -617,73 +622,12 @@ def test_attrgetter_using():
@pytest.mark.usefixtures('setup_test') @pytest.mark.usefixtures('setup_test')
def test_generate(): def test_generate():
logging.setup(app, app._status, app._warning)
def assert_warns(warn_str, objtype, name, **kw):
inst = app.registry.documenters[objtype](directive, name)
inst.generate(**kw)
assert len(directive.result) == 0, directive.result
assert warn_str in app._warning.getvalue()
app._warning.truncate(0)
def assert_works(objtype, name, **kw):
inst = app.registry.documenters[objtype](directive, name)
inst.generate(**kw)
assert directive.result
# print '\n'.join(directive.result)
assert app._warning.getvalue() == ''
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): def assert_result_contains(item, objtype, name, **kw):
inst = app.registry.documenters[objtype](directive, name) inst = app.registry.documenters[objtype](directive, name)
inst.generate(**kw) inst.generate(**kw)
# print '\n'.join(directive.result)
assert app._warning.getvalue() == ''
assert item in directive.result assert item in directive.result
del directive.result[:] del directive.result[:]
def assert_order(items, objtype, name, member_order, **kw):
inst = app.registry.documenters[objtype](directive, name)
inst.options.member_order = member_order
inst.generate(**kw)
assert app._warning.getvalue() == ''
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 'target';",
'method', 'target.Class.foobar', more_content=None)
# test auto and given content mixing # test auto and given content mixing
directive.env.ref_context['py:module'] = 'target' directive.env.ref_context['py:module'] = 'target'
assert_result_contains(' Function.', 'method', 'Class.meth') assert_result_contains(' Function.', 'method', 'Class.meth')
@ -694,80 +638,238 @@ def test_generate():
assert_result_contains(' Content.', 'method', assert_result_contains(' Content.', 'method',
'Class.meth', more_content=add_content) 'Class.meth', more_content=add_content)
# test check_module
inst = FunctionDocumenter(directive, 'add_documenter')
inst.generate(check_module=True)
assert len(directive.result) == 0
# assert that exceptions can be documented @pytest.mark.sphinx('html', testroot='ext-autodoc')
assert_works('exception', 'target.CustomEx', all_members=True) def test_autodoc_exception(app):
assert_works('exception', 'target.CustomEx') actual = do_autodoc(app, 'exception', 'target.CustomEx')
assert list(actual) == [
'',
'.. py:exception:: CustomEx',
' :module: target',
'',
' My custom exception.',
' '
]
# test diverse inclusion settings for members
should = [('class', 'target.Class')]
assert_processes(should, 'class', 'Class')
should.extend([('method', 'target.Class.meth')])
options.members = ['meth']
options.exclude_members = set(['excludemeth'])
assert_processes(should, 'class', 'Class')
should.extend([('attribute', 'target.Class.prop'),
('attribute', 'target.Class.descr'),
('attribute', 'target.Class.attr'),
('attribute', 'target.Class.docattr'),
('attribute', 'target.Class.udocattr'),
('attribute', 'target.Class.mdocattr'),
('attribute', 'target.Class.inst_attr_comment'),
('attribute', 'target.Class.inst_attr_inline'),
('attribute', 'target.Class.inst_attr_string'),
('method', 'target.Class.moore'),
])
options.members = ALL
assert_processes(should, 'class', 'Class')
options.undoc_members = True
should.extend((('attribute', 'target.Class.skipattr'),
('method', 'target.Class.undocmeth'),
('method', 'target.Class.roger')))
assert_processes(should, 'class', 'Class')
options.inherited_members = True
should.append(('method', 'target.Class.inheritedmeth'))
should.append(('method', 'target.Class.inheritedclassmeth'))
should.append(('method', 'target.Class.inheritedstaticmeth'))
assert_processes(should, 'class', 'Class')
# test special members @pytest.mark.sphinx('html', testroot='ext-autodoc')
options.special_members = ['__special1__'] def test_autodoc_warnings(app, warning):
should.append(('method', 'target.Class.__special1__')) app.env.temp_data['docname'] = 'dummy'
assert_processes(should, 'class', 'Class')
options.special_members = ALL
should.append(('method', 'target.Class.__special2__'))
assert_processes(should, 'class', 'Class')
options.special_members = False
options.members = [] # can't import module
# test module flags do_autodoc(app, 'module', 'unknown')
assert_result_contains('.. py:module:: target', assert "failed to import module 'unknown'" in warning.getvalue()
'module', 'target')
options.synopsis = 'Synopsis'
assert_result_contains(' :synopsis: Synopsis', 'module', 'target')
options.deprecated = True
assert_result_contains(' :deprecated:', 'module', 'target')
options.platform = 'Platform'
assert_result_contains(' :platform: Platform', 'module', 'target')
# test if __all__ is respected for modules
options.members = ALL
assert_result_contains('.. py:class:: Class(arg)', 'module', 'target')
try:
assert_result_contains('.. py:exception:: CustomEx',
'module', 'target')
except AssertionError:
pass
else:
assert False, 'documented CustomEx which is not in __all__'
# test ignore-module-all # missing function
options.ignore_module_all = True do_autodoc(app, 'function', 'unknown')
assert_result_contains('.. py:class:: Class(arg)', 'module', 'target') assert "import for autodocumenting 'unknown'" in warning.getvalue()
assert_result_contains('.. py:exception:: CustomEx', 'module', 'target')
do_autodoc(app, 'function', 'target.unknown')
assert "failed to import function 'unknown' from module 'target'" in warning.getvalue()
# missing method
do_autodoc(app, 'method', 'target.Class.unknown')
assert "failed to import method 'Class.unknown' from module 'target'" in warning.getvalue()
@pytest.mark.sphinx('html', testroot='ext-autodoc')
def test_autodoc_attributes(app):
options = {"synopsis": 'Synopsis',
"platform": "Platform",
"deprecated": None}
actual = do_autodoc(app, 'module', 'target', options)
assert list(actual) == [
'',
'.. py:module:: target',
' :synopsis: Synopsis',
' :platform: Platform',
' :deprecated:',
''
]
@pytest.mark.sphinx('html', testroot='ext-autodoc')
def test_autodoc_members(app):
# default (no-members)
actual = do_autodoc(app, 'class', 'target.Base')
assert list(filter(lambda l: '::' in l, actual)) == [
'.. py:class:: Base',
]
# default ALL-members
options = {"members": None}
actual = do_autodoc(app, 'class', 'target.Base', options)
assert list(filter(lambda l: '::' in l, actual)) == [
'.. py:class:: Base',
' .. py:classmethod:: Base.inheritedclassmeth()',
' .. py:method:: Base.inheritedmeth()',
' .. py:staticmethod:: Base.inheritedstaticmeth(cls)'
]
# default specific-members
options = {"members": "inheritedmeth,inheritedstaticmeth"}
actual = do_autodoc(app, 'class', 'target.Base', options)
assert list(filter(lambda l: '::' in l, actual)) == [
'.. py:class:: Base',
' .. py:method:: Base.inheritedmeth()',
' .. py:staticmethod:: Base.inheritedstaticmeth(cls)'
]
@pytest.mark.sphinx('html', testroot='ext-autodoc')
def test_autodoc_exclude_members(app):
options = {"members": None,
"exclude-members": "inheritedmeth,inheritedstaticmeth"}
actual = do_autodoc(app, 'class', 'target.Base', options)
assert list(filter(lambda l: '::' in l, actual)) == [
'.. py:class:: Base',
' .. py:classmethod:: Base.inheritedclassmeth()'
]
# members vs exclude-members
options = {"members": "inheritedmeth",
"exclude-members": "inheritedmeth"}
actual = do_autodoc(app, 'class', 'target.Base', options)
assert list(filter(lambda l: '::' in l, actual)) == [
'.. py:class:: Base',
]
@pytest.mark.sphinx('html', testroot='ext-autodoc')
def test_autodoc_undoc_members(app):
options = {"members": None,
"undoc-members": None}
actual = do_autodoc(app, 'class', 'target.Class', options)
assert list(filter(lambda l: '::' in l, actual)) == [
'.. py:class:: Class(arg)',
' .. py:attribute:: Class.attr',
' .. py:attribute:: Class.descr',
' .. py:attribute:: Class.docattr',
' .. py:method:: Class.excludemeth()',
' .. py:attribute:: Class.inst_attr_comment',
' .. py:attribute:: Class.inst_attr_inline',
' .. py:attribute:: Class.inst_attr_string',
' .. py:attribute:: Class.mdocattr',
' .. py:method:: Class.meth()',
' .. py:classmethod:: Class.moore(a, e, f) -> happiness',
' .. py:attribute:: Class.prop',
ROGER_METHOD,
' .. py:attribute:: Class.skipattr',
' .. py:method:: Class.skipmeth()',
' .. py:attribute:: Class.udocattr',
' .. py:method:: Class.undocmeth()'
]
@pytest.mark.sphinx('html', testroot='ext-autodoc')
def test_autodoc_inherited_members(app):
options = {"members": None,
"inherited-members": None}
actual = do_autodoc(app, 'class', 'target.Class', options)
assert list(filter(lambda l: 'method::' in l, actual)) == [
' .. py:method:: Class.excludemeth()',
' .. py:classmethod:: Class.inheritedclassmeth()',
' .. py:method:: Class.inheritedmeth()',
' .. py:staticmethod:: Class.inheritedstaticmeth(cls)',
' .. py:method:: Class.meth()',
' .. py:classmethod:: Class.moore(a, e, f) -> happiness',
' .. py:method:: Class.skipmeth()'
]
@pytest.mark.sphinx('html', testroot='ext-autodoc')
def test_autodoc_imported_members(app):
options = {"members": None,
"imported-members": None,
"ignore-module-all": None}
actual = do_autodoc(app, 'module', 'target', options)
assert '.. py:function:: add_documenter(cls)' in actual
@pytest.mark.sphinx('html', testroot='ext-autodoc')
def test_autodoc_special_members(app):
# all special methods
options = {"members": None,
"undoc-members": None,
"special-members": None}
actual = do_autodoc(app, 'class', 'target.Class', options)
assert list(filter(lambda l: '::' in l, actual)) == [
'.. py:class:: Class(arg)',
' .. py:method:: Class.__init__(arg)',
' .. py:attribute:: Class.__module__',
' .. py:method:: Class.__special1__()',
' .. py:method:: Class.__special2__()',
' .. py:attribute:: Class.attr',
' .. py:attribute:: Class.descr',
' .. py:attribute:: Class.docattr',
' .. py:method:: Class.excludemeth()',
' .. py:attribute:: Class.inst_attr_comment',
' .. py:attribute:: Class.inst_attr_inline',
' .. py:attribute:: Class.inst_attr_string',
' .. py:attribute:: Class.mdocattr',
' .. py:method:: Class.meth()',
' .. py:classmethod:: Class.moore(a, e, f) -> happiness',
' .. py:attribute:: Class.prop',
ROGER_METHOD,
' .. py:attribute:: Class.skipattr',
' .. py:method:: Class.skipmeth()',
' .. py:attribute:: Class.udocattr',
' .. py:method:: Class.undocmeth()'
]
# specific special methods
options = {"members": None,
"undoc-members": None,
"special-members": "__init__,__special1__"}
actual = do_autodoc(app, 'class', 'target.Class', options)
assert list(filter(lambda l: '::' in l, actual)) == [
'.. py:class:: Class(arg)',
' .. py:method:: Class.__init__(arg)',
' .. py:method:: Class.__special1__()',
' .. py:attribute:: Class.attr',
' .. py:attribute:: Class.descr',
' .. py:attribute:: Class.docattr',
' .. py:method:: Class.excludemeth()',
' .. py:attribute:: Class.inst_attr_comment',
' .. py:attribute:: Class.inst_attr_inline',
' .. py:attribute:: Class.inst_attr_string',
' .. py:attribute:: Class.mdocattr',
' .. py:method:: Class.meth()',
' .. py:classmethod:: Class.moore(a, e, f) -> happiness',
' .. py:attribute:: Class.prop',
ROGER_METHOD,
' .. py:attribute:: Class.skipattr',
' .. py:method:: Class.skipmeth()',
' .. py:attribute:: Class.udocattr',
' .. py:method:: Class.undocmeth()'
]
@pytest.mark.sphinx('html', testroot='ext-autodoc')
def test_autodoc_ignore_module_all(app):
# default (no-ignore-module-all)
options = {"members": None}
actual = do_autodoc(app, 'module', 'target', options)
assert list(filter(lambda l: 'class::' in l, actual)) == [
'.. py:class:: Class(arg)',
]
# ignore-module-all
options = {"members": None,
"ignore-module-all": None}
actual = do_autodoc(app, 'module', 'target', options)
assert list(filter(lambda l: 'class::' in l, actual)) == [
'.. py:class:: Class(arg)',
'.. py:class:: CustomDataDescriptor(doc)',
'.. py:class:: CustomDataDescriptor2(doc)',
'.. py:class:: CustomDataDescriptorMeta',
'.. py:class:: CustomDict',
'.. py:class:: EnumCls',
'.. py:class:: InstAttCls()',
'.. py:class:: Outer',
' .. py:class:: Outer.Inner',
'.. py:class:: StrRepr'
]
@pytest.mark.sphinx('html', testroot='ext-autodoc') @pytest.mark.sphinx('html', testroot='ext-autodoc')
@ -889,11 +991,6 @@ def test_autodoc_c_module(app):
@pytest.mark.sphinx('html', testroot='ext-autodoc') @pytest.mark.sphinx('html', testroot='ext-autodoc')
def test_autodoc_member_order(app): def test_autodoc_member_order(app):
if PY3:
roger_method = ' .. py:classmethod:: Class.roger(a, *, b=2, c=3, d=4, e=5, f=6)'
else:
roger_method = ' .. py:classmethod:: Class.roger(a, e=5, f=6)'
# case member-order='bysource' # case member-order='bysource'
options = {"members": None, options = {"members": None,
'member-order': 'bysource', 'member-order': 'bysource',
@ -913,7 +1010,7 @@ def test_autodoc_member_order(app):
' .. py:attribute:: Class.docattr', ' .. py:attribute:: Class.docattr',
' .. py:attribute:: Class.udocattr', ' .. py:attribute:: Class.udocattr',
' .. py:attribute:: Class.mdocattr', ' .. py:attribute:: Class.mdocattr',
roger_method, ROGER_METHOD,
' .. py:classmethod:: Class.moore(a, e, f) -> happiness', ' .. py:classmethod:: Class.moore(a, e, f) -> happiness',
' .. py:attribute:: Class.inst_attr_inline', ' .. py:attribute:: Class.inst_attr_inline',
' .. py:attribute:: Class.inst_attr_comment', ' .. py:attribute:: Class.inst_attr_comment',
@ -932,7 +1029,7 @@ def test_autodoc_member_order(app):
' .. py:method:: Class.excludemeth()', ' .. py:method:: Class.excludemeth()',
' .. py:method:: Class.meth()', ' .. py:method:: Class.meth()',
' .. py:classmethod:: Class.moore(a, e, f) -> happiness', ' .. py:classmethod:: Class.moore(a, e, f) -> happiness',
roger_method, ROGER_METHOD,
' .. py:method:: Class.skipmeth()', ' .. py:method:: Class.skipmeth()',
' .. py:method:: Class.undocmeth()', ' .. py:method:: Class.undocmeth()',
' .. py:attribute:: Class._private_inst_attr', ' .. py:attribute:: Class._private_inst_attr',
@ -967,7 +1064,7 @@ def test_autodoc_member_order(app):
' .. py:method:: Class.meth()', ' .. py:method:: Class.meth()',
' .. py:classmethod:: Class.moore(a, e, f) -> happiness', ' .. py:classmethod:: Class.moore(a, e, f) -> happiness',
' .. py:attribute:: Class.prop', ' .. py:attribute:: Class.prop',
roger_method, ROGER_METHOD,
' .. py:attribute:: Class.skipattr', ' .. py:attribute:: Class.skipattr',
' .. py:method:: Class.skipmeth()', ' .. py:method:: Class.skipmeth()',
' .. py:attribute:: Class.udocattr', ' .. py:attribute:: Class.udocattr',