""" test_ext_autodoc_configs ~~~~~~~~~~~~~~~~~~~~~~~~ Test the autodoc extension. This tests mainly for config variables :copyright: Copyright 2007-2020 by the Sphinx team, see AUTHORS. :license: BSD, see LICENSE for details. """ import platform import pytest from test_autodoc import do_autodoc IS_PYPY = platform.python_implementation() == 'PyPy' @pytest.mark.sphinx('html', testroot='ext-autodoc') def test_autoclass_content_class(app): app.config.autoclass_content = 'class' options = {"members": None} actual = do_autodoc(app, 'module', 'target.autoclass_content', options) assert list(actual) == [ '', '.. py:module:: target.autoclass_content', '', '', '.. py:class:: A', ' :module: target.autoclass_content', '', ' A class having no __init__, no __new__', '', '', '.. py:class:: B()', ' :module: target.autoclass_content', '', ' A class having __init__(no docstring), no __new__', '', '', '.. py:class:: C()', ' :module: target.autoclass_content', '', ' A class having __init__, no __new__', '', '', '.. py:class:: D', ' :module: target.autoclass_content', '', ' A class having no __init__, __new__(no docstring)', '', '', '.. py:class:: E', ' :module: target.autoclass_content', '', ' A class having no __init__, __new__', '', '', '.. py:class:: F()', ' :module: target.autoclass_content', '', ' A class having both __init__ and __new__', '', '', '.. py:class:: G()', ' :module: target.autoclass_content', '', ' A class inherits __init__ without docstring.', '', '', '.. py:class:: H()', ' :module: target.autoclass_content', '', ' A class inherits __new__ without docstring.', '', ] @pytest.mark.sphinx('html', testroot='ext-autodoc') def test_autoclass_content_init(app): app.config.autoclass_content = 'init' options = {"members": None} actual = do_autodoc(app, 'module', 'target.autoclass_content', options) assert list(actual) == [ '', '.. py:module:: target.autoclass_content', '', '', '.. py:class:: A', ' :module: target.autoclass_content', '', ' A class having no __init__, no __new__', '', '', '.. py:class:: B()', ' :module: target.autoclass_content', '', ' A class having __init__(no docstring), no __new__', '', '', '.. py:class:: C()', ' :module: target.autoclass_content', '', ' __init__ docstring', '', '', '.. py:class:: D', ' :module: target.autoclass_content', '', ' A class having no __init__, __new__(no docstring)', '', '', '.. py:class:: E', ' :module: target.autoclass_content', '', ' __new__ docstring', '', '', '.. py:class:: F()', ' :module: target.autoclass_content', '', ' __init__ docstring', '', '', '.. py:class:: G()', ' :module: target.autoclass_content', '', ' __init__ docstring', '', '', '.. py:class:: H()', ' :module: target.autoclass_content', '', ' __new__ docstring', '', ] @pytest.mark.sphinx('html', testroot='ext-autodoc') def test_autoclass_content_both(app): app.config.autoclass_content = 'both' options = {"members": None} actual = do_autodoc(app, 'module', 'target.autoclass_content', options) assert list(actual) == [ '', '.. py:module:: target.autoclass_content', '', '', '.. py:class:: A', ' :module: target.autoclass_content', '', ' A class having no __init__, no __new__', '', '', '.. py:class:: B()', ' :module: target.autoclass_content', '', ' A class having __init__(no docstring), no __new__', '', '', '.. py:class:: C()', ' :module: target.autoclass_content', '', ' A class having __init__, no __new__', '', ' __init__ docstring', '', '', '.. py:class:: D', ' :module: target.autoclass_content', '', ' A class having no __init__, __new__(no docstring)', '', '', '.. py:class:: E', ' :module: target.autoclass_content', '', ' A class having no __init__, __new__', '', ' __new__ docstring', '', '', '.. py:class:: F()', ' :module: target.autoclass_content', '', ' A class having both __init__ and __new__', '', ' __init__ docstring', '', '', '.. py:class:: G()', ' :module: target.autoclass_content', '', ' A class inherits __init__ without docstring.', '', ' __init__ docstring', '', '', '.. py:class:: H()', ' :module: target.autoclass_content', '', ' A class inherits __new__ without docstring.', '', ' __new__ docstring', '', ] @pytest.mark.sphinx('html', testroot='ext-autodoc') def test_autodoc_inherit_docstrings(app): assert app.config.autodoc_inherit_docstrings is True # default actual = do_autodoc(app, 'method', 'target.inheritance.Derived.inheritedmeth') assert list(actual) == [ '', '.. py:method:: Derived.inheritedmeth()', ' :module: target.inheritance', '', ' Inherited function.', '', ] # disable autodoc_inherit_docstrings app.config.autodoc_inherit_docstrings = False actual = do_autodoc(app, 'method', 'target.inheritance.Derived.inheritedmeth') assert list(actual) == [ '', '.. py:method:: Derived.inheritedmeth()', ' :module: target.inheritance', '' ] @pytest.mark.sphinx('html', testroot='ext-autodoc') def test_autodoc_docstring_signature(app): options = {"members": None} actual = do_autodoc(app, 'class', 'target.DocstringSig', options) assert list(actual) == [ '', '.. py:class:: DocstringSig', ' :module: target', '', '', ' .. py:method:: DocstringSig.meth(FOO, BAR=1) -> BAZ', ' :module: target', '', ' First line of docstring', '', ' rest of docstring', '', '', ' .. py:method:: DocstringSig.meth2()', ' :module: target', '', ' First line, no signature', ' Second line followed by indentation::', '', ' indented line', '', '', ' .. py:method:: DocstringSig.prop1', ' :module: target', ' :property:', '', ' First line of docstring', '', '', ' .. py:method:: DocstringSig.prop2', ' :module: target', ' :property:', '', ' First line of docstring', ' Second line of docstring', '', ] # disable autodoc_docstring_signature app.config.autodoc_docstring_signature = False actual = do_autodoc(app, 'class', 'target.DocstringSig', options) assert list(actual) == [ '', '.. py:class:: DocstringSig', ' :module: target', '', '', ' .. py:method:: DocstringSig.meth()', ' :module: target', '', ' meth(FOO, BAR=1) -> BAZ', ' First line of docstring', '', ' rest of docstring', '', '', '', ' .. py:method:: DocstringSig.meth2()', ' :module: target', '', ' First line, no signature', ' Second line followed by indentation::', '', ' indented line', '', '', ' .. py:method:: DocstringSig.prop1', ' :module: target', ' :property:', '', ' DocstringSig.prop1(self)', ' First line of docstring', '', '', ' .. py:method:: DocstringSig.prop2', ' :module: target', ' :property:', '', ' First line of docstring', ' Second line of docstring', '', ] @pytest.mark.sphinx('html', testroot='ext-autodoc') def test_autoclass_content_and_docstring_signature_class(app): app.config.autoclass_content = 'class' options = {"members": None, "undoc-members": None} actual = do_autodoc(app, 'module', 'target.docstring_signature', options) assert list(actual) == [ '', '.. py:module:: target.docstring_signature', '', '', '.. py:class:: A(foo, bar)', ' :module: target.docstring_signature', '', '', '.. py:class:: B(foo, bar)', ' :module: target.docstring_signature', '', '', '.. py:class:: C(foo, bar)', ' :module: target.docstring_signature', '', '', '.. py:class:: D()', ' :module: target.docstring_signature', '' ] @pytest.mark.sphinx('html', testroot='ext-autodoc') def test_autoclass_content_and_docstring_signature_init(app): app.config.autoclass_content = 'init' options = {"members": None, "undoc-members": None} actual = do_autodoc(app, 'module', 'target.docstring_signature', options) assert list(actual) == [ '', '.. py:module:: target.docstring_signature', '', '', '.. py:class:: A(foo, bar)', ' :module: target.docstring_signature', '', '', '.. py:class:: B(foo, bar, baz)', ' :module: target.docstring_signature', '', '', '.. py:class:: C(foo, bar, baz)', ' :module: target.docstring_signature', '', '', '.. py:class:: D(foo, bar, baz)', ' :module: target.docstring_signature', '' ] @pytest.mark.sphinx('html', testroot='ext-autodoc') def test_autoclass_content_and_docstring_signature_both(app): app.config.autoclass_content = 'both' options = {"members": None, "undoc-members": None} actual = do_autodoc(app, 'module', 'target.docstring_signature', options) assert list(actual) == [ '', '.. py:module:: target.docstring_signature', '', '', '.. py:class:: A(foo, bar)', ' :module: target.docstring_signature', '', '', '.. py:class:: B(foo, bar)', ' :module: target.docstring_signature', '', ' B(foo, bar, baz)', '', '', '.. py:class:: C(foo, bar)', ' :module: target.docstring_signature', '', ' C(foo, bar, baz)', '', '', '.. py:class:: D(foo, bar, baz)', ' :module: target.docstring_signature', '', ] @pytest.mark.sphinx('html', testroot='ext-autodoc') def test_mocked_module_imports(app, warning): # no autodoc_mock_imports options = {"members": 'TestAutodoc,decoratedFunction,func'} actual = do_autodoc(app, 'module', 'target.need_mocks', options) assert list(actual) == [] assert "autodoc: failed to import module 'need_mocks'" in warning.getvalue() # with autodoc_mock_imports app.config.autodoc_mock_imports = [ 'missing_module', 'missing_package1', 'missing_package2', 'missing_package3', 'sphinx.missing_module4', ] warning.truncate(0) actual = do_autodoc(app, 'module', 'target.need_mocks', options) assert list(actual) == [ '', '.. py:module:: target.need_mocks', '', '', '.. py:class:: TestAutodoc', ' :module: target.need_mocks', '', ' TestAutodoc docstring.', '', '', ' .. py:method:: TestAutodoc.decoratedMethod()', ' :module: target.need_mocks', '', ' TestAutodoc::decoratedMethod docstring', '', '', '.. py:function:: decoratedFunction()', ' :module: target.need_mocks', '', ' decoratedFunction docstring', '', '', '.. py:function:: func(arg: missing_module.Class)', ' :module: target.need_mocks', '', ' a function takes mocked object as an argument', '', ] assert warning.getvalue() == '' @pytest.mark.sphinx('html', testroot='ext-autodoc', confoverrides={'autodoc_typehints': "signature"}) def test_autodoc_typehints_signature(app): options = {"members": None, "undoc-members": True} actual = do_autodoc(app, 'module', 'target.typehints', options) assert list(actual) == [ '', '.. py:module:: target.typehints', '', '', '.. py:class:: Math(s: str, o: object = None)', ' :module: target.typehints', '', '', ' .. py:method:: Math.decr(a: int, b: int = 1) -> int', ' :module: target.typehints', '', '', ' .. py:method:: Math.horse(a: str, b: int) -> None', ' :module: target.typehints', '', '', ' .. py:method:: Math.incr(a: int, b: int = 1) -> int', ' :module: target.typehints', '', '', ' .. py:method:: Math.nothing() -> None', ' :module: target.typehints', '', '', '.. py:function:: complex_func(arg1: str, arg2: List[int], arg3: Tuple[int, ' 'Union[str, Unknown]] = None, *args: str, **kwargs: str) -> None', ' :module: target.typehints', '', '', '.. py:function:: decr(a: int, b: int = 1) -> int', ' :module: target.typehints', '', '', '.. py:function:: incr(a: int, b: int = 1) -> int', ' :module: target.typehints', '', '', '.. py:function:: missing_attr(c, a: str, b: Optional[str] = None) -> str', ' :module: target.typehints', '' ] @pytest.mark.sphinx('html', testroot='ext-autodoc', confoverrides={'autodoc_typehints': "none"}) def test_autodoc_typehints_none(app): options = {"members": None, "undoc-members": True} actual = do_autodoc(app, 'module', 'target.typehints', options) assert list(actual) == [ '', '.. py:module:: target.typehints', '', '', '.. py:class:: Math(s, o=None)', ' :module: target.typehints', '', '', ' .. py:method:: Math.decr(a, b=1)', ' :module: target.typehints', '', '', ' .. py:method:: Math.horse(a, b)', ' :module: target.typehints', '', '', ' .. py:method:: Math.incr(a, b=1)', ' :module: target.typehints', '', '', ' .. py:method:: Math.nothing()', ' :module: target.typehints', '', '', '.. py:function:: complex_func(arg1, arg2, arg3=None, *args, **kwargs)', ' :module: target.typehints', '', '', '.. py:function:: decr(a, b=1)', ' :module: target.typehints', '', '', '.. py:function:: incr(a, b=1)', ' :module: target.typehints', '', '', '.. py:function:: missing_attr(c, a, b=None)', ' :module: target.typehints', '' ] @pytest.mark.sphinx('text', testroot='ext-autodoc', confoverrides={'autodoc_typehints': "description"}) def test_autodoc_typehints_description(app): app.build() context = (app.outdir / 'index.txt').read_text() assert ('target.typehints.incr(a, b=1)\n' '\n' ' Parameters:\n' ' * **a** (*int*) --\n' '\n' ' * **b** (*int*) --\n' '\n' ' Return type:\n' ' int\n' in context) @pytest.mark.sphinx('html', testroot='ext-autodoc') def test_autodoc_default_options(app): # no settings actual = do_autodoc(app, 'class', 'target.enum.EnumCls') assert ' .. py:attribute:: EnumCls.val1' not in actual assert ' .. py:attribute:: EnumCls.val4' not in actual actual = do_autodoc(app, 'class', 'target.CustomIter') assert ' .. py:method:: target.CustomIter' not in actual actual = do_autodoc(app, 'module', 'target') assert '.. py:function:: save_traceback(app)' not in actual # with :members: app.config.autodoc_default_options = {'members': None} actual = do_autodoc(app, 'class', 'target.enum.EnumCls') assert ' .. py:attribute:: EnumCls.val1' in actual assert ' .. py:attribute:: EnumCls.val4' not in actual # with :members: = True app.config.autodoc_default_options = {'members': True} actual = do_autodoc(app, 'class', 'target.enum.EnumCls') assert ' .. py:attribute:: EnumCls.val1' in actual assert ' .. py:attribute:: EnumCls.val4' not in actual # with :members: and :undoc-members: app.config.autodoc_default_options = { 'members': None, 'undoc-members': None, } actual = do_autodoc(app, 'class', 'target.enum.EnumCls') assert ' .. py:attribute:: EnumCls.val1' in actual assert ' .. py:attribute:: EnumCls.val4' in actual # with :special-members: # Note that :members: must be *on* for :special-members: to work. app.config.autodoc_default_options = { 'members': None, 'special-members': None } actual = do_autodoc(app, 'class', 'target.CustomIter') assert ' .. py:method:: CustomIter.__init__()' in actual assert ' Create a new `CustomIter`.' in actual assert ' .. py:method:: CustomIter.__iter__()' in actual assert ' Iterate squares of each value.' in actual if not IS_PYPY: assert ' .. py:attribute:: CustomIter.__weakref__' in actual assert ' list of weak references to the object (if defined)' in actual # :exclude-members: None - has no effect. Unlike :members:, # :special-members:, etc. where None == "include all", here None means # "no/false/off". app.config.autodoc_default_options = { 'members': None, 'exclude-members': None, } actual = do_autodoc(app, 'class', 'target.enum.EnumCls') assert ' .. py:attribute:: EnumCls.val1' in actual assert ' .. py:attribute:: EnumCls.val4' not in actual app.config.autodoc_default_options = { 'members': None, 'special-members': None, 'exclude-members': None, } actual = do_autodoc(app, 'class', 'target.CustomIter') assert ' .. py:method:: CustomIter.__init__()' in actual assert ' Create a new `CustomIter`.' in actual assert ' .. py:method:: CustomIter.__iter__()' in actual assert ' Iterate squares of each value.' in actual if not IS_PYPY: assert ' .. py:attribute:: CustomIter.__weakref__' in actual assert ' list of weak references to the object (if defined)' in actual assert ' .. py:method:: CustomIter.snafucate()' in actual assert ' Makes this snafucated.' in actual @pytest.mark.sphinx('html', testroot='ext-autodoc') def test_autodoc_default_options_with_values(app): # with :members: app.config.autodoc_default_options = {'members': 'val1,val2'} actual = do_autodoc(app, 'class', 'target.enum.EnumCls') assert ' .. py:attribute:: EnumCls.val1' in actual assert ' .. py:attribute:: EnumCls.val2' in actual assert ' .. py:attribute:: EnumCls.val3' not in actual assert ' .. py:attribute:: EnumCls.val4' not in actual # with :member-order: app.config.autodoc_default_options = { 'members': None, 'member-order': 'bysource', } actual = do_autodoc(app, 'class', 'target.Class') assert list(filter(lambda l: '::' in l, actual)) == [ '.. py:class:: Class(arg)', ' .. py:method:: Class.meth()', ' .. py:method:: Class.skipmeth()', ' .. py:method:: Class.excludemeth()', ' .. py:attribute:: Class.attr', ' .. py:attribute:: Class.docattr', ' .. py:attribute:: Class.udocattr', ' .. py:attribute:: Class.mdocattr', ' .. 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', ] # with :special-members: app.config.autodoc_default_options = { 'special-members': '__init__,__iter__', } actual = do_autodoc(app, 'class', 'target.CustomIter') assert ' .. py:method:: CustomIter.__init__()' in actual assert ' Create a new `CustomIter`.' in actual assert ' .. py:method:: CustomIter.__iter__()' in actual assert ' Iterate squares of each value.' in actual if not IS_PYPY: assert ' .. py:attribute:: CustomIter.__weakref__' not in actual assert ' list of weak references to the object (if defined)' not in actual # with :exclude-members: app.config.autodoc_default_options = { 'members': None, 'exclude-members': 'val1' } actual = do_autodoc(app, 'class', 'target.enum.EnumCls') assert ' .. py:attribute:: EnumCls.val1' not in actual assert ' .. py:attribute:: EnumCls.val2' in actual assert ' .. py:attribute:: EnumCls.val3' in actual assert ' .. py:attribute:: EnumCls.val4' not in actual app.config.autodoc_default_options = { 'members': None, 'special-members': None, 'exclude-members': '__weakref__,snafucate', } actual = do_autodoc(app, 'class', 'target.CustomIter') assert ' .. py:method:: CustomIter.__init__()' in actual assert ' Create a new `CustomIter`.' in actual assert ' .. py:method:: CustomIter.__iter__()' in actual assert ' Iterate squares of each value.' in actual if not IS_PYPY: assert ' .. py:attribute:: CustomIter.__weakref__' not in actual assert ' list of weak references to the object (if defined)' not in actual assert ' .. py:method:: CustomIter.snafucate()' not in actual assert ' Makes this snafucated.' not in actual