sphinx/tests/test_ext_autodoc.py
2020-05-14 19:41:17 +02:00

1790 lines
54 KiB
Python

"""
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-2020 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
import sys
from unittest.mock import Mock
from warnings import catch_warnings
import pytest
from docutils.statemachine import ViewList
from sphinx import addnodes
from sphinx.ext.autodoc import ModuleLevelDocumenter, ALL, Options
from sphinx.ext.autodoc.directive import DocumenterBridge, process_documenter_options
from sphinx.testing.util import SphinxTestApp, Struct # NOQA
from sphinx.util.docutils import LoggingReporter
try:
# Enable pyximport to test cython module
import pyximport
pyximport.install()
except ImportError:
pyximport = None
def do_autodoc(app, objtype, name, options=None):
if options is None:
options = {}
app.env.temp_data.setdefault('docname', 'index') # set dummy docname
doccls = app.registry.documenters[objtype]
docoptions = process_documenter_options(doccls, app.config, options)
state = Mock()
state.document.settings.tab_width = 8
bridge = DocumenterBridge(app.env, LoggingReporter(''), docoptions, 1, state)
documenter = doccls(bridge, name)
documenter.generate()
return bridge.result
def make_directive_bridge(env):
options = Options(
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(),
ignore_module_all = False,
)
directive = Struct(
env = env,
genopt = options,
result = ViewList(),
filename_set = set(),
state = Mock(),
)
directive.state.document.settings.tab_width = 8
return directive
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
def test_parse_name(app):
def verify(objtype, name, result):
inst = app.registry.documenters[objtype](directive, name)
assert inst.parse_name()
assert (inst.modname, inst.objpath, inst.args, inst.retann) == result
directive = make_directive_bridge(app.env)
# for modules
verify('module', 'test_ext_autodoc', ('test_ext_autodoc', [], None, None))
verify('module', 'test.test_ext_autodoc', ('test.test_ext_autodoc', [], None, None))
verify('module', 'test(arg)', ('test', [], 'arg', None))
assert 'signature arguments' in app._warning.getvalue()
# for functions/classes
verify('function', 'test_ext_autodoc.raises',
('test_ext_autodoc', ['raises'], None, None))
verify('function', 'test_ext_autodoc.raises(exc) -> None',
('test_ext_autodoc', ['raises'], 'exc', 'None'))
directive.env.temp_data['autodoc:module'] = 'test_ext_autodoc'
verify('function', 'raises', ('test_ext_autodoc', ['raises'], None, None))
del directive.env.temp_data['autodoc:module']
directive.env.ref_context['py:module'] = 'test_ext_autodoc'
verify('function', 'raises', ('test_ext_autodoc', ['raises'], None, None))
verify('class', 'Base', ('test_ext_autodoc', ['Base'], None, None))
# for members
directive.env.ref_context['py:module'] = 'foo'
verify('method', 'util.SphinxTestApp.cleanup',
('foo', ['util', 'SphinxTestApp', 'cleanup'], None, None))
directive.env.ref_context['py:module'] = 'util'
directive.env.ref_context['py:class'] = 'Foo'
directive.env.temp_data['autodoc:class'] = 'SphinxTestApp'
verify('method', 'cleanup', ('util', ['SphinxTestApp', 'cleanup'], None, None))
verify('method', 'SphinxTestApp.cleanup',
('util', ['SphinxTestApp', 'cleanup'], None, None))
def test_format_signature(app):
app.connect('autodoc-process-signature', process_signature)
app.connect('autodoc-skip-member', skip_member)
directive = make_directive_bridge(app.env)
def formatsig(objtype, name, obj, args, retann):
inst = app.registry.documenters[objtype](directive, name)
inst.fullname = name
inst.doc_as_attr = False # for class objtype
inst.object = obj
inst.objpath = [name]
inst.args = args
inst.retann = retann
res = inst.format_signature()
print(res)
return res
# no signatures for modules
assert formatsig('module', 'test', None, None, None) == ''
# test for functions
def f(a, b, c=1, **d):
pass
def g(a='\n'):
pass
assert formatsig('function', 'f', f, None, None) == '(a, b, c=1, **d)'
assert formatsig('function', 'f', f, 'a, b, c, d', None) == '(a, b, c, d)'
assert formatsig('function', 'f', f, None, 'None') == '(a, b, c=1, **d) -> None'
assert formatsig('function', 'g', g, None, None) == r"(a='\n')"
# test for classes
class D:
pass
class E:
pass
# no signature for classes without __init__
for C in (D, E):
assert formatsig('class', 'D', C, None, None) == ''
class F:
def __init__(self, a, b=None):
pass
class G(F):
pass
for C in (F, G):
assert formatsig('class', 'C', C, None, None) == '(a, b=None)'
assert formatsig('class', 'C', D, 'a, b', 'X') == '(a, b) -> X'
# __init__ have signature at first line of docstring
directive.env.config.autoclass_content = 'both'
class F2:
'''some docstring for F2.'''
def __init__(self, *args, **kw):
'''
__init__(a1, a2, kw1=True, kw2=False)
some docstring for __init__.
'''
class G2(F2):
pass
assert formatsig('class', 'F2', F2, None, None) == \
'(a1, a2, kw1=True, kw2=False)'
assert formatsig('class', 'G2', G2, None, None) == \
'(a1, a2, kw1=True, kw2=False)'
# test for methods
class H:
def foo1(self, b, *c):
pass
def foo2(b, *c):
pass
def foo3(self, d='\n'):
pass
assert formatsig('method', 'H.foo', H.foo1, None, None) == '(b, *c)'
assert formatsig('method', 'H.foo', H.foo1, 'a', None) == '(a)'
assert formatsig('method', 'H.foo', H.foo2, None, None) == '(*c)'
assert formatsig('method', 'H.foo', H.foo3, None, None) == r"(d='\n')"
# test bound methods interpreted as functions
assert formatsig('function', 'foo', H().foo1, None, None) == '(b, *c)'
assert formatsig('function', 'foo', H().foo2, None, None) == '(*c)'
assert formatsig('function', 'foo', H().foo3, None, None) == r"(d='\n')"
# test exception handling (exception is caught and args is '')
directive.env.config.autodoc_docstring_signature = False
assert formatsig('function', 'int', int, None, None) == ''
# test processing by event handler
assert formatsig('method', 'bar', H.foo1, None, None) == '42'
# test functions created via functools.partial
from functools import partial
curried1 = partial(lambda a, b, c: None, 'A')
assert formatsig('function', 'curried1', curried1, None, None) == \
'(b, c)'
curried2 = partial(lambda a, b, c=42: None, 'A')
assert formatsig('function', 'curried2', curried2, None, None) == \
'(b, c=42)'
curried3 = partial(lambda a, b, *c: None, 'A')
assert formatsig('function', 'curried3', curried3, None, None) == \
'(b, *c)'
curried4 = partial(lambda a, b, c=42, *d, **e: None, 'A')
assert formatsig('function', 'curried4', curried4, None, None) == \
'(b, c=42, *d, **e)'
def test_get_doc(app):
directive = make_directive_bridge(app.env)
def getdocl(objtype, obj):
inst = app.registry.documenters[objtype](directive, 'tmp')
inst.object = obj
inst.objpath = [obj.__name__]
inst.doc_as_attr = False
inst.format_signature() # handle docstring signatures!
ds = inst.get_doc()
# for testing purposes, concat them and strip the empty line at the end
res = sum(ds, [])[:-1]
print(res)
return res
# objects without docstring
def f():
pass
assert getdocl('function', f) == []
# standard function, diverse docstring styles...
def f():
"""Docstring"""
def g():
"""
Docstring
"""
for func in (f, g):
assert getdocl('function', func) == ['Docstring']
# first line vs. other lines indentation
def f():
"""First line
Other
lines
"""
assert getdocl('function', f) == ['First line', '', 'Other', ' lines']
# charset guessing (this module is encoded in utf-8)
def f():
"""Döcstring"""
assert getdocl('function', f) == ['Döcstring']
# already-unicode docstrings must be taken literally
def f():
"""Döcstring"""
assert getdocl('function', f) == ['Döcstring']
# verify that method docstrings get extracted in both normal case
# and in case of bound method posing as a function
class J: # NOQA
def foo(self):
"""Method docstring"""
assert getdocl('method', J.foo) == ['Method docstring']
assert getdocl('function', J().foo) == ['Method docstring']
@pytest.mark.sphinx('html', testroot='ext-autodoc')
def test_new_documenter(app):
class MyDocumenter(ModuleLevelDocumenter):
objtype = 'integer'
directivetype = 'integer'
priority = 100
@classmethod
def can_document_member(cls, member, membername, isattr, parent):
return isinstance(member, int)
def document_members(self, all_members=False):
return
app.add_autodocumenter(MyDocumenter)
options = {"members": 'integer'}
actual = do_autodoc(app, 'module', 'target', options)
assert list(actual) == [
'',
'.. py:module:: target',
'',
'',
'.. py:integer:: integer',
' :module: target',
'',
' documentation for the integer',
'',
]
def test_attrgetter_using(app):
from target import Class
from target.inheritance import Derived
directive = make_directive_bridge(app.env)
def assert_getter_works(objtype, name, obj, attrs=[], **kw):
getattr_spy = []
def special_getattr(obj, name, *defargs):
if name in attrs:
getattr_spy.append((obj, name))
return None
return getattr(obj, name, *defargs)
app.add_autodoc_attrgetter(type, special_getattr)
del getattr_spy[:]
inst = app.registry.documenters[objtype](directive, name)
inst.generate(**kw)
hooked_members = [s[1] for s in getattr_spy]
documented_members = [s[1] for s in processed_signatures]
for attr in attrs:
fullname = '.'.join((name, attr))
assert attr in hooked_members
assert fullname not in documented_members, \
'%r was not hooked by special_attrgetter function' % fullname
with catch_warnings(record=True):
directive.genopt['members'] = ALL
directive.genopt['inherited_members'] = False
print(directive.genopt)
assert_getter_works('class', 'target.Class', Class, ['meth'])
directive.genopt['inherited_members'] = True
assert_getter_works('class', 'target.inheritance.Derived', Derived, ['inheritedmeth'])
@pytest.mark.sphinx('html', testroot='ext-autodoc')
def test_py_module(app, warning):
# without py:module
actual = do_autodoc(app, 'method', 'Class.meth')
assert list(actual) == []
assert ("don't know which module to import for autodocumenting 'Class.meth'"
in warning.getvalue())
# with py:module
app.env.ref_context['py:module'] = 'target'
warning.truncate(0)
actual = do_autodoc(app, 'method', 'Class.meth')
assert list(actual) == [
'',
'.. py:method:: Class.meth()',
' :module: target',
'',
' Function.',
'',
]
assert ("don't know which module to import for autodocumenting 'Class.meth'"
not in warning.getvalue())
@pytest.mark.sphinx('html', testroot='ext-autodoc')
def test_autodoc_decorator(app):
actual = do_autodoc(app, 'decorator', 'target.decorator.deco1')
assert list(actual) == [
'',
'.. py:decorator:: deco1',
' :module: target.decorator',
'',
' docstring for deco1',
'',
]
actual = do_autodoc(app, 'decorator', 'target.decorator.deco2')
assert list(actual) == [
'',
'.. py:decorator:: deco2(condition, message)',
' :module: target.decorator',
'',
' docstring for deco2',
'',
]
@pytest.mark.sphinx('html', testroot='ext-autodoc')
def test_autodoc_exception(app):
actual = do_autodoc(app, 'exception', 'target.CustomEx')
assert list(actual) == [
'',
'.. py:exception:: CustomEx',
' :module: target',
'',
' My custom exception.',
'',
]
@pytest.mark.sphinx('html', testroot='ext-autodoc')
def test_autodoc_warnings(app, warning):
app.env.temp_data['docname'] = 'dummy'
# can't import module
do_autodoc(app, 'module', 'unknown')
assert "failed to import module 'unknown'" in warning.getvalue()
# missing function
do_autodoc(app, 'function', 'unknown')
assert "import for autodocumenting 'unknown'" in warning.getvalue()
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.inheritance.Base')
assert list(filter(lambda l: '::' in l, actual)) == [
'.. py:class:: Base',
]
# default ALL-members
options = {"members": None}
actual = do_autodoc(app, 'class', 'target.inheritance.Base', options)
assert list(filter(lambda l: '::' in l, actual)) == [
'.. py:class:: Base',
' .. py:method:: Base.inheritedclassmeth()',
' .. py:method:: Base.inheritedmeth()',
' .. py:method:: Base.inheritedstaticmeth(cls)'
]
# default specific-members
options = {"members": "inheritedmeth,inheritedstaticmeth"}
actual = do_autodoc(app, 'class', 'target.inheritance.Base', options)
assert list(filter(lambda l: '::' in l, actual)) == [
'.. py:class:: Base',
' .. py:method:: Base.inheritedmeth()',
' .. py:method:: 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.inheritance.Base', options)
assert list(filter(lambda l: '::' in l, actual)) == [
'.. py:class:: Base',
' .. py:method:: Base.inheritedclassmeth()'
]
# members vs exclude-members
options = {"members": "inheritedmeth",
"exclude-members": "inheritedmeth"}
actual = do_autodoc(app, 'class', 'target.inheritance.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.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:method:: Class.moore(a, e, f) -> happiness',
' .. py:method:: Class.roger(a, *, b=2, c=3, d=4, e=5, f=6)',
' .. 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.inheritance.Derived', options)
assert list(filter(lambda l: 'method::' in l, actual)) == [
' .. py:method:: Derived.inheritedclassmeth()',
' .. py:method:: Derived.inheritedmeth()',
' .. py:method:: Derived.inheritedstaticmeth(cls)',
]
@pytest.mark.sphinx('html', testroot='ext-autodoc')
def test_autodoc_inherited_members_Base(app):
options = {"members": None,
"inherited-members": "Base",
"special-members": None}
# check methods for object class are shown
actual = do_autodoc(app, 'class', 'target.inheritance.Derived', options)
assert ' .. py:method:: Derived.inheritedmeth()' in actual
assert ' .. py:method:: Derived.inheritedclassmeth' not in actual
@pytest.mark.sphinx('html', testroot='ext-autodoc')
def test_autodoc_inherited_members_None(app):
options = {"members": None,
"inherited-members": "None",
"special-members": None}
# check methods for object class are shown
actual = do_autodoc(app, 'class', 'target.inheritance.Derived', options)
assert ' .. py:method:: Derived.__init__()' in actual
assert ' .. py:method:: Derived.__str__()' in actual
@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:: save_traceback(app: Sphinx) -> str' in actual
@pytest.mark.sphinx('html', testroot='ext-autodoc')
def test_autodoc_special_members(app):
# specific special methods
options = {"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__()',
]
# combination with specific members
options = {"members": "attr,docattr",
"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.docattr',
]
# 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:attribute:: Class.__dict__',
' .. py:method:: Class.__init__(arg)',
' .. py:attribute:: Class.__module__',
' .. py:method:: Class.__special1__()',
' .. py:method:: Class.__special2__()',
' .. py:attribute:: Class.__weakref__',
' .. py:attribute:: Class.attr',
' .. 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:method:: Class.moore(a, e, f) -> happiness',
' .. py:method:: Class.roger(a, *, b=2, c=3, d=4, e=5, f=6)',
' .. 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:: CustomDict',
'.. py:class:: InnerChild',
'.. py:class:: InstAttCls()',
'.. py:class:: Outer',
' .. py:class:: Outer.Inner',
'.. py:class:: StrRepr'
]
@pytest.mark.sphinx('html', testroot='ext-autodoc')
def test_autodoc_noindex(app):
options = {"noindex": True}
actual = do_autodoc(app, 'module', 'target', options)
assert list(actual) == [
'',
'.. py:module:: target',
' :noindex:',
''
]
# TODO: :noindex: should be propagated to children of target item.
actual = do_autodoc(app, 'class', 'target.inheritance.Base', options)
assert list(actual) == [
'',
'.. py:class:: Base',
' :noindex:',
' :module: target.inheritance',
''
]
@pytest.mark.sphinx('html', testroot='ext-autodoc')
def test_autodoc_subclass_of_builtin_class(app):
options = {"members": None}
actual = do_autodoc(app, 'class', 'target.CustomDict', options)
assert list(actual) == [
'',
'.. py:class:: CustomDict',
' :module: target',
'',
' Docstring.',
'',
]
@pytest.mark.sphinx('html', testroot='ext-autodoc')
def test_autodoc_inner_class(app):
options = {"members": None}
actual = do_autodoc(app, 'class', 'target.Outer', options)
assert list(actual) == [
'',
'.. py:class:: Outer',
' :module: target',
'',
' Foo',
'',
'',
' .. py:class:: Outer.Inner',
' :module: target',
'',
' Foo',
'',
'',
' .. py:method:: Outer.Inner.meth()',
' :module: target',
'',
' Foo',
'',
'',
' .. py:attribute:: Outer.factory',
' :module: target',
'',
' alias of :class:`builtins.dict`'
]
actual = do_autodoc(app, 'class', 'target.Outer.Inner', options)
assert list(actual) == [
'',
'.. py:class:: Outer.Inner',
' :module: target',
'',
' Foo',
'',
'',
' .. py:method:: Outer.Inner.meth()',
' :module: target',
'',
' Foo',
'',
]
options['show-inheritance'] = True
actual = do_autodoc(app, 'class', 'target.InnerChild', options)
assert list(actual) == [
'',
'.. py:class:: InnerChild',
' :module: target', '',
' Bases: :class:`target.Outer.Inner`',
'',
' InnerChild docstring',
'',
]
@pytest.mark.sphinx('html', testroot='ext-autodoc')
def test_autodoc_classmethod(app):
actual = do_autodoc(app, 'method', 'target.inheritance.Base.inheritedclassmeth')
assert list(actual) == [
'',
'.. py:method:: Base.inheritedclassmeth()',
' :module: target.inheritance',
' :classmethod:',
'',
' Inherited class method.',
'',
]
@pytest.mark.sphinx('html', testroot='ext-autodoc')
def test_autodoc_staticmethod(app):
actual = do_autodoc(app, 'method', 'target.inheritance.Base.inheritedstaticmeth')
assert list(actual) == [
'',
'.. py:method:: Base.inheritedstaticmeth(cls)',
' :module: target.inheritance',
' :staticmethod:',
'',
' Inherited static method.',
'',
]
@pytest.mark.sphinx('html', testroot='ext-autodoc')
def test_autodoc_descriptor(app):
options = {"members": None,
"undoc-members": True}
actual = do_autodoc(app, 'class', 'target.descriptor.Class', options)
assert list(actual) == [
'',
'.. py:class:: Class',
' :module: target.descriptor',
'',
'',
' .. py:attribute:: Class.descr',
' :module: target.descriptor',
'',
' Descriptor instance docstring.',
'',
'',
' .. py:method:: Class.prop',
' :module: target.descriptor',
' :property:',
'',
' Property.',
''
]
@pytest.mark.sphinx('html', testroot='ext-autodoc')
def test_autodoc_c_module(app):
actual = do_autodoc(app, 'function', 'time.asctime')
assert list(actual) == [
'',
'.. py:function:: asctime([tuple]) -> string',
' :module: time',
'',
" Convert a time tuple to a string, e.g. 'Sat Jun 06 16:26:11 1998'.",
' When the time tuple is not present, current time as returned by localtime()',
' is used.',
'',
]
@pytest.mark.sphinx('html', testroot='ext-autodoc')
def test_autodoc_member_order(app):
# case member-order='bysource'
options = {"members": None,
'member-order': 'bysource',
"undoc-members": True,
'private-members': True}
actual = do_autodoc(app, 'class', 'target.Class', options)
assert list(filter(lambda l: '::' in l, actual)) == [
'.. py:class:: Class(arg)',
' .. py:method:: Class.meth()',
' .. py:method:: Class.undocmeth()',
' .. py:method:: Class.skipmeth()',
' .. py:method:: Class.excludemeth()',
' .. py:attribute:: Class.skipattr',
' .. py:attribute:: Class.attr',
' .. py:attribute:: Class.docattr',
' .. py:attribute:: Class.udocattr',
' .. py:attribute:: Class.mdocattr',
' .. py:method:: Class.roger(a, *, b=2, c=3, d=4, e=5, f=6)',
' .. py:method:: Class.moore(a, e, f) -> happiness',
' .. py:attribute:: Class.inst_attr_inline',
' .. py:attribute:: Class.inst_attr_comment',
' .. py:attribute:: Class.inst_attr_string',
' .. py:attribute:: Class._private_inst_attr'
]
# case member-order='groupwise'
options = {"members": None,
'member-order': 'groupwise',
"undoc-members": True,
'private-members': True}
actual = do_autodoc(app, 'class', 'target.Class', options)
assert list(filter(lambda l: '::' in l, actual)) == [
'.. py:class:: Class(arg)',
' .. py:method:: Class.excludemeth()',
' .. py:method:: Class.meth()',
' .. py:method:: Class.moore(a, e, f) -> happiness',
' .. py:method:: Class.roger(a, *, b=2, c=3, d=4, e=5, f=6)',
' .. py:method:: Class.skipmeth()',
' .. py:method:: Class.undocmeth()',
' .. py:attribute:: Class._private_inst_attr',
' .. py:attribute:: Class.attr',
' .. py:attribute:: Class.docattr',
' .. py:attribute:: Class.inst_attr_comment',
' .. py:attribute:: Class.inst_attr_inline',
' .. py:attribute:: Class.inst_attr_string',
' .. py:attribute:: Class.mdocattr',
' .. py:attribute:: Class.skipattr',
' .. py:attribute:: Class.udocattr'
]
# case member-order=None
options = {"members": None,
"undoc-members": True,
'private-members': True}
actual = do_autodoc(app, 'class', 'target.Class', options)
assert list(filter(lambda l: '::' in l, actual)) == [
'.. py:class:: Class(arg)',
' .. py:attribute:: Class._private_inst_attr',
' .. py:attribute:: Class.attr',
' .. 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:method:: Class.moore(a, e, f) -> happiness',
' .. py:method:: Class.roger(a, *, b=2, c=3, d=4, e=5, f=6)',
' .. 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_module_scope(app):
app.env.temp_data['autodoc:module'] = 'target'
actual = do_autodoc(app, 'attribute', 'Class.mdocattr')
assert list(actual) == [
'',
'.. py:attribute:: Class.mdocattr',
' :module: target',
' :value: <_io.StringIO object>',
'',
' should be documented as well - süß',
'',
]
@pytest.mark.sphinx('html', testroot='ext-autodoc')
def test_autodoc_class_scope(app):
app.env.temp_data['autodoc:module'] = 'target'
app.env.temp_data['autodoc:class'] = 'Class'
actual = do_autodoc(app, 'attribute', 'mdocattr')
assert list(actual) == [
'',
'.. py:attribute:: Class.mdocattr',
' :module: target',
' :value: <_io.StringIO object>',
'',
' should be documented as well - süß',
'',
]
@pytest.mark.sphinx('html', testroot='ext-autodoc')
def test_class_attributes(app):
options = {"members": None,
"undoc-members": True}
actual = do_autodoc(app, 'class', 'target.AttCls', options)
assert list(actual) == [
'',
'.. py:class:: AttCls',
' :module: target',
'',
'',
' .. py:attribute:: AttCls.a1',
' :module: target',
' :value: hello world',
'',
'',
' .. py:attribute:: AttCls.a2',
' :module: target',
' :value: None',
''
]
@pytest.mark.sphinx('html', testroot='ext-autodoc')
def test_instance_attributes(app):
options = {"members": None}
actual = do_autodoc(app, 'class', 'target.InstAttCls', options)
assert list(actual) == [
'',
'.. py:class:: InstAttCls()',
' :module: target',
'',
' Class with documented class and instance attributes.',
'',
'',
' .. py:attribute:: InstAttCls.ca1',
' :module: target',
" :value: 'a'",
'',
' Doc comment for class attribute InstAttCls.ca1.',
' It can have multiple lines.',
'',
'',
' .. py:attribute:: InstAttCls.ca2',
' :module: target',
" :value: 'b'",
'',
' Doc comment for InstAttCls.ca2. One line only.',
'',
'',
' .. py:attribute:: InstAttCls.ca3',
' :module: target',
" :value: 'c'",
'',
' Docstring for class attribute InstAttCls.ca3.',
'',
'',
' .. py:attribute:: InstAttCls.ia1',
' :module: target',
'',
' Doc comment for instance attribute InstAttCls.ia1',
'',
'',
' .. py:attribute:: InstAttCls.ia2',
' :module: target',
'',
' Docstring for instance attribute InstAttCls.ia2.',
''
]
# pick up arbitrary attributes
options = {"members": 'ca1,ia1'}
actual = do_autodoc(app, 'class', 'target.InstAttCls', options)
assert list(actual) == [
'',
'.. py:class:: InstAttCls()',
' :module: target',
'',
' Class with documented class and instance attributes.',
'',
'',
' .. py:attribute:: InstAttCls.ca1',
' :module: target',
" :value: 'a'",
'',
' Doc comment for class attribute InstAttCls.ca1.',
' It can have multiple lines.',
'',
'',
' .. py:attribute:: InstAttCls.ia1',
' :module: target',
'',
' Doc comment for instance attribute InstAttCls.ia1',
''
]
@pytest.mark.sphinx('html', testroot='ext-autodoc')
def test_slots(app):
options = {"members": None,
"undoc-members": True}
actual = do_autodoc(app, 'module', 'target.slots', options)
assert list(actual) == [
'',
'.. py:module:: target.slots',
'',
'',
'.. py:class:: Bar()',
' :module: target.slots',
'',
'',
' .. py:attribute:: Bar.attr1',
' :module: target.slots',
'',
' docstring of attr1',
'',
'',
' .. py:attribute:: Bar.attr2',
' :module: target.slots',
'',
' docstring of instance attr2',
'',
'',
' .. py:attribute:: Bar.attr3',
' :module: target.slots',
'',
'',
'.. py:class:: Foo',
' :module: target.slots',
'',
'',
' .. py:attribute:: Foo.attr',
' :module: target.slots',
'',
]
@pytest.mark.sphinx('html', testroot='ext-autodoc')
def test_enum_class(app):
options = {"members": None}
actual = do_autodoc(app, 'class', 'target.enum.EnumCls', options)
assert list(actual) == [
'',
'.. py:class:: EnumCls',
' :module: target.enum',
'',
' this is enum class',
'',
'',
' .. py:method:: EnumCls.say_goodbye()',
' :module: target.enum',
' :classmethod:',
'',
' a classmethod says good-bye to you.',
'',
'',
' .. py:method:: EnumCls.say_hello()',
' :module: target.enum',
'',
' a method says hello to you.',
'',
'',
' .. py:attribute:: EnumCls.val1',
' :module: target.enum',
' :value: 12',
'',
' doc for val1',
'',
'',
' .. py:attribute:: EnumCls.val2',
' :module: target.enum',
' :value: 23',
'',
' doc for val2',
'',
'',
' .. py:attribute:: EnumCls.val3',
' :module: target.enum',
' :value: 34',
'',
' doc for val3',
'',
]
# checks for an attribute of EnumClass
actual = do_autodoc(app, 'attribute', 'target.enum.EnumCls.val1')
assert list(actual) == [
'',
'.. py:attribute:: EnumCls.val1',
' :module: target.enum',
' :value: 12',
'',
' doc for val1',
''
]
@pytest.mark.sphinx('html', testroot='ext-autodoc')
def test_descriptor_class(app):
options = {"members": 'CustomDataDescriptor,CustomDataDescriptor2'}
actual = do_autodoc(app, 'module', 'target.descriptor', options)
assert list(actual) == [
'',
'.. py:module:: target.descriptor',
'',
'',
'.. py:class:: CustomDataDescriptor(doc)',
' :module: target.descriptor',
'',
' Descriptor class docstring.',
'',
'',
' .. py:method:: CustomDataDescriptor.meth()',
' :module: target.descriptor',
'',
' Function.',
'',
'',
'.. py:class:: CustomDataDescriptor2(doc)',
' :module: target.descriptor',
'',
' Descriptor class with custom metaclass docstring.',
'',
]
@pytest.mark.sphinx('html', testroot='ext-autodoc')
def test_autofunction_for_classes(app):
actual = do_autodoc(app, 'function', 'target.classes.Foo')
assert list(actual) == [
'',
'.. py:function:: Foo()',
' :module: target.classes',
'',
]
actual = do_autodoc(app, 'function', 'target.classes.Bar')
assert list(actual) == [
'',
'.. py:function:: Bar(x, y)',
' :module: target.classes',
'',
]
actual = do_autodoc(app, 'function', 'target.classes.Baz')
assert list(actual) == [
'',
'.. py:function:: Baz(x, y)',
' :module: target.classes',
'',
]
@pytest.mark.sphinx('html', testroot='ext-autodoc')
def test_autofunction_for_callable(app):
actual = do_autodoc(app, 'function', 'target.callable.function')
assert list(actual) == [
'',
'.. py:function:: function(arg1, arg2, **kwargs)',
' :module: target.callable',
'',
' A callable object that behaves like a function.',
'',
]
@pytest.mark.sphinx('html', testroot='ext-autodoc')
def test_autofunction_for_method(app):
actual = do_autodoc(app, 'function', 'target.callable.method')
assert list(actual) == [
'',
'.. py:function:: method(arg1, arg2)',
' :module: target.callable',
'',
' docstring of Callable.method().',
'',
]
@pytest.mark.sphinx('html', testroot='ext-autodoc')
def test_autofunction_for_builtin(app):
actual = do_autodoc(app, 'function', 'os.umask')
assert list(actual) == [
'',
'.. py:function:: umask(mask, /)',
' :module: os',
'',
' Set the current numeric umask and return the previous umask.',
'',
]
@pytest.mark.sphinx('html', testroot='ext-autodoc')
def test_autofunction_for_methoddescriptor(app):
actual = do_autodoc(app, 'function', 'builtins.int.__add__')
assert list(actual) == [
'',
'.. py:function:: int.__add__(self, value, /)',
' :module: builtins',
'',
' Return self+value.',
'',
]
@pytest.mark.sphinx('html', testroot='ext-autodoc')
def test_automethod_for_builtin(app):
actual = do_autodoc(app, 'method', 'builtins.int.__add__')
assert list(actual) == [
'',
'.. py:method:: int.__add__(value, /)',
' :module: builtins',
'',
' Return self+value.',
'',
]
@pytest.mark.sphinx('html', testroot='ext-autodoc')
def test_abstractmethods(app):
options = {"members": None,
"undoc-members": None}
actual = do_autodoc(app, 'module', 'target.abstractmethods', options)
assert list(actual) == [
'',
'.. py:module:: target.abstractmethods',
'',
'',
'.. py:class:: Base',
' :module: target.abstractmethods',
'',
'',
' .. py:method:: Base.abstractmeth()',
' :module: target.abstractmethods',
' :abstractmethod:',
'',
'',
' .. py:method:: Base.classmeth()',
' :module: target.abstractmethods',
' :abstractmethod:',
' :classmethod:',
'',
'',
' .. py:method:: Base.coroutinemeth()',
' :module: target.abstractmethods',
' :abstractmethod:',
' :async:',
'',
'',
' .. py:method:: Base.meth()',
' :module: target.abstractmethods',
'',
'',
' .. py:method:: Base.prop',
' :module: target.abstractmethods',
' :abstractmethod:',
' :property:',
'',
'',
' .. py:method:: Base.staticmeth()',
' :module: target.abstractmethods',
' :abstractmethod:',
' :staticmethod:',
'',
]
@pytest.mark.sphinx('html', testroot='ext-autodoc')
def test_partialfunction(app):
options = {"members": None}
actual = do_autodoc(app, 'module', 'target.partialfunction', options)
assert list(actual) == [
'',
'.. py:module:: target.partialfunction',
'',
'',
'.. py:function:: func1(a, b, c)',
' :module: target.partialfunction',
'',
' docstring of func1',
'',
'',
'.. py:function:: func2(b, c)',
' :module: target.partialfunction',
'',
' docstring of func1',
'',
'',
'.. py:function:: func3(c)',
' :module: target.partialfunction',
'',
' docstring of func3',
'',
'',
'.. py:function:: func4()',
' :module: target.partialfunction',
'',
' docstring of func3',
'',
]
@pytest.mark.sphinx('html', testroot='ext-autodoc')
def test_imported_partialfunction_should_not_shown_without_imported_members(app):
options = {"members": None}
actual = do_autodoc(app, 'module', 'target.imported_members', options)
assert list(actual) == [
'',
'.. py:module:: target.imported_members',
''
]
@pytest.mark.sphinx('html', testroot='ext-autodoc')
def test_bound_method(app):
options = {"members": None}
actual = do_autodoc(app, 'module', 'target.bound_method', options)
assert list(actual) == [
'',
'.. py:module:: target.bound_method',
'',
'',
'.. py:function:: bound_method()',
' :module: target.bound_method',
'',
' Method docstring',
'',
]
@pytest.mark.sphinx('html', testroot='ext-autodoc')
def test_coroutine(app):
actual = do_autodoc(app, 'function', 'target.functions.coroutinefunc')
assert list(actual) == [
'',
'.. py:function:: coroutinefunc()',
' :module: target.functions',
' :async:',
'',
]
options = {"members": None}
actual = do_autodoc(app, 'class', 'target.coroutine.AsyncClass', options)
assert list(actual) == [
'',
'.. py:class:: AsyncClass',
' :module: target.coroutine',
'',
'',
' .. py:method:: AsyncClass.do_coroutine()',
' :module: target.coroutine',
' :async:',
'',
' A documented coroutine function',
'',
'',
' .. py:method:: AsyncClass.do_coroutine2()',
' :module: target.coroutine',
' :async:',
' :classmethod:',
'',
' A documented coroutine classmethod',
'',
'',
' .. py:method:: AsyncClass.do_coroutine3()',
' :module: target.coroutine',
' :async:',
' :staticmethod:',
'',
' A documented coroutine staticmethod',
'',
]
# force-synchronized wrapper
actual = do_autodoc(app, 'function', 'target.coroutine.sync_func')
assert list(actual) == [
'',
'.. py:function:: sync_func()',
' :module: target.coroutine',
'',
]
@pytest.mark.sphinx('html', testroot='ext-autodoc')
def test_partialmethod(app):
expected = [
'',
'.. py:class:: Cell',
' :module: target.partialmethod',
'',
' An example for partialmethod.',
'',
' refs: https://docs.python.jp/3/library/functools.html#functools.partialmethod',
'',
'',
' .. py:method:: Cell.set_alive()',
' :module: target.partialmethod',
'',
' Make a cell alive.',
'',
'',
' .. py:method:: Cell.set_state(state)',
' :module: target.partialmethod',
'',
' Update state of cell to *state*.',
'',
]
options = {"members": None}
actual = do_autodoc(app, 'class', 'target.partialmethod.Cell', options)
assert list(actual) == expected
@pytest.mark.sphinx('html', testroot='ext-autodoc')
def test_wrappedfunction(app):
actual = do_autodoc(app, 'function', 'target.wrappedfunction.slow_function')
assert list(actual) == [
'',
'.. py:function:: slow_function(message, timeout)',
' :module: target.wrappedfunction',
'',
' This function is slow.',
'',
]
@pytest.mark.sphinx('html', testroot='ext-autodoc')
def test_partialmethod_undoc_members(app):
expected = [
'',
'.. py:class:: Cell',
' :module: target.partialmethod',
'',
' An example for partialmethod.',
'',
' refs: https://docs.python.jp/3/library/functools.html#functools.partialmethod',
'',
'',
' .. py:method:: Cell.set_alive()',
' :module: target.partialmethod',
'',
' Make a cell alive.',
'',
'',
' .. py:method:: Cell.set_dead()',
' :module: target.partialmethod',
'',
'',
' .. py:method:: Cell.set_state(state)',
' :module: target.partialmethod',
'',
' Update state of cell to *state*.',
'',
]
options = {"members": None,
"undoc-members": None}
actual = do_autodoc(app, 'class', 'target.partialmethod.Cell', options)
assert list(actual) == expected
@pytest.mark.skipif(sys.version_info < (3, 6), reason='py36+ is available since python3.6.')
@pytest.mark.sphinx('html', testroot='ext-autodoc')
def test_autodoc_typed_instance_variables(app):
options = {"members": None,
"undoc-members": True}
actual = do_autodoc(app, 'module', 'target.typed_vars', options)
assert list(actual) == [
'',
'.. py:module:: target.typed_vars',
'',
'',
'.. py:class:: Class()',
' :module: target.typed_vars',
'',
'',
' .. py:attribute:: Class.attr1',
' :module: target.typed_vars',
' :type: int',
' :value: 0',
'',
'',
' .. py:attribute:: Class.attr2',
' :module: target.typed_vars',
' :type: int',
'',
'',
' .. py:attribute:: Class.attr3',
' :module: target.typed_vars',
' :type: int',
' :value: 0',
'',
'',
' .. py:attribute:: Class.attr4',
' :module: target.typed_vars',
' :type: int',
'',
' attr4',
'',
'',
' .. py:attribute:: Class.attr5',
' :module: target.typed_vars',
' :type: int',
'',
' attr5',
'',
'',
' .. py:attribute:: Class.attr6',
' :module: target.typed_vars',
' :type: int',
'',
' attr6',
'',
'',
' .. py:attribute:: Class.descr4',
' :module: target.typed_vars',
' :type: int',
'',
' This is descr4',
'',
'',
'.. py:data:: attr1',
' :module: target.typed_vars',
' :type: str',
" :value: ''",
'',
' attr1',
'',
'',
'.. py:data:: attr2',
' :module: target.typed_vars',
' :type: str',
'',
' attr2',
'',
'',
'.. py:data:: attr3',
' :module: target.typed_vars',
' :type: str',
" :value: ''",
'',
' attr3',
'',
]
@pytest.mark.skipif(sys.version_info < (3, 9), reason='py39+ is required.')
@pytest.mark.sphinx('html', testroot='ext-autodoc')
def test_autodoc_Annotated(app):
options = {"members": None}
actual = do_autodoc(app, 'module', 'target.annotated', options)
assert list(actual) == [
'',
'.. py:module:: target.annotated',
'',
'',
'.. py:function:: hello(name: str) -> None',
' :module: target.annotated',
'',
' docstring',
'',
]
@pytest.mark.sphinx('html', testroot='pycode-egg')
def test_autodoc_for_egged_code(app):
options = {"members": None,
"undoc-members": None}
actual = do_autodoc(app, 'module', 'sample', options)
assert list(actual) == [
'',
'.. py:module:: sample',
'',
'',
'.. py:data:: CONSTANT',
' :module: sample',
' :value: 1',
'',
' constant on sample.py',
'',
'',
'.. py:function:: hello(s)',
' :module: sample',
''
]
@pytest.mark.sphinx('html', testroot='ext-autodoc')
def test_singledispatch(app):
options = {"members": None}
actual = do_autodoc(app, 'module', 'target.singledispatch', options)
assert list(actual) == [
'',
'.. py:module:: target.singledispatch',
'',
'',
'.. py:function:: func(arg, kwarg=None)',
' func(arg: int, kwarg=None)',
' func(arg: str, kwarg=None)',
' :module: target.singledispatch',
'',
' A function for general use.',
'',
]
@pytest.mark.sphinx('html', testroot='ext-autodoc')
def test_singledispatch_autofunction(app):
options = {}
actual = do_autodoc(app, 'function', 'target.singledispatch.func', options)
assert list(actual) == [
'',
'.. py:function:: func(arg, kwarg=None)',
' func(arg: int, kwarg=None)',
' func(arg: str, kwarg=None)',
' :module: target.singledispatch',
'',
' A function for general use.',
'',
]
@pytest.mark.skipif(sys.version_info < (3, 8),
reason='singledispatchmethod is available since python3.8')
@pytest.mark.sphinx('html', testroot='ext-autodoc')
def test_singledispatchmethod(app):
options = {"members": None}
actual = do_autodoc(app, 'module', 'target.singledispatchmethod', options)
assert list(actual) == [
'',
'.. py:module:: target.singledispatchmethod',
'',
'',
'.. py:class:: Foo',
' :module: target.singledispatchmethod',
'',
' docstring',
'',
'',
' .. py:method:: Foo.meth(arg, kwarg=None)',
' Foo.meth(arg: int, kwarg=None)',
' Foo.meth(arg: str, kwarg=None)',
' :module: target.singledispatchmethod',
'',
' A method for general use.',
'',
]
@pytest.mark.skipif(sys.version_info < (3, 8),
reason='singledispatchmethod is available since python3.8')
@pytest.mark.sphinx('html', testroot='ext-autodoc')
def test_singledispatchmethod_automethod(app):
options = {}
actual = do_autodoc(app, 'method', 'target.singledispatchmethod.Foo.meth', options)
assert list(actual) == [
'',
'.. py:method:: Foo.meth(arg, kwarg=None)',
' Foo.meth(arg: int, kwarg=None)',
' Foo.meth(arg: str, kwarg=None)',
' :module: target.singledispatchmethod',
'',
' A method for general use.',
'',
]
@pytest.mark.skipif(pyximport is None, reason='cython is not installed')
@pytest.mark.sphinx('html', testroot='ext-autodoc')
def test_cython(app):
options = {"members": None,
"undoc-members": None}
actual = do_autodoc(app, 'module', 'target.cython', options)
assert list(actual) == [
'',
'.. py:module:: target.cython',
'',
'',
'.. py:class:: Class',
' :module: target.cython',
'',
' Docstring.',
'',
'',
' .. py:method:: Class.meth(name: str, age: int = 0) -> None',
' :module: target.cython',
'',
' Docstring.',
'',
'',
'.. py:function:: foo(x: int, *args, y: str, **kwargs)',
' :module: target.cython',
'',
' Docstring.',
'',
]
@pytest.mark.skipif(sys.version_info < (3, 8),
reason='typing.final is available since python3.8')
@pytest.mark.sphinx('html', testroot='ext-autodoc')
def test_final(app):
options = {"members": None}
actual = do_autodoc(app, 'module', 'target.final', options)
assert list(actual) == [
'',
'.. py:module:: target.final',
'',
'',
'.. py:class:: Class',
' :module: target.final',
' :final:',
'',
' docstring',
'',
'',
' .. py:method:: Class.meth1()',
' :module: target.final',
' :final:',
'',
' docstring',
'',
'',
' .. py:method:: Class.meth2()',
' :module: target.final',
'',
' docstring',
'',
]
@pytest.mark.sphinx('dummy', testroot='ext-autodoc')
def test_autodoc(app, status, warning):
app.builder.build_all()
content = app.env.get_doctree('index')
assert isinstance(content[3], addnodes.desc)
assert content[3][0].astext() == 'autodoc_dummy_module.test()'
assert content[3][1].astext() == 'Dummy function using dummy.*'
# issue sphinx-doc/sphinx#2437
assert content[11][-1].astext() == """Dummy class Bar with alias.
my_name
alias of bug2437.autodoc_dummy_foo.Foo"""
assert warning.getvalue() == ''