diff options
author | Takeshi KOMIYA <i.tkomiya@gmail.com> | 2020-05-17 19:12:56 +0900 |
---|---|---|
committer | Takeshi KOMIYA <i.tkomiya@gmail.com> | 2020-05-17 19:12:56 +0900 |
commit | 3c7d35d2a20cfa7c915538704680a33db87c0563 (patch) | |
tree | 5c8e1c9e894f90bf18d77c324ab7829fe0c784a6 /tests/test_ext_autodoc.py | |
parent | 4ad466c7a602ca3b60a9cee15a07f84903a28fe8 (diff) | |
parent | 5f51a1e63f9442439466b7acede87ad21d49bdc0 (diff) | |
download | sphinx-git-3c7d35d2a20cfa7c915538704680a33db87c0563.tar.gz |
Merge branch '3.x'
Diffstat (limited to 'tests/test_ext_autodoc.py')
-rw-r--r-- | tests/test_ext_autodoc.py | 1784 |
1 files changed, 1782 insertions, 2 deletions
diff --git a/tests/test_ext_autodoc.py b/tests/test_ext_autodoc.py index 69846d3ae..4ed4a9b05 100644 --- a/tests/test_ext_autodoc.py +++ b/tests/test_ext_autodoc.py @@ -2,15 +2,1795 @@ test_autodoc ~~~~~~~~~~~~ - Test the autodoc extension. + 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 = 'alphabetical', + 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.parent = object # dummy + 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.parent = object # dummy + 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_autofunction_for_decorated(app): + actual = do_autodoc(app, 'function', 'target.decorator.foo') + assert list(actual) == [ + '', + '.. py:function:: foo()', + ' :module: target.decorator', + '', + ] + + +@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_automethod_for_decorated(app): + actual = do_autodoc(app, 'method', 'target.decorator.Bar.meth') + assert list(actual) == [ + '', + '.. py:method:: Bar.meth()', + ' :module: target.decorator', + '', + ] + + +@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(*args, **kwargs)', + ' :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') @@ -19,7 +1799,7 @@ def test_autodoc(app, status, warning): content = app.env.get_doctree('index') assert isinstance(content[3], addnodes.desc) - assert content[3][0].astext() == 'autodoc_dummy_module.test' + assert content[3][0].astext() == 'autodoc_dummy_module.test()' assert content[3][1].astext() == 'Dummy function using dummy.*' # issue sphinx-doc/sphinx#2437 |