diff options
Diffstat (limited to '')
-rw-r--r-- | tests/test_extensions/test_ext_autodoc.py (renamed from tests/test_ext_autodoc.py) | 626 |
1 files changed, 506 insertions, 120 deletions
diff --git a/tests/test_ext_autodoc.py b/tests/test_extensions/test_ext_autodoc.py index 7062763..54f81f2 100644 --- a/tests/test_ext_autodoc.py +++ b/tests/test_extensions/test_ext_autodoc.py @@ -4,8 +4,14 @@ This tests mainly the Documenters; the auto directives are tested in a test source file translated by test_build. """ +from __future__ import annotations + +import functools +import itertools +import operator import sys from types import SimpleNamespace +from typing import TYPE_CHECKING from unittest.mock import Mock from warnings import catch_warnings @@ -14,8 +20,8 @@ from docutils.statemachine import ViewList from sphinx import addnodes from sphinx.ext.autodoc import ALL, ModuleLevelDocumenter, Options -from sphinx.ext.autodoc.directive import DocumenterBridge, process_documenter_options -from sphinx.util.docutils import LoggingReporter + +from tests.test_extensions.autodoc_util import do_autodoc try: # Enable pyximport to test cython module @@ -24,47 +30,35 @@ try: 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 +if TYPE_CHECKING: + from typing import Any 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, - no_index = False, - annotation = None, - synopsis = '', - platform = '', - deprecated = False, - members = [], - member_order = 'alphabetical', - exclude_members = set(), - ignore_module_all = False, + inherited_members=False, + undoc_members=False, + private_members=False, + special_members=False, + imported_members=False, + show_inheritance=False, + no_index=False, + annotation=None, + synopsis='', + platform='', + deprecated=False, + members=[], + member_order='alphabetical', + exclude_members=set(), + ignore_module_all=False, ) directive = SimpleNamespace( - env = env, - genopt = options, - result = ViewList(), - record_dependencies = set(), - state = Mock(), + env=env, + genopt=options, + result=ViewList(), + record_dependencies=set(), + state=Mock(), ) directive.state.document.settings.tab_width = 8 @@ -74,23 +68,6 @@ def make_directive_bridge(env): processed_signatures = [] -def process_signature(app, what, name, obj, options, args, retann): - processed_signatures.append((what, name)) - if name == 'bar': - return '42', None - return 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 - return None - - def test_parse_name(app): def verify(objtype, name, result): inst = app.registry.documenters[objtype](directive, name) @@ -103,7 +80,7 @@ def test_parse_name(app): 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() + assert 'signature arguments' in app.warning.getvalue() # for functions/classes verify('function', 'test_ext_autodoc.raises', @@ -131,6 +108,21 @@ def test_parse_name(app): def test_format_signature(app): + def process_signature(app, what, name, obj, options, args, retann): + processed_signatures.append((what, name)) + if name == 'bar': + return '42', None + return 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 + return None + app.connect('autodoc-process-signature', process_signature) app.connect('autodoc-skip-member', skip_member) @@ -226,12 +218,14 @@ def test_format_signature(app): 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 @@ -330,7 +324,7 @@ def test_get_doc(app): 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] + res = functools.reduce(operator.iadd, ds, [])[:-1] print(res) return res @@ -342,6 +336,7 @@ def test_get_doc(app): # standard function, diverse docstring styles... def f(): """Docstring""" + def g(): """ Docstring @@ -837,9 +832,13 @@ def test_autodoc_special_members(app): ] # all special methods - options = {"members": None, - "undoc-members": None, - "special-members": None} + options = { + "members": None, + "undoc-members": None, + "special-members": None, + } + if sys.version_info >= (3, 13, 0, 'alpha', 5): + options["exclude-members"] = "__static_attributes__" actual = do_autodoc(app, 'class', 'target.Class', options) assert list(filter(lambda l: '::' in l, actual)) == [ '.. py:class:: Class(arg)', @@ -1402,73 +1401,411 @@ def test_slots(app): ] +class _EnumFormatter: + def __init__(self, name: str, *, module: str = 'target.enums') -> None: + self.name = name + self.module = module + + @property + def target(self) -> str: + """The autodoc target class.""" + return f'{self.module}.{self.name}' + + def subtarget(self, name: str) -> str: + """The autodoc sub-target (an attribute, method, etc).""" + return f'{self.target}.{name}' + + def _node( + self, role: str, name: str, doc: str, *, args: str, indent: int, **options: Any, + ) -> list[str]: + prefix = indent * ' ' + tab = ' ' * 3 + + def rst_option(name: str, value: Any) -> str: + value = '' if value in {1, True} else value + return f'{prefix}{tab}:{name}: {value!s}'.rstrip() + + lines = [ + '', + f'{prefix}.. py:{role}:: {name}{args}', + f'{prefix}{tab}:module: {self.module}', + *itertools.starmap(rst_option, options.items()), + ] + if doc: + lines.extend(['', f'{prefix}{tab}{doc}']) + lines.append('') + return lines + + def entry( + self, + entry_name: str, + doc: str = '', + *, + role: str, + args: str = '', + indent: int = 3, + **rst_options: Any, + ) -> list[str]: + """Get the RST lines for a named attribute, method, etc.""" + qualname = f'{self.name}.{entry_name}' + return self._node(role, qualname, doc, args=args, indent=indent, **rst_options) + + def brief(self, doc: str, *, indent: int = 0, **options: Any) -> list[str]: + """Generate the brief part of the class being documented.""" + assert doc, f'enumeration class {self.target!r} should have an explicit docstring' + + if sys.version_info[:2] >= (3, 13) or sys.version_info[:3] >= (3, 12, 3): + args = ('(value, names=<not given>, *values, module=None, ' + 'qualname=None, type=None, start=1, boundary=None)') + elif sys.version_info[:2] >= (3, 12): + args = ('(value, names=None, *values, module=None, ' + 'qualname=None, type=None, start=1, boundary=None)') + elif sys.version_info[:2] >= (3, 11): + args = ('(value, names=None, *, module=None, qualname=None, ' + 'type=None, start=1, boundary=None)') + else: + args = '(value)' + + return self._node('class', self.name, doc, args=args, indent=indent, **options) + + def method( + self, name: str, doc: str, *flags: str, args: str = '()', indent: int = 3, + ) -> list[str]: + rst_options = dict.fromkeys(flags, '') + return self.entry(name, doc, role='method', args=args, indent=indent, **rst_options) + + def member(self, name: str, value: Any, doc: str, *, indent: int = 3) -> list[str]: + rst_options = {'value': repr(value)} + return self.entry(name, doc, role='attribute', indent=indent, **rst_options) + + +@pytest.fixture() +def autodoc_enum_options() -> dict[str, object]: + """Default autodoc options to use when testing enum's documentation.""" + return {"members": None, "undoc-members": None} + + +@pytest.mark.sphinx('html', testroot='ext-autodoc') +def test_enum_class(app, autodoc_enum_options): + fmt = _EnumFormatter('EnumCls') + options = autodoc_enum_options | {'private-members': None} + + actual = do_autodoc(app, 'class', fmt.target, options) + assert list(actual) == [ + *fmt.brief('this is enum class'), + *fmt.method('say_goodbye', 'a classmethod says good-bye to you.', 'classmethod'), + *fmt.method('say_hello', 'a method says hello to you.'), + *fmt.member('val1', 12, 'doc for val1'), + *fmt.member('val2', 23, 'doc for val2'), + *fmt.member('val3', 34, 'doc for val3'), + *fmt.member('val4', 34, ''), # val4 is alias of val3 + ] + + # Inherited members exclude the native Enum API (in particular + # the 'name' and 'value' properties), unless they were explicitly + # redefined by the user in one of the bases. + actual = do_autodoc(app, 'class', fmt.target, options | {'inherited-members': None}) + assert list(actual) == [ + *fmt.brief('this is enum class'), + *fmt.method('say_goodbye', 'a classmethod says good-bye to you.', 'classmethod'), + *fmt.method('say_hello', 'a method says hello to you.'), + *fmt.member('val1', 12, 'doc for val1'), + *fmt.member('val2', 23, 'doc for val2'), + *fmt.member('val3', 34, 'doc for val3'), + *fmt.member('val4', 34, ''), # val4 is alias of val3 + ] + + # checks for an attribute of EnumCls + actual = do_autodoc(app, 'attribute', fmt.subtarget('val1')) + assert list(actual) == fmt.member('val1', 12, 'doc for val1', indent=0) + + @pytest.mark.sphinx('html', testroot='ext-autodoc') -def test_enum_class(app): - options = {"members": None} - actual = do_autodoc(app, 'class', 'target.enums.EnumCls', options) - - if sys.version_info[:2] >= (3, 12): - args = ('(value, names=None, *values, module=None, ' - 'qualname=None, type=None, start=1, boundary=None)') - elif sys.version_info[:2] >= (3, 11): - args = ('(value, names=None, *, module=None, qualname=None, ' - 'type=None, start=1, boundary=None)') - else: - args = '(value)' +def test_enum_class_with_data_type(app, autodoc_enum_options): + fmt = _EnumFormatter('EnumClassWithDataType') + actual = do_autodoc(app, 'class', fmt.target, autodoc_enum_options) assert list(actual) == [ - '', - '.. py:class:: EnumCls' + args, - ' :module: target.enums', - '', - ' this is enum class', - '', - '', - ' .. py:method:: EnumCls.say_goodbye()', - ' :module: target.enums', - ' :classmethod:', - '', - ' a classmethod says good-bye to you.', - '', - '', - ' .. py:method:: EnumCls.say_hello()', - ' :module: target.enums', - '', - ' a method says hello to you.', - '', - '', - ' .. py:attribute:: EnumCls.val1', - ' :module: target.enums', - ' :value: 12', - '', - ' doc for val1', - '', - '', - ' .. py:attribute:: EnumCls.val2', - ' :module: target.enums', - ' :value: 23', - '', - ' doc for val2', - '', - '', - ' .. py:attribute:: EnumCls.val3', - ' :module: target.enums', - ' :value: 34', - '', - ' doc for val3', - '', + *fmt.brief('this is enum class'), + *fmt.method('say_goodbye', 'docstring', 'classmethod'), + *fmt.method('say_hello', 'docstring'), + *fmt.member('x', 'x', ''), ] - # checks for an attribute of EnumClass - actual = do_autodoc(app, 'attribute', 'target.enums.EnumCls.val1') + options = autodoc_enum_options | {'inherited-members': None} + actual = do_autodoc(app, 'class', fmt.target, options) assert list(actual) == [ - '', - '.. py:attribute:: EnumCls.val1', - ' :module: target.enums', - ' :value: 12', - '', - ' doc for val1', - '', + *fmt.brief('this is enum class'), + *fmt.entry('dtype', 'docstring', role='property'), + *fmt.method('isupper', 'inherited'), + *fmt.method('say_goodbye', 'docstring', 'classmethod'), + *fmt.method('say_hello', 'docstring'), + *fmt.member('x', 'x', ''), + ] + + +@pytest.mark.sphinx('html', testroot='ext-autodoc') +def test_enum_class_with_mixin_type(app, autodoc_enum_options): + fmt = _EnumFormatter('EnumClassWithMixinType') + + actual = do_autodoc(app, 'class', fmt.target, autodoc_enum_options) + assert list(actual) == [ + *fmt.brief('this is enum class'), + *fmt.method('say_goodbye', 'docstring', 'classmethod'), + *fmt.method('say_hello', 'docstring'), + *fmt.member('x', 'X', ''), + ] + + options = autodoc_enum_options | {'inherited-members': None} + actual = do_autodoc(app, 'class', fmt.target, options) + assert list(actual) == [ + *fmt.brief('this is enum class'), + *fmt.method('say_goodbye', 'docstring', 'classmethod'), + *fmt.method('say_hello', 'docstring'), + *fmt.entry('value', 'uppercased', role='property'), + *fmt.member('x', 'X', ''), + ] + + +@pytest.mark.sphinx('html', testroot='ext-autodoc') +def test_enum_class_with_mixin_type_and_inheritence(app, autodoc_enum_options): + fmt = _EnumFormatter('EnumClassWithMixinTypeInherit') + + actual = do_autodoc(app, 'class', fmt.target, autodoc_enum_options) + assert list(actual) == [ + *fmt.brief('this is enum class'), + *fmt.member('x', 'X', ''), + ] + + options = autodoc_enum_options | {'inherited-members': None} + actual = do_autodoc(app, 'class', fmt.target, options) + assert list(actual) == [ + *fmt.brief('this is enum class'), + *fmt.method('say_goodbye', 'inherited', 'classmethod'), + *fmt.method('say_hello', 'inherited'), + *fmt.entry('value', 'uppercased', role='property'), + *fmt.member('x', 'X', ''), + ] + + +@pytest.mark.sphinx('html', testroot='ext-autodoc') +def test_enum_class_with_mixin_enum_type(app, autodoc_enum_options): + fmt = _EnumFormatter('EnumClassWithMixinEnumType') + + actual = do_autodoc(app, 'class', fmt.target, autodoc_enum_options) + assert list(actual) == [ + *fmt.brief('this is enum class'), + # override() is overridden at the class level so it should be rendered + *fmt.method('override', 'overridden'), + # say_goodbye() and say_hello() are not rendered since they are inherited + *fmt.member('x', 'x', ''), + ] + + options = autodoc_enum_options | {'inherited-members': None} + actual = do_autodoc(app, 'class', fmt.target, options) + assert list(actual) == [ + *fmt.brief('this is enum class'), + *fmt.method('override', 'overridden'), + *fmt.method('say_goodbye', 'inherited', 'classmethod'), + *fmt.method('say_hello', 'inherited'), + *fmt.member('x', 'x', ''), + ] + + +@pytest.mark.sphinx('html', testroot='ext-autodoc') +def test_enum_class_with_mixin_and_data_type(app, autodoc_enum_options): + fmt = _EnumFormatter('EnumClassWithMixinAndDataType') + + actual = do_autodoc(app, 'class', fmt.target, autodoc_enum_options) + assert list(actual) == [ + *fmt.brief('this is enum class'), + *fmt.method('isupper', 'overridden'), + *fmt.method('say_goodbye', 'overridden', 'classmethod'), + *fmt.method('say_hello', 'overridden'), + *fmt.member('x', 'X', ''), + ] + + # add the special member __str__ (but not the inherited members) + options = autodoc_enum_options | {'special-members': '__str__'} + actual = do_autodoc(app, 'class', fmt.target, options) + assert list(actual) == [ + *fmt.brief('this is enum class'), + *fmt.method('__str__', 'overridden'), + *fmt.method('isupper', 'overridden'), + *fmt.method('say_goodbye', 'overridden', 'classmethod'), + *fmt.method('say_hello', 'overridden'), + *fmt.member('x', 'X', ''), + ] + + options = autodoc_enum_options | {'inherited-members': None} + actual = do_autodoc(app, 'class', fmt.target, options) + assert list(actual) == [ + *fmt.brief('this is enum class'), + *fmt.entry('dtype', 'docstring', role='property'), + *fmt.method('isupper', 'overridden'), + *fmt.method('say_goodbye', 'overridden', 'classmethod'), + *fmt.method('say_hello', 'overridden'), + *fmt.entry('value', 'uppercased', role='property'), + *fmt.member('x', 'X', ''), + ] + + +@pytest.mark.sphinx('html', testroot='ext-autodoc') +def test_enum_with_parent_enum(app, autodoc_enum_options): + fmt = _EnumFormatter('EnumClassWithParentEnum') + + actual = do_autodoc(app, 'class', fmt.target, autodoc_enum_options) + assert list(actual) == [ + *fmt.brief('this is enum class'), + *fmt.method('isupper', 'overridden'), + *fmt.member('x', 'X', ''), + ] + + # add the special member __str__ (but not the inherited members) + options = autodoc_enum_options | {'special-members': '__str__'} + actual = do_autodoc(app, 'class', fmt.target, options) + assert list(actual) == [ + *fmt.brief('this is enum class'), + *fmt.method('__str__', 'overridden'), + *fmt.method('isupper', 'overridden'), + *fmt.member('x', 'X', ''), + ] + + options = autodoc_enum_options | {'inherited-members': None} + actual = do_autodoc(app, 'class', fmt.target, options) + assert list(actual) == [ + *fmt.brief('this is enum class'), + *fmt.entry('dtype', 'docstring', role='property'), + *fmt.method('isupper', 'overridden'), + *fmt.method('override', 'inherited'), + *fmt.method('say_goodbye', 'inherited', 'classmethod'), + *fmt.method('say_hello', 'inherited'), + *fmt.entry('value', 'uppercased', role='property'), + *fmt.member('x', 'X', ''), + ] + + +@pytest.mark.sphinx('html', testroot='ext-autodoc') +def test_enum_sunder_method(app, autodoc_enum_options): + PRIVATE = {'private-members': None} # sunder methods are recognized as private + + fmt = _EnumFormatter('EnumSunderMissingInNonEnumMixin') + actual = do_autodoc(app, 'class', fmt.target, autodoc_enum_options) + assert list(actual) == [*fmt.brief('this is enum class')] + actual = do_autodoc(app, 'class', fmt.target, autodoc_enum_options | PRIVATE) + assert list(actual) == [*fmt.brief('this is enum class')] + + fmt = _EnumFormatter('EnumSunderMissingInEnumMixin') + actual = do_autodoc(app, 'class', fmt.target, autodoc_enum_options) + assert list(actual) == [*fmt.brief('this is enum class')] + actual = do_autodoc(app, 'class', fmt.target, autodoc_enum_options | PRIVATE) + assert list(actual) == [*fmt.brief('this is enum class')] + + fmt = _EnumFormatter('EnumSunderMissingInDataType') + actual = do_autodoc(app, 'class', fmt.target, autodoc_enum_options) + assert list(actual) == [*fmt.brief('this is enum class')] + actual = do_autodoc(app, 'class', fmt.target, autodoc_enum_options | PRIVATE) + assert list(actual) == [*fmt.brief('this is enum class')] + + fmt = _EnumFormatter('EnumSunderMissingInClass') + actual = do_autodoc(app, 'class', fmt.target, autodoc_enum_options) + assert list(actual) == [*fmt.brief('this is enum class')] + actual = do_autodoc(app, 'class', fmt.target, autodoc_enum_options | PRIVATE) + assert list(actual) == [ + *fmt.brief('this is enum class'), + *fmt.method('_missing_', 'docstring', 'classmethod', args='(value)'), + ] + + +@pytest.mark.sphinx('html', testroot='ext-autodoc') +def test_enum_inherited_sunder_method(app, autodoc_enum_options): + options = autodoc_enum_options | {'private-members': None, 'inherited-members': None} + + fmt = _EnumFormatter('EnumSunderMissingInNonEnumMixin') + actual = do_autodoc(app, 'class', fmt.target, options) + assert list(actual) == [ + *fmt.brief('this is enum class'), + *fmt.method('_missing_', 'inherited', 'classmethod', args='(value)'), + ] + + fmt = _EnumFormatter('EnumSunderMissingInEnumMixin') + actual = do_autodoc(app, 'class', fmt.target, options) + assert list(actual) == [ + *fmt.brief('this is enum class'), + *fmt.method('_missing_', 'inherited', 'classmethod', args='(value)'), + ] + + fmt = _EnumFormatter('EnumSunderMissingInDataType') + actual = do_autodoc(app, 'class', fmt.target, options) + assert list(actual) == [ + *fmt.brief('this is enum class'), + *fmt.method('_missing_', 'inherited', 'classmethod', args='(value)'), + *fmt.entry('dtype', 'docstring', role='property'), + *fmt.method('isupper', 'inherited'), + ] + + fmt = _EnumFormatter('EnumSunderMissingInClass') + actual = do_autodoc(app, 'class', fmt.target, options) + assert list(actual) == [ + *fmt.brief('this is enum class'), + *fmt.method('_missing_', 'docstring', 'classmethod', args='(value)'), + ] + + +@pytest.mark.sphinx('html', testroot='ext-autodoc') +def test_enum_custom_name_property(app, autodoc_enum_options): + fmt = _EnumFormatter('EnumNamePropertyInNonEnumMixin') + actual = do_autodoc(app, 'class', fmt.target, autodoc_enum_options) + assert list(actual) == [*fmt.brief('this is enum class')] + + fmt = _EnumFormatter('EnumNamePropertyInEnumMixin') + actual = do_autodoc(app, 'class', fmt.target, autodoc_enum_options) + assert list(actual) == [*fmt.brief('this is enum class')] + + fmt = _EnumFormatter('EnumNamePropertyInDataType') + actual = do_autodoc(app, 'class', fmt.target, autodoc_enum_options) + assert list(actual) == [*fmt.brief('this is enum class')] + + fmt = _EnumFormatter('EnumNamePropertyInClass') + actual = do_autodoc(app, 'class', fmt.target, autodoc_enum_options) + assert list(actual) == [ + *fmt.brief('this is enum class'), + *fmt.entry('name', 'docstring', role='property'), + ] + + +@pytest.mark.sphinx('html', testroot='ext-autodoc') +def test_enum_inherited_custom_name_property(app, autodoc_enum_options): + options = autodoc_enum_options | {"inherited-members": None} + + fmt = _EnumFormatter('EnumNamePropertyInNonEnumMixin') + actual = do_autodoc(app, 'class', fmt.target, options) + assert list(actual) == [ + *fmt.brief('this is enum class'), + *fmt.entry('name', 'inherited', role='property'), + ] + + fmt = _EnumFormatter('EnumNamePropertyInEnumMixin') + actual = do_autodoc(app, 'class', fmt.target, options) + assert list(actual) == [ + *fmt.brief('this is enum class'), + *fmt.entry('name', 'inherited', role='property'), + ] + + fmt = _EnumFormatter('EnumNamePropertyInDataType') + actual = do_autodoc(app, 'class', fmt.target, options) + assert list(actual) == [ + *fmt.brief('this is enum class'), + *fmt.entry('dtype', 'docstring', role='property'), + *fmt.method('isupper', 'inherited'), + *fmt.entry('name', 'inherited', role='property'), + ] + + fmt = _EnumFormatter('EnumNamePropertyInClass') + actual = do_autodoc(app, 'class', fmt.target, options) + assert list(actual) == [ + *fmt.brief('this is enum class'), + *fmt.entry('name', 'docstring', role='property'), ] @@ -2103,6 +2440,55 @@ def test_singledispatchmethod_automethod(app): ] +@pytest.mark.sphinx('html', testroot='ext-autodoc') +def test_singledispatchmethod_classmethod(app): + options = {"members": None} + actual = do_autodoc(app, 'module', 'target.singledispatchmethod_classmethod', options) + + assert list(actual) == [ + '', + '.. py:module:: target.singledispatchmethod_classmethod', + '', + '', + '.. py:class:: Foo()', + ' :module: target.singledispatchmethod_classmethod', + '', + ' docstring', + '', + '', + ' .. py:method:: Foo.class_meth(arg, kwarg=None)', + ' Foo.class_meth(arg: float, kwarg=None)', + ' Foo.class_meth(arg: int, kwarg=None)', + ' Foo.class_meth(arg: str, kwarg=None)', + ' Foo.class_meth(arg: dict, kwarg=None)', + ' :module: target.singledispatchmethod_classmethod', + ' :classmethod:', + '', + ' A class method for general use.', + '', + ] + + +@pytest.mark.sphinx('html', testroot='ext-autodoc') +def test_singledispatchmethod_classmethod_automethod(app): + options = {} + actual = do_autodoc(app, 'method', 'target.singledispatchmethod_classmethod.Foo.class_meth', options) + + assert list(actual) == [ + '', + '.. py:method:: Foo.class_meth(arg, kwarg=None)', + ' Foo.class_meth(arg: float, kwarg=None)', + ' Foo.class_meth(arg: int, kwarg=None)', + ' Foo.class_meth(arg: str, kwarg=None)', + ' Foo.class_meth(arg: dict, kwarg=None)', + ' :module: target.singledispatchmethod_classmethod', + ' :classmethod:', + '', + ' A class method for general use.', + '', + ] + + @pytest.mark.skipif(sys.version_info[:2] >= (3, 13), reason='Cython does not support Python 3.13 yet.') @pytest.mark.skipif(pyximport is None, reason='cython is not installed') @@ -2276,7 +2662,7 @@ def test_pyclass_for_ClassLevelDocumenter(app): @pytest.mark.sphinx('dummy', testroot='ext-autodoc') def test_autodoc(app, status, warning): - app.builder.build_all() + app.build(force_all=True) content = app.env.get_doctree('index') assert isinstance(content[3], addnodes.desc) |