From c1b2711b0e5e54c5f3c10bc2e48adf25c65e3923 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Sun, 14 Apr 2024 08:48:43 +0200 Subject: [PATCH 1/4] cleanup --- .ruff.toml | 1 - pyproject.toml | 2 -- sphinx/ext/autodoc/mock.py | 42 ++++++++++++++++++++++++++------------ 3 files changed, 29 insertions(+), 16 deletions(-) diff --git a/.ruff.toml b/.ruff.toml index 55ad44b1dad..a06dfcd812d 100644 --- a/.ruff.toml +++ b/.ruff.toml @@ -518,7 +518,6 @@ exclude = [ "sphinx/ext/autodoc/directive.py", "sphinx/ext/autodoc/importer.py", "sphinx/ext/autodoc/type_comment.py", - "sphinx/ext/autodoc/mock.py", "sphinx/ext/autosummary/__init__.py", "sphinx/ext/autosummary/generate.py", "sphinx/pycode/ast.py", diff --git a/pyproject.toml b/pyproject.toml index 027504bc37f..e4b85530c60 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -258,8 +258,6 @@ module = [ "sphinx.ext.autodoc", "sphinx.ext.autodoc.directive", "sphinx.ext.autodoc.importer", - "sphinx.ext.autodoc.mock", - "sphinx.ext.autodoc.mock", "sphinx.ext.autosummary.generate", "sphinx.ext.doctest", "sphinx.ext.graphviz", diff --git a/sphinx/ext/autodoc/mock.py b/sphinx/ext/autodoc/mock.py index c2ab0feb442..0cfdf281f28 100644 --- a/sphinx/ext/autodoc/mock.py +++ b/sphinx/ext/autodoc/mock.py @@ -8,13 +8,14 @@ from importlib.abc import Loader, MetaPathFinder from importlib.machinery import ModuleSpec from types import MethodType, ModuleType -from typing import TYPE_CHECKING, Any +from typing import TYPE_CHECKING from sphinx.util import logging from sphinx.util.inspect import isboundmethod, safe_getattr if TYPE_CHECKING: from collections.abc import Iterator, Sequence + from typing import Any logger = logging.getLogger(__name__) @@ -32,8 +33,12 @@ def __new__(cls, *args: Any, **kwargs: Any) -> Any: superclass = args[1][-1].__class__ if superclass is cls: # subclassing MockObject - return _make_subclass(args[0], superclass.__display_name__, - superclass=superclass, attributes=args[2]) + return _make_subclass( + args[0], + superclass.__display_name__, + superclass=superclass, + attributes=args[2], + ) return super().__new__(cls) @@ -46,10 +51,10 @@ def __len__(self) -> int: def __contains__(self, key: str) -> bool: return False - def __iter__(self) -> Iterator: + def __iter__(self) -> Iterator[Any]: return iter([]) - def __mro_entries__(self, bases: tuple) -> tuple: + def __mro_entries__(self, bases: tuple[Any, ...]) -> tuple[type[Any], ...]: return (self.__class__,) def __getitem__(self, key: Any) -> _MockObject: @@ -67,12 +72,19 @@ def __repr__(self) -> str: return self.__display_name__ -def _make_subclass(name: str, module: str, superclass: Any = _MockObject, - attributes: Any = None, decorator_args: tuple = ()) -> Any: - attrs = {'__module__': module, - '__display_name__': module + '.' + name, - '__name__': name, - '__sphinx_decorator_args__': decorator_args} +def _make_subclass( + name: str, + module: str, + superclass: Any = _MockObject, + attributes: Any = None, + decorator_args: tuple[Any, ...] = (), +) -> Any: + attrs = { + '__module__': module, + '__display_name__': module + '.' + name, + '__name__': name, + '__sphinx_decorator_args__': decorator_args, + } attrs.update(attributes or {}) return type(name, (superclass,), attrs) @@ -121,8 +133,12 @@ def __init__(self, modnames: list[str]) -> None: self.loader = MockLoader(self) self.mocked_modules: list[str] = [] - def find_spec(self, fullname: str, path: Sequence[bytes | str] | None, - target: ModuleType | None = None) -> ModuleSpec | None: + def find_spec( + self, + fullname: str, + path: Sequence[bytes | str] | None, + target: ModuleType | None = None, + ) -> ModuleSpec | None: for modname in self.modnames: # check if fullname is (or is a descendant of) one of our targets if modname == fullname or fullname.startswith(modname + '.'): From e4c915d8d2c42acc8bf6ecd5d45f4e5a533b592c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Sun, 14 Apr 2024 08:48:55 +0200 Subject: [PATCH 2/4] fix test --- .../test_ext_autodoc_automodule.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/tests/test_extensions/test_ext_autodoc_automodule.py b/tests/test_extensions/test_ext_autodoc_automodule.py index 92565aef058..c6ced7eebb1 100644 --- a/tests/test_extensions/test_ext_autodoc_automodule.py +++ b/tests/test_extensions/test_ext_autodoc_automodule.py @@ -4,7 +4,9 @@ source file translated by test_build. """ +import inspect import sys +import typing import pytest @@ -185,8 +187,22 @@ def test_automodule_inherited_members(app): 'sphinx.missing_module4']}) @pytest.mark.usefixtures("rollback_sysmodules") def test_subclass_of_mocked_object(app): + from sphinx.ext.autodoc.mock import _MockObject sys.modules.pop('target', None) # unload target module to clear the module cache + options = {'members': None} + actual = do_autodoc(app, 'module', 'target.need_mocks', options) + # ``typing.Any`` is not available at runtime on ``_MockObject.__new__`` + assert '.. py:class:: Inherited(*args: Any, **kwargs: Any)' in actual + + # make ``typing.Any`` available at runtime on ``_MockObject.__new__`` + sig = inspect.signature(_MockObject.__new__) + parameters = sig.parameters.copy() + for name in ('args', 'kwargs'): + parameters[name] = parameters[name].replace(annotation=typing.Any) + sig = sig.replace(parameters=tuple(parameters.values())) + _MockObject.__new__.__signature__ = sig # type: ignore[attr-defined] + options = {'members': None} actual = do_autodoc(app, 'module', 'target.need_mocks', options) assert '.. py:class:: Inherited(*args: ~typing.Any, **kwargs: ~typing.Any)' in actual From e2b1eeff3c19672d8aeb52f3e91f39c022b69021 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Sun, 14 Apr 2024 11:07:38 +0200 Subject: [PATCH 3/4] cleanup --- sphinx/ext/autodoc/mock.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sphinx/ext/autodoc/mock.py b/sphinx/ext/autodoc/mock.py index 0cfdf281f28..6767937d978 100644 --- a/sphinx/ext/autodoc/mock.py +++ b/sphinx/ext/autodoc/mock.py @@ -54,7 +54,7 @@ def __contains__(self, key: str) -> bool: def __iter__(self) -> Iterator[Any]: return iter([]) - def __mro_entries__(self, bases: tuple[Any, ...]) -> tuple[type[Any], ...]: + def __mro_entries__(self, bases: tuple[Any, ...]) -> tuple[type, ...]: return (self.__class__,) def __getitem__(self, key: Any) -> _MockObject: @@ -160,8 +160,8 @@ def mock(modnames: list[str]) -> Iterator[None]: # mock modules are enabled here ... """ + finder = MockFinder(modnames) try: - finder = MockFinder(modnames) sys.meta_path.insert(0, finder) yield finally: From 3d2c82c0baeb12fab29beb3f8d20be4892a93e25 Mon Sep 17 00:00:00 2001 From: Adam Turner <9087854+aa-turner@users.noreply.github.com> Date: Mon, 22 Apr 2024 23:19:44 +0100 Subject: [PATCH 4/4] revert superfluous --- .ruff.toml | 1 + sphinx/ext/autodoc/mock.py | 37 +++++++++++-------------------------- 2 files changed, 12 insertions(+), 26 deletions(-) diff --git a/.ruff.toml b/.ruff.toml index f82ba9b5083..4a19e8ed4e4 100644 --- a/.ruff.toml +++ b/.ruff.toml @@ -525,6 +525,7 @@ exclude = [ "sphinx/ext/autodoc/directive.py", "sphinx/ext/autodoc/importer.py", "sphinx/ext/autodoc/type_comment.py", + "sphinx/ext/autodoc/mock.py", "sphinx/ext/autosummary/__init__.py", "sphinx/ext/autosummary/generate.py", "sphinx/pycode/ast.py", diff --git a/sphinx/ext/autodoc/mock.py b/sphinx/ext/autodoc/mock.py index 6767937d978..f17c3302cb6 100644 --- a/sphinx/ext/autodoc/mock.py +++ b/sphinx/ext/autodoc/mock.py @@ -33,12 +33,8 @@ def __new__(cls, *args: Any, **kwargs: Any) -> Any: superclass = args[1][-1].__class__ if superclass is cls: # subclassing MockObject - return _make_subclass( - args[0], - superclass.__display_name__, - superclass=superclass, - attributes=args[2], - ) + return _make_subclass(args[0], superclass.__display_name__, + superclass=superclass, attributes=args[2]) return super().__new__(cls) @@ -52,7 +48,7 @@ def __contains__(self, key: str) -> bool: return False def __iter__(self) -> Iterator[Any]: - return iter([]) + return iter(()) def __mro_entries__(self, bases: tuple[Any, ...]) -> tuple[type, ...]: return (self.__class__,) @@ -72,19 +68,12 @@ def __repr__(self) -> str: return self.__display_name__ -def _make_subclass( - name: str, - module: str, - superclass: Any = _MockObject, - attributes: Any = None, - decorator_args: tuple[Any, ...] = (), -) -> Any: - attrs = { - '__module__': module, - '__display_name__': module + '.' + name, - '__name__': name, - '__sphinx_decorator_args__': decorator_args, - } +def _make_subclass(name: str, module: str, superclass: Any = _MockObject, + attributes: Any = None, decorator_args: tuple[Any, ...] = ()) -> Any: + attrs = {'__module__': module, + '__display_name__': module + '.' + name, + '__name__': name, + '__sphinx_decorator_args__': decorator_args} attrs.update(attributes or {}) return type(name, (superclass,), attrs) @@ -133,12 +122,8 @@ def __init__(self, modnames: list[str]) -> None: self.loader = MockLoader(self) self.mocked_modules: list[str] = [] - def find_spec( - self, - fullname: str, - path: Sequence[bytes | str] | None, - target: ModuleType | None = None, - ) -> ModuleSpec | None: + def find_spec(self, fullname: str, path: Sequence[bytes | str] | None, + target: ModuleType | None = None) -> ModuleSpec | None: for modname in self.modnames: # check if fullname is (or is a descendant of) one of our targets if modname == fullname or fullname.startswith(modname + '.'):