From ec461b940286a296465df9368ee79e1ad56f0f5c Mon Sep 17 00:00:00 2001 From: Adam Turner <9087854+aa-turner@users.noreply.github.com> Date: Wed, 12 Nov 2025 03:11:41 +0000 Subject: [PATCH] Introduce ``sphinx.ext.autodoc._dynamic`` subpackage --- sphinx/ext/autodoc/__init__.py | 10 ++--- sphinx/ext/autodoc/_dynamic/__init__.py | 1 + .../ext/autodoc/{ => _dynamic}/_docstrings.py | 2 +- .../ext/autodoc/{ => _dynamic}/_importer.py | 2 +- sphinx/ext/autodoc/{ => _dynamic}/_loader.py | 18 ++++---- .../autodoc/{ => _dynamic}/_member_finder.py | 4 +- .../autodoc/{mock.py => _dynamic/_mock.py} | 0 .../_preserve_defaults.py} | 0 .../ext/autodoc/{ => _dynamic}/_signatures.py | 8 ++-- .../ext/autodoc/_dynamic/_type_annotations.py | 45 +++++++++++++++++++ .../autodoc/{ => _dynamic}/_type_comments.py | 0 sphinx/ext/autodoc/_generate.py | 6 +-- sphinx/ext/autodoc/typehints.py | 39 +--------------- sphinx/ext/autosummary/__init__.py | 8 ++-- sphinx/ext/autosummary/generate.py | 6 +-- sphinx/util/typing.py | 4 +- tests/test_ext_autodoc/test_ext_autodoc.py | 2 +- .../test_ext_autodoc_automodule.py | 2 +- .../test_ext_autodoc_importer.py | 2 +- .../test_ext_autodoc/test_ext_autodoc_mock.py | 8 +++- .../test_ext_autodoc_signatures.py | 4 +- tests/test_util/test_util_typing.py | 2 +- 22 files changed, 93 insertions(+), 80 deletions(-) create mode 100644 sphinx/ext/autodoc/_dynamic/__init__.py rename sphinx/ext/autodoc/{ => _dynamic}/_docstrings.py (99%) rename sphinx/ext/autodoc/{ => _dynamic}/_importer.py (99%) rename sphinx/ext/autodoc/{ => _dynamic}/_loader.py (97%) rename sphinx/ext/autodoc/{ => _dynamic}/_member_finder.py (99%) rename sphinx/ext/autodoc/{mock.py => _dynamic/_mock.py} (100%) rename sphinx/ext/autodoc/{preserve_defaults.py => _dynamic/_preserve_defaults.py} (100%) rename sphinx/ext/autodoc/{ => _dynamic}/_signatures.py (98%) create mode 100644 sphinx/ext/autodoc/_dynamic/_type_annotations.py rename sphinx/ext/autodoc/{ => _dynamic}/_type_comments.py (100%) diff --git a/sphinx/ext/autodoc/__init__.py b/sphinx/ext/autodoc/__init__.py index 31d6f8e6647..ca73e8c4c78 100644 --- a/sphinx/ext/autodoc/__init__.py +++ b/sphinx/ext/autodoc/__init__.py @@ -23,16 +23,12 @@ members_option, merge_members_option, ) +from sphinx.ext.autodoc._dynamic._member_finder import ObjectMember, special_member_re from sphinx.ext.autodoc._event_listeners import between, cut_lines -from sphinx.ext.autodoc._member_finder import ObjectMember, special_member_re from sphinx.ext.autodoc._names import py_ext_sig_re from sphinx.ext.autodoc._sentinels import ALL, EMPTY, SUPPRESS, UNINITIALIZED_ATTR -from sphinx.ext.autodoc._sentinels import ( - INSTANCE_ATTR as INSTANCEATTR, -) -from sphinx.ext.autodoc._sentinels import ( - SLOTS_ATTR as SLOTSATTR, -) +from sphinx.ext.autodoc._sentinels import INSTANCE_ATTR as INSTANCEATTR +from sphinx.ext.autodoc._sentinels import SLOTS_ATTR as SLOTSATTR from sphinx.ext.autodoc.directive import AutodocDirective from sphinx.ext.autodoc.typehints import _merge_typehints diff --git a/sphinx/ext/autodoc/_dynamic/__init__.py b/sphinx/ext/autodoc/_dynamic/__init__.py new file mode 100644 index 00000000000..6a6433ae842 --- /dev/null +++ b/sphinx/ext/autodoc/_dynamic/__init__.py @@ -0,0 +1 @@ +"""The dynamic (import-based) backend for autodoc.""" diff --git a/sphinx/ext/autodoc/_docstrings.py b/sphinx/ext/autodoc/_dynamic/_docstrings.py similarity index 99% rename from sphinx/ext/autodoc/_docstrings.py rename to sphinx/ext/autodoc/_dynamic/_docstrings.py index a1b924dd43a..439df8ad50f 100644 --- a/sphinx/ext/autodoc/_docstrings.py +++ b/sphinx/ext/autodoc/_dynamic/_docstrings.py @@ -3,7 +3,7 @@ from typing import TYPE_CHECKING, TypeVar from sphinx.errors import PycodeError -from sphinx.ext.autodoc._importer import ( +from sphinx.ext.autodoc._dynamic._importer import ( _get_attribute_comment, _is_runtime_instance_attribute_not_commented, ) diff --git a/sphinx/ext/autodoc/_importer.py b/sphinx/ext/autodoc/_dynamic/_importer.py similarity index 99% rename from sphinx/ext/autodoc/_importer.py rename to sphinx/ext/autodoc/_dynamic/_importer.py index a106b49f325..3bbe6424fb4 100644 --- a/sphinx/ext/autodoc/_importer.py +++ b/sphinx/ext/autodoc/_dynamic/_importer.py @@ -15,8 +15,8 @@ from typing import TYPE_CHECKING from sphinx.errors import PycodeError +from sphinx.ext.autodoc._dynamic._mock import ismock, mock, undecorate from sphinx.ext.autodoc._sentinels import RUNTIME_INSTANCE_ATTRIBUTE, UNINITIALIZED_ATTR -from sphinx.ext.autodoc.mock import ismock, mock, undecorate from sphinx.pycode import ModuleAnalyzer from sphinx.util import inspect, logging from sphinx.util.inspect import isclass, safe_getattr diff --git a/sphinx/ext/autodoc/_loader.py b/sphinx/ext/autodoc/_dynamic/_loader.py similarity index 97% rename from sphinx/ext/autodoc/_loader.py rename to sphinx/ext/autodoc/_dynamic/_loader.py index 246293855fb..6831b80d413 100644 --- a/sphinx/ext/autodoc/_loader.py +++ b/sphinx/ext/autodoc/_dynamic/_loader.py @@ -8,11 +8,17 @@ from types import SimpleNamespace from typing import TYPE_CHECKING, NewType, TypeVar -from sphinx.ext.autodoc._docstrings import ( +from sphinx.ext.autodoc._dynamic._docstrings import ( _docstring_lines_for_props, _get_docstring_lines, ) -from sphinx.ext.autodoc._importer import _import_object +from sphinx.ext.autodoc._dynamic._importer import _import_object +from sphinx.ext.autodoc._dynamic._mock import ismock +from sphinx.ext.autodoc._dynamic._signatures import _format_signatures +from sphinx.ext.autodoc._dynamic._type_comments import ( + _ensure_annotations_from_type_comments, + _update_annotations_using_type_comments, +) from sphinx.ext.autodoc._names import _parse_name from sphinx.ext.autodoc._property_types import ( _AssignStatementProperties, @@ -28,12 +34,6 @@ UNINITIALIZED_ATTR, ) from sphinx.ext.autodoc._shared import _get_render_mode -from sphinx.ext.autodoc._signatures import _format_signatures -from sphinx.ext.autodoc._type_comments import ( - _ensure_annotations_from_type_comments, - _update_annotations_using_type_comments, -) -from sphinx.ext.autodoc.mock import ismock from sphinx.locale import __ from sphinx.util import inspect, logging from sphinx.util.inspect import safe_getattr @@ -46,7 +46,7 @@ from sphinx.environment import _CurrentDocument from sphinx.events import EventManager from sphinx.ext.autodoc._directive_options import _AutoDocumenterOptions - from sphinx.ext.autodoc._importer import _ImportedObject + from sphinx.ext.autodoc._dynamic._importer import _ImportedObject from sphinx.ext.autodoc._property_types import _AutodocFuncProperty, _AutodocObjType from sphinx.ext.autodoc._shared import _AttrGetter, _AutodocConfig diff --git a/sphinx/ext/autodoc/_member_finder.py b/sphinx/ext/autodoc/_dynamic/_member_finder.py similarity index 99% rename from sphinx/ext/autodoc/_member_finder.py rename to sphinx/ext/autodoc/_dynamic/_member_finder.py index 8130ee5b7c0..4d46d84b4b0 100644 --- a/sphinx/ext/autodoc/_member_finder.py +++ b/sphinx/ext/autodoc/_dynamic/_member_finder.py @@ -8,10 +8,10 @@ from sphinx.errors import PycodeError from sphinx.events import EventManager from sphinx.ext.autodoc._directive_options import _AutoDocumenterOptions -from sphinx.ext.autodoc._loader import _load_object_by_name +from sphinx.ext.autodoc._dynamic._loader import _load_object_by_name +from sphinx.ext.autodoc._dynamic._mock import ismock, undecorate from sphinx.ext.autodoc._property_types import _ClassDefProperties, _ModuleProperties from sphinx.ext.autodoc._sentinels import ALL, INSTANCE_ATTR, SLOTS_ATTR -from sphinx.ext.autodoc.mock import ismock, undecorate from sphinx.locale import __ from sphinx.pycode import ModuleAnalyzer from sphinx.util import inspect, logging diff --git a/sphinx/ext/autodoc/mock.py b/sphinx/ext/autodoc/_dynamic/_mock.py similarity index 100% rename from sphinx/ext/autodoc/mock.py rename to sphinx/ext/autodoc/_dynamic/_mock.py diff --git a/sphinx/ext/autodoc/preserve_defaults.py b/sphinx/ext/autodoc/_dynamic/_preserve_defaults.py similarity index 100% rename from sphinx/ext/autodoc/preserve_defaults.py rename to sphinx/ext/autodoc/_dynamic/_preserve_defaults.py diff --git a/sphinx/ext/autodoc/_signatures.py b/sphinx/ext/autodoc/_dynamic/_signatures.py similarity index 98% rename from sphinx/ext/autodoc/_signatures.py rename to sphinx/ext/autodoc/_dynamic/_signatures.py index 0dcf669b09c..e4928b2460a 100644 --- a/sphinx/ext/autodoc/_signatures.py +++ b/sphinx/ext/autodoc/_dynamic/_signatures.py @@ -7,11 +7,13 @@ from typing import TYPE_CHECKING, NewType, TypeVar from sphinx.errors import PycodeError +from sphinx.ext.autodoc._dynamic._preserve_defaults import update_default_value +from sphinx.ext.autodoc._dynamic._type_annotations import _record_typehints +from sphinx.ext.autodoc._dynamic._type_comments import ( + _update_annotations_using_type_comments, +) from sphinx.ext.autodoc._names import py_ext_sig_re from sphinx.ext.autodoc._property_types import _AssignStatementProperties -from sphinx.ext.autodoc._type_comments import _update_annotations_using_type_comments -from sphinx.ext.autodoc.preserve_defaults import update_default_value -from sphinx.ext.autodoc.typehints import _record_typehints from sphinx.locale import __ from sphinx.pycode import ModuleAnalyzer from sphinx.util import inspect, logging diff --git a/sphinx/ext/autodoc/_dynamic/_type_annotations.py b/sphinx/ext/autodoc/_dynamic/_type_annotations.py new file mode 100644 index 00000000000..4772a4c2795 --- /dev/null +++ b/sphinx/ext/autodoc/_dynamic/_type_annotations.py @@ -0,0 +1,45 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING + +from sphinx.util import inspect +from sphinx.util.typing import stringify_annotation + +if TYPE_CHECKING: + from collections.abc import Mapping + from typing import Any + + from sphinx.util.typing import _StringifyMode + + +def _record_typehints( + *, + autodoc_annotations: dict[str, dict[str, str]], + name: str, + obj: Any, + short_literals: bool, + type_aliases: Mapping[str, str] | None, + unqualified_typehints: bool, +) -> None: + """Record type hints to env object.""" + mode: _StringifyMode + if unqualified_typehints: + mode = 'smart' + else: + mode = 'fully-qualified' + + try: + if callable(obj): + annotation = autodoc_annotations.setdefault(name, {}) + sig = inspect.signature(obj, type_aliases=type_aliases) + for param in sig.parameters.values(): + if param.annotation is not param.empty: + annotation[param.name] = stringify_annotation( + param.annotation, mode, short_literals=short_literals + ) + if sig.return_annotation is not sig.empty: + annotation['return'] = stringify_annotation( + sig.return_annotation, mode, short_literals=short_literals + ) + except (TypeError, ValueError): + pass diff --git a/sphinx/ext/autodoc/_type_comments.py b/sphinx/ext/autodoc/_dynamic/_type_comments.py similarity index 100% rename from sphinx/ext/autodoc/_type_comments.py rename to sphinx/ext/autodoc/_dynamic/_type_comments.py diff --git a/sphinx/ext/autodoc/_generate.py b/sphinx/ext/autodoc/_generate.py index d416c694b7b..79eb31d6bb3 100644 --- a/sphinx/ext/autodoc/_generate.py +++ b/sphinx/ext/autodoc/_generate.py @@ -6,12 +6,12 @@ from docutils.statemachine import StringList from sphinx.errors import PycodeError -from sphinx.ext.autodoc._loader import _load_object_by_name -from sphinx.ext.autodoc._member_finder import _gather_members +from sphinx.ext.autodoc._dynamic._loader import _load_object_by_name +from sphinx.ext.autodoc._dynamic._member_finder import _gather_members +from sphinx.ext.autodoc._dynamic._mock import ismock from sphinx.ext.autodoc._renderer import _add_content, _directive_header_lines from sphinx.ext.autodoc._sentinels import ALL from sphinx.ext.autodoc._shared import _get_render_mode -from sphinx.ext.autodoc.mock import ismock from sphinx.locale import _, __ from sphinx.pycode import ModuleAnalyzer from sphinx.util import inspect, logging diff --git a/sphinx/ext/autodoc/typehints.py b/sphinx/ext/autodoc/typehints.py index 2b3c3e48e3c..46e11d96bf5 100644 --- a/sphinx/ext/autodoc/typehints.py +++ b/sphinx/ext/autodoc/typehints.py @@ -8,51 +8,14 @@ from docutils import nodes from sphinx import addnodes -from sphinx.util import inspect -from sphinx.util.typing import stringify_annotation if TYPE_CHECKING: - from collections.abc import Iterable, Mapping - from typing import Any + from collections.abc import Iterable from docutils.nodes import Element from sphinx.application import Sphinx from sphinx.ext.autodoc._property_types import _AutodocObjType - from sphinx.util.typing import _StringifyMode - - -def _record_typehints( - *, - autodoc_annotations: dict[str, dict[str, str]], - name: str, - obj: Any, - short_literals: bool, - type_aliases: Mapping[str, str] | None, - unqualified_typehints: bool, -) -> None: - """Record type hints to env object.""" - mode: _StringifyMode - if unqualified_typehints: - mode = 'smart' - else: - mode = 'fully-qualified' - - try: - if callable(obj): - annotation = autodoc_annotations.setdefault(name, {}) - sig = inspect.signature(obj, type_aliases=type_aliases) - for param in sig.parameters.values(): - if param.annotation is not param.empty: - annotation[param.name] = stringify_annotation( - param.annotation, mode, short_literals=short_literals - ) - if sig.return_annotation is not sig.empty: - annotation['return'] = stringify_annotation( - sig.return_annotation, mode, short_literals=short_literals - ) - except (TypeError, ValueError): - pass def _merge_typehints( diff --git a/sphinx/ext/autosummary/__init__.py b/sphinx/ext/autosummary/__init__.py index d4b822d46ff..c3bd7dd4b05 100644 --- a/sphinx/ext/autosummary/__init__.py +++ b/sphinx/ext/autosummary/__init__.py @@ -67,12 +67,12 @@ from sphinx import addnodes from sphinx.errors import PycodeError from sphinx.ext.autodoc._directive_options import _AutoDocumenterOptions -from sphinx.ext.autodoc._importer import _import_module -from sphinx.ext.autodoc._loader import _load_object_by_name -from sphinx.ext.autodoc._member_finder import _best_object_type_for_member +from sphinx.ext.autodoc._dynamic._importer import _import_module +from sphinx.ext.autodoc._dynamic._loader import _load_object_by_name +from sphinx.ext.autodoc._dynamic._member_finder import _best_object_type_for_member +from sphinx.ext.autodoc._dynamic._mock import mock from sphinx.ext.autodoc._sentinels import INSTANCE_ATTR from sphinx.ext.autodoc._shared import _AutodocAttrGetter, _AutodocConfig -from sphinx.ext.autodoc.mock import mock from sphinx.locale import __ from sphinx.pycode import ModuleAnalyzer from sphinx.util import logging, rst diff --git a/sphinx/ext/autosummary/generate.py b/sphinx/ext/autosummary/generate.py index 1e8055bf73e..aec02012ddf 100644 --- a/sphinx/ext/autosummary/generate.py +++ b/sphinx/ext/autosummary/generate.py @@ -33,10 +33,10 @@ from sphinx.builders import Builder from sphinx.config import Config from sphinx.errors import PycodeError -from sphinx.ext.autodoc._importer import _import_module -from sphinx.ext.autodoc._member_finder import _filter_enum_dict, unmangle +from sphinx.ext.autodoc._dynamic._importer import _import_module +from sphinx.ext.autodoc._dynamic._member_finder import _filter_enum_dict, unmangle +from sphinx.ext.autodoc._dynamic._mock import ismock, undecorate from sphinx.ext.autodoc._sentinels import INSTANCE_ATTR, SLOTS_ATTR -from sphinx.ext.autodoc.mock import ismock, undecorate from sphinx.ext.autosummary import ( ImportExceptionGroup, _get_documenter, diff --git a/sphinx/util/typing.py b/sphinx/util/typing.py index cedffb46a02..76feaf78b38 100644 --- a/sphinx/util/typing.py +++ b/sphinx/util/typing.py @@ -253,7 +253,7 @@ def restify(cls: Any, mode: _RestifyMode = 'fully-qualified-except-typing') -> s 'smart' Show the name of the annotation. """ - from sphinx.ext.autodoc.mock import ismock, ismockmodule # lazy loading + from sphinx.ext.autodoc._dynamic._mock import ismock, ismockmodule # lazy loading from sphinx.util.inspect import isgenericalias, object_description # lazy loading valid_modes = {'fully-qualified-except-typing', 'smart'} @@ -425,7 +425,7 @@ def stringify_annotation( :param short_literals: Render :py:class:`Literals` in PEP 604 style (``|``). """ - from sphinx.ext.autodoc.mock import ismock, ismockmodule # lazy loading + from sphinx.ext.autodoc._dynamic._mock import ismock, ismockmodule # lazy loading valid_modes = {'fully-qualified-except-typing', 'fully-qualified', 'smart'} if mode not in valid_modes: diff --git a/tests/test_ext_autodoc/test_ext_autodoc.py b/tests/test_ext_autodoc/test_ext_autodoc.py index 3e4691c9978..6c73627fda1 100644 --- a/tests/test_ext_autodoc/test_ext_autodoc.py +++ b/tests/test_ext_autodoc/test_ext_autodoc.py @@ -20,8 +20,8 @@ _AutoDocumenterOptions, inherited_members_option, ) -from sphinx.ext.autodoc._docstrings import _get_docstring_lines from sphinx.ext.autodoc._documenters import Documenter +from sphinx.ext.autodoc._dynamic._docstrings import _get_docstring_lines from sphinx.ext.autodoc._generate import _auto_document_object from sphinx.ext.autodoc._property_types import _ItemProperties from sphinx.ext.autodoc._sentinels import ALL diff --git a/tests/test_ext_autodoc/test_ext_autodoc_automodule.py b/tests/test_ext_autodoc/test_ext_autodoc_automodule.py index 745447f8f1f..6fad349a134 100644 --- a/tests/test_ext_autodoc/test_ext_autodoc_automodule.py +++ b/tests/test_ext_autodoc/test_ext_autodoc_automodule.py @@ -12,8 +12,8 @@ import pytest +from sphinx.ext.autodoc._dynamic._mock import _MockObject from sphinx.ext.autodoc._shared import _AutodocConfig -from sphinx.ext.autodoc.mock import _MockObject from tests.test_ext_autodoc.autodoc_util import do_autodoc diff --git a/tests/test_ext_autodoc/test_ext_autodoc_importer.py b/tests/test_ext_autodoc/test_ext_autodoc_importer.py index e61fd8b6594..8142af0282d 100644 --- a/tests/test_ext_autodoc/test_ext_autodoc_importer.py +++ b/tests/test_ext_autodoc/test_ext_autodoc_importer.py @@ -3,7 +3,7 @@ import sys from pathlib import Path -from sphinx.ext.autodoc._importer import _import_module +from sphinx.ext.autodoc._dynamic._importer import _import_module def test_import_native_module_stubs(rootdir: Path) -> None: diff --git a/tests/test_ext_autodoc/test_ext_autodoc_mock.py b/tests/test_ext_autodoc/test_ext_autodoc_mock.py index b2a0e917bee..c0b8275cce5 100644 --- a/tests/test_ext_autodoc/test_ext_autodoc_mock.py +++ b/tests/test_ext_autodoc/test_ext_autodoc_mock.py @@ -9,7 +9,13 @@ import pytest -from sphinx.ext.autodoc.mock import _MockModule, _MockObject, ismock, mock, undecorate +from sphinx.ext.autodoc._dynamic._mock import ( + _MockModule, + _MockObject, + ismock, + mock, + undecorate, +) def test_MockModule() -> None: diff --git a/tests/test_ext_autodoc/test_ext_autodoc_signatures.py b/tests/test_ext_autodoc/test_ext_autodoc_signatures.py index 9404d41386f..6da388c3a4c 100644 --- a/tests/test_ext_autodoc/test_ext_autodoc_signatures.py +++ b/tests/test_ext_autodoc/test_ext_autodoc_signatures.py @@ -8,13 +8,13 @@ import pytest from sphinx.ext.autodoc._directive_options import _AutoDocumenterOptions -from sphinx.ext.autodoc._docstrings import _get_docstring_lines +from sphinx.ext.autodoc._dynamic._docstrings import _get_docstring_lines +from sphinx.ext.autodoc._dynamic._signatures import _format_signatures from sphinx.ext.autodoc._property_types import ( _ClassDefProperties, _FunctionDefProperties, ) from sphinx.ext.autodoc._shared import _AutodocConfig -from sphinx.ext.autodoc._signatures import _format_signatures from sphinx.util.inspect import safe_getattr from tests.test_ext_autodoc.autodoc_util import FakeEvents diff --git a/tests/test_util/test_util_typing.py b/tests/test_util/test_util_typing.py index 8a561c378ed..2d25a387521 100644 --- a/tests/test_util/test_util_typing.py +++ b/tests/test_util/test_util_typing.py @@ -75,7 +75,7 @@ ) from weakref import WeakSet -from sphinx.ext.autodoc.mock import mock +from sphinx.ext.autodoc._dynamic._mock import mock from sphinx.util.typing import _INVALID_BUILTIN_CLASSES, restify, stringify_annotation