diff --git a/CHANGES.rst b/CHANGES.rst index 28438131aa2..e3eaf731ff7 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -134,6 +134,8 @@ Bugs fixed * #12975: Avoid rendering a trailing comma in C and C++ multi-line signatures. * #13178: autodoc: Fix resolution for ``pathlib`` types. Patch by Adam Turner. +* #13136: autodoc: Correctly handle multiple inheritance. + Patch by Pavel Holica Testing ------- diff --git a/sphinx/ext/autodoc/__init__.py b/sphinx/ext/autodoc/__init__.py index 5c2b202b927..eaa0294e6f1 100644 --- a/sphinx/ext/autodoc/__init__.py +++ b/sphinx/ext/autodoc/__init__.py @@ -742,10 +742,19 @@ def filter_members( def is_filtered_inherited_member(name: str, obj: Any) -> bool: inherited_members = self.options.inherited_members or set() + seen = set() if inspect.isclass(self.object): for cls in self.object.__mro__: - if cls.__name__ in inherited_members and cls != self.object: + if name in cls.__dict__: + seen.add(cls) + if ( + cls.__name__ in inherited_members + and cls != self.object + and any( + issubclass(potential_child, cls) for potential_child in seen + ) + ): # given member is a member of specified *super class* return True if name in cls.__dict__: diff --git a/tests/roots/test-ext-autodoc/target/inheritance.py b/tests/roots/test-ext-autodoc/target/inheritance.py index 5c65aa65afd..a8ddc5b9213 100644 --- a/tests/roots/test-ext-autodoc/target/inheritance.py +++ b/tests/roots/test-ext-autodoc/target/inheritance.py @@ -14,7 +14,13 @@ def inheritedstaticmeth(cls): # NoQA: PLW0211 """Inherited static method.""" -class Derived(Base): +class AnotherBase: + #: docstring + def another_inheritedmeth(self): + """Another inherited function.""" + + +class Derived(Base, AnotherBase): def inheritedmeth(self): # no docstring here pass diff --git a/tests/test_extensions/test_ext_autodoc.py b/tests/test_extensions/test_ext_autodoc.py index b1ba884033c..9383401ac6c 100644 --- a/tests/test_extensions/test_ext_autodoc.py +++ b/tests/test_extensions/test_ext_autodoc.py @@ -831,6 +831,7 @@ def test_autodoc_inherited_members(app): } actual = do_autodoc(app, 'class', 'target.inheritance.Derived', options) assert list(filter(lambda l: 'method::' in l, actual)) == [ + ' .. py:method:: Derived.another_inheritedmeth()', ' .. py:method:: Derived.inheritedclassmeth()', ' .. py:method:: Derived.inheritedmeth()', ' .. py:method:: Derived.inheritedstaticmeth(cls)', diff --git a/tests/test_extensions/test_ext_autodoc_automodule.py b/tests/test_extensions/test_ext_autodoc_automodule.py index c9503a765c3..c27c7b59537 100644 --- a/tests/test_extensions/test_ext_autodoc_automodule.py +++ b/tests/test_extensions/test_ext_autodoc_automodule.py @@ -134,6 +134,16 @@ def test_automodule_inherited_members(app): '.. py:module:: target.inheritance', '', '', + '.. py:class:: AnotherBase()', + ' :module: target.inheritance', + '', + '', + ' .. py:method:: AnotherBase.another_inheritedmeth()', + ' :module: target.inheritance', + '', + ' Another inherited function.', + '', + '', '.. py:class:: Base()', ' :module: target.inheritance', '', @@ -169,6 +179,12 @@ def test_automodule_inherited_members(app): ' :module: target.inheritance', '', '', + ' .. py:method:: Derived.another_inheritedmeth()', + ' :module: target.inheritance', + '', + ' Another inherited function.', + '', + '', ' .. py:method:: Derived.inheritedmeth()', ' :module: target.inheritance', '', diff --git a/tests/test_extensions/test_ext_autodoc_configs.py b/tests/test_extensions/test_ext_autodoc_configs.py index 66681f696bc..73a3f171285 100644 --- a/tests/test_extensions/test_ext_autodoc_configs.py +++ b/tests/test_extensions/test_ext_autodoc_configs.py @@ -319,6 +319,12 @@ def test_autodoc_inherit_docstrings_for_inherited_members(app): ' :module: target.inheritance', '', '', + ' .. py:method:: Derived.another_inheritedmeth()', + ' :module: target.inheritance', + '', + ' Another inherited function.', + '', + '', ' .. py:attribute:: Derived.inheritedattr', ' :module: target.inheritance', ' :value: None', @@ -356,6 +362,12 @@ def test_autodoc_inherit_docstrings_for_inherited_members(app): ' :module: target.inheritance', '', '', + ' .. py:method:: Derived.another_inheritedmeth()', + ' :module: target.inheritance', + '', + ' Another inherited function.', + '', + '', ' .. py:method:: Derived.inheritedclassmeth()', ' :module: target.inheritance', ' :classmethod:',