diff --git a/CHANGES.rst b/CHANGES.rst index 64f94e14ec3..9824906c640 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -118,6 +118,9 @@ Bugs fixed for objects documented as ``:py:data:`` to be hyperlinked in function signatures. * #13858: doctest: doctest blocks are now correctly added to a group defined by the configuration variable ``doctest_test_doctest_blocks``. +* #12797: Fix + ``TypeError: Some type variables (...) are not listed in Generic[...]`` + when inheriting from both Generic and autodoc mocked class. Testing diff --git a/sphinx/ext/autodoc/mock.py b/sphinx/ext/autodoc/mock.py index 236174a62f9..3a892fcfebd 100644 --- a/sphinx/ext/autodoc/mock.py +++ b/sphinx/ext/autodoc/mock.py @@ -29,6 +29,8 @@ class _MockObject: __name__ = '' __sphinx_mock__ = True __sphinx_decorator_args__: tuple[Any, ...] = () + # Attributes listed here should not be mocked and rather raise an Attribute error: + __sphinx_empty_attrs__: set[str] = {'__typing_subst__'} def __new__(cls, *args: Any, **kwargs: Any) -> Any: # NoQA: ARG004 if len(args) == 3 and isinstance(args[1], tuple): @@ -63,6 +65,8 @@ def __getitem__(self, key: Any) -> _MockObject: return _make_subclass(str(key), self.__display_name__, self.__class__)() def __getattr__(self, key: str) -> _MockObject: + if key in self.__sphinx_empty_attrs__: + raise AttributeError return _make_subclass(key, self.__display_name__, self.__class__)() def __call__(self, *args: Any, **kwargs: Any) -> Any: diff --git a/tests/test_extensions/test_ext_autodoc_mock.py b/tests/test_extensions/test_ext_autodoc_mock.py index b2a0e917bee..0bf6d1ce187 100644 --- a/tests/test_extensions/test_ext_autodoc_mock.py +++ b/tests/test_extensions/test_ext_autodoc_mock.py @@ -5,7 +5,7 @@ import abc import sys from importlib import import_module -from typing import TypeVar +from typing import Generic, TypeVar import pytest @@ -57,6 +57,28 @@ class SubClass2(mock.SomeClass[T]): assert SubClass2.__doc__ == 'docstring of SubClass' assert isinstance(obj2, SubClass2) + # test subclass with typing.Generic + # Creating this class would raise an error on Python3.11+ + # as mock objects are detected as typevars if hasattr(__typing_subst__) is True. + + assert not hasattr(mock.SomeClass, '__typing_subst__') + S = TypeVar('S') + + class GenericClass(mock.SomeClass, Generic[T, S]): + """docstring of GenericSubclass""" + + obj3 = GenericClass() + assert isinstance(obj3, _MockObject) + assert isinstance(obj3.some_attr, _MockObject) + assert isinstance(obj3.some_method(), _MockObject) + assert isinstance(obj3.attr1.attr2, _MockObject) + assert isinstance(obj3.attr1.attr2.meth(), _MockObject) + + # check that Generic Subscriptions still works + + class GenericSubclass(GenericClass[mock.MockedClass, S]): + """docstring of GenericSubclass""" + def test_mock() -> None: modname = 'sphinx.unknown'