diff --git a/sphinx/ext/autodoc/_loader.py b/sphinx/ext/autodoc/_loader.py
new file mode 100644
index 00000000000..29ae3b9b69f
--- /dev/null
+++ b/sphinx/ext/autodoc/_loader.py
@@ -0,0 +1,528 @@
+"""Object loader for autodoc"""
+
+from __future__ import annotations
+
+import re
+from inspect import Parameter
+from pathlib import Path
+from types import SimpleNamespace
+from typing import TYPE_CHECKING, NewType, TypeVar
+
+from sphinx.ext.autodoc._docstrings import (
+ _docstring_lines_for_props,
+ _get_docstring_lines,
+)
+from sphinx.ext.autodoc._names import _parse_name
+from sphinx.ext.autodoc._property_types import (
+ _AssignStatementProperties,
+ _ClassDefProperties,
+ _FunctionDefProperties,
+ _ItemProperties,
+ _ModuleProperties,
+ _TypeStatementProperties,
+)
+from sphinx.ext.autodoc._sentinels import (
+ RUNTIME_INSTANCE_ATTRIBUTE,
+ SLOTS_ATTR,
+ UNINITIALIZED_ATTR,
+)
+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.importer import _import_object
+from sphinx.ext.autodoc.mock import ismock
+from sphinx.locale import __
+from sphinx.util import inspect, logging
+from sphinx.util.inspect import safe_getattr
+from sphinx.util.typing import get_type_hints, restify, stringify_annotation
+
+if TYPE_CHECKING:
+ from collections.abc import Mapping, MutableSet, Sequence
+ from typing import Any
+
+ from sphinx.config import Config
+ from sphinx.environment import _CurrentDocument
+ from sphinx.events import EventManager
+ from sphinx.ext.autodoc._directive_options import _AutoDocumenterOptions
+ from sphinx.ext.autodoc._property_types import _AutodocFuncProperty, _AutodocObjType
+ from sphinx.ext.autodoc.importer import _AttrGetter, _ImportedObject
+
+logger = logging.getLogger(__name__)
+
+_hide_value_re = re.compile(r'^:meta \s*hide-value:( +|$)')
+
+
+def _load_object_by_name(
+ *,
+ name: str,
+ objtype: _AutodocObjType,
+ mock_imports: list[str],
+ type_aliases: dict[str, Any] | None,
+ current_document: _CurrentDocument,
+ config: Config,
+ events: EventManager,
+ get_attr: _AttrGetter,
+ options: _AutoDocumenterOptions,
+ parent_modname: str | None = None,
+ ref_context: Mapping[str, str | None],
+ reread_always: MutableSet[str],
+) -> _ItemProperties | None:
+ """Import and load the object given by *name*."""
+ parsed = _parse_name(
+ name=name,
+ objtype=objtype,
+ current_document=current_document,
+ ref_context=ref_context,
+ )
+ if parsed is None:
+ return None
+ module_name, parts, args, retann = parsed
+
+ # Import the module and get the object to document
+ im = _import_object(
+ module_name=module_name,
+ obj_path=parts,
+ mock_imports=mock_imports,
+ get_attr=get_attr,
+ obj_type=objtype,
+ type_aliases=type_aliases,
+ )
+ if im is None:
+ # See BuildEnvironment.note_reread()
+ reread_always.add(current_document.docname)
+ return None
+
+ # Assemble object properties from the imported object.
+ parent = im.parent
+ props = _make_props_from_imported_object(
+ im,
+ config=config,
+ events=events,
+ get_attr=get_attr,
+ module_name=module_name,
+ objtype=objtype,
+ parts=parts,
+ )
+ if props is None:
+ return None
+
+ if options.class_doc_from is not None:
+ class_doc_from = options.class_doc_from
+ else:
+ class_doc_from = config.autoclass_content
+
+ docstrings = _get_docstring_lines(
+ props,
+ class_doc_from=class_doc_from,
+ get_attr=get_attr,
+ inherit_docstrings=config.autodoc_inherit_docstrings,
+ parent=parent,
+ tab_width=options._tab_width,
+ )
+ if docstrings:
+ for docstring_lines in docstrings:
+ for line in docstring_lines:
+ if _hide_value_re.match(line):
+ props._docstrings_has_hide_value = True
+ break
+
+ # format the object's signature, if any
+ try:
+ signatures = _format_signatures(
+ args=args,
+ retann=retann,
+ autodoc_annotations=current_document.autodoc_annotations,
+ config=config,
+ docstrings=docstrings,
+ events=events,
+ get_attr=get_attr,
+ parent=parent,
+ options=options,
+ props=props,
+ )
+ except Exception as exc:
+ msg = __('error while formatting signature for %s: %s')
+ logger.warning(msg, props.full_name, exc, type='autodoc')
+ return None
+ props.signatures = tuple(
+ f'{args} -> {retann}' if retann else str(args) for args, retann in signatures
+ )
+
+ props.docstring_lines = _docstring_lines_for_props(
+ docstrings,
+ props=props,
+ parent_modname=parent_modname,
+ events=events,
+ options=options,
+ )
+
+ return props
+
+
+def _make_props_from_imported_object(
+ im: _ImportedObject,
+ *,
+ config: Config,
+ events: EventManager,
+ get_attr: _AttrGetter,
+ module_name: str,
+ objtype: _AutodocObjType,
+ parts: tuple[str, ...],
+) -> _ItemProperties | None:
+ parent = im.parent
+ object_name = im.object_name
+ obj = im.obj
+ obj_properties: set[_AutodocFuncProperty] = set()
+
+ if objtype == 'module':
+ try:
+ mod_origin = im.module.__spec__.origin # type: ignore[union-attr]
+ except AttributeError:
+ file_path = None
+ else:
+ file_path = Path(mod_origin) if mod_origin is not None else None
+
+ mod_all = safe_getattr(obj, '__all__', None)
+ if isinstance(mod_all, (list, tuple)) and all(
+ isinstance(e, str) for e in mod_all
+ ):
+ mod_all = tuple(mod_all)
+ elif mod_all is not None:
+ # Invalid __all__ found.
+ msg = __('Ignoring invalid __all__ in module %s: %r')
+ logger.warning(msg, module_name, mod_all, type='autodoc')
+ mod_all = None
+
+ return _ModuleProperties(
+ obj_type=objtype,
+ module_name=module_name,
+ docstring_lines=(),
+ file_path=file_path,
+ all=mod_all,
+ _obj=obj,
+ _obj___module__=obj.__name__,
+ )
+
+ if objtype in {'class', 'exception'}:
+ if isinstance(obj, (NewType, TypeVar)):
+ obj_module_name = getattr(obj, '__module__', module_name)
+ if obj_module_name != module_name and module_name.startswith(
+ obj_module_name
+ ):
+ bases = module_name[len(obj_module_name) :].strip('.').split('.')
+ parts = tuple(bases) + parts
+ module_name = obj_module_name
+
+ if orig_bases := inspect.getorigbases(obj):
+ # A subclass of generic types
+ # refs: PEP-560
+ obj_bases = list(orig_bases)
+ elif hasattr(obj, '__bases__') and obj.__bases__:
+ # A normal class
+ obj_bases = list(obj.__bases__)
+ else:
+ obj_bases = []
+ full_name = '.'.join((module_name, *parts))
+ events.emit(
+ 'autodoc-process-bases',
+ full_name,
+ obj,
+ SimpleNamespace(),
+ obj_bases,
+ )
+ if config.autodoc_typehints_format == 'short':
+ mode = 'smart'
+ else:
+ mode = 'fully-qualified-except-typing'
+ base_classes = tuple(restify(cls, mode=mode) for cls in obj_bases) # type: ignore[arg-type]
+
+ return _ClassDefProperties(
+ obj_type=objtype, # type: ignore[arg-type]
+ module_name=module_name,
+ parts=parts,
+ docstring_lines=(),
+ bases=getattr(obj, '__bases__', None),
+ _obj=obj,
+ _obj___module__=get_attr(obj, '__module__', None),
+ _obj___name__=getattr(obj, '__name__', None),
+ _obj___qualname__=getattr(obj, '__qualname__', None),
+ _obj_bases=base_classes,
+ _obj_is_new_type=isinstance(obj, NewType),
+ _obj_is_typevar=isinstance(obj, TypeVar),
+ )
+
+ if objtype in {'function', 'decorator'}:
+ if inspect.isstaticmethod(obj, cls=parent, name=object_name):
+ obj_properties.add('staticmethod')
+ if inspect.isclassmethod(obj):
+ obj_properties.add('classmethod')
+ if inspect.iscoroutinefunction(obj) or inspect.isasyncgenfunction(obj):
+ obj_properties.add('async')
+
+ return _FunctionDefProperties(
+ obj_type=objtype, # type: ignore[arg-type]
+ module_name=module_name,
+ parts=parts,
+ docstring_lines=(),
+ properties=frozenset(obj_properties),
+ _obj=obj,
+ _obj___module__=get_attr(obj, '__module__', None),
+ _obj___name__=getattr(obj, '__name__', None),
+ _obj___qualname__=getattr(obj, '__qualname__', None),
+ )
+
+ if objtype == 'method':
+ # to distinguish classmethod/staticmethod
+ obj_ = parent.__dict__.get(object_name, obj)
+ if inspect.isstaticmethod(obj_, cls=parent, name=object_name):
+ obj_properties.add('staticmethod')
+ elif (
+ inspect.is_classmethod_like(obj_)
+ or inspect.is_singledispatch_method(obj_)
+ and inspect.is_classmethod_like(obj_.func)
+ ):
+ obj_properties.add('classmethod')
+ if inspect.isabstractmethod(obj_):
+ obj_properties.add('abstractmethod')
+ if inspect.iscoroutinefunction(obj_) or inspect.isasyncgenfunction(obj_):
+ obj_properties.add('async')
+
+ return _FunctionDefProperties(
+ obj_type=objtype,
+ module_name=module_name,
+ parts=parts,
+ docstring_lines=(),
+ properties=frozenset(obj_properties),
+ _obj=obj,
+ _obj___module__=get_attr(obj, '__module__', None),
+ _obj___name__=getattr(obj, '__name__', None),
+ _obj___qualname__=getattr(obj, '__qualname__', None),
+ )
+
+ if objtype == 'property':
+ if not inspect.isproperty(obj):
+ # Support for class properties. Note: these only work on Python 3.9.
+ __dict__ = safe_getattr(parent, '__dict__', {})
+ obj = __dict__.get(parts[-1])
+ if isinstance(obj, classmethod) and inspect.isproperty(obj.__func__):
+ obj = obj.__func__
+ obj_properties.add('classmethod')
+ else:
+ return None
+ if inspect.isabstractmethod(obj):
+ obj_properties.add('abstractmethod')
+
+ # get property return type annotation
+ obj_property_type_annotation = None
+ if safe_getattr(obj, 'fget', None): # property
+ func = obj.fget # type: ignore[union-attr]
+ elif safe_getattr(obj, 'func', None): # cached_property
+ func = obj.func # type: ignore[union-attr]
+ else:
+ func = None
+ if func is not None:
+ # update the annotations of the property getter
+ if config.autodoc_use_type_comments:
+ _update_annotations_using_type_comments(func, False)
+
+ try:
+ signature = inspect.signature(
+ func, type_aliases=config.autodoc_type_aliases
+ )
+ except TypeError as exc:
+ full_name = '.'.join((module_name, *parts))
+ logger.warning(
+ __('Failed to get a function signature for %s: %s'),
+ full_name,
+ exc,
+ )
+ pass
+ except ValueError:
+ pass
+ else:
+ if config.autodoc_typehints_format == 'short':
+ mode = 'smart'
+ else:
+ mode = 'fully-qualified-except-typing'
+ if signature.return_annotation is not Parameter.empty:
+ short_literals = config.python_display_short_literal_types
+ obj_property_type_annotation = stringify_annotation(
+ signature.return_annotation,
+ mode, # type: ignore[arg-type]
+ short_literals=short_literals,
+ )
+
+ return _FunctionDefProperties(
+ obj_type=objtype,
+ module_name=module_name,
+ parts=parts,
+ docstring_lines=(),
+ properties=frozenset(obj_properties),
+ _obj=obj,
+ _obj___module__=get_attr(parent or obj, '__module__', None) or module_name,
+ _obj___name__=getattr(parent or obj, '__name__', None),
+ _obj___qualname__=getattr(parent or obj, '__qualname__', None),
+ _obj_property_type_annotation=obj_property_type_annotation,
+ )
+
+ if objtype == 'data':
+ # Update __annotations__ to support type_comment and so on
+ _ensure_annotations_from_type_comments(parent)
+
+ # obtain annotation
+ annotations = get_type_hints(
+ parent,
+ None,
+ config.autodoc_type_aliases,
+ include_extras=True,
+ )
+ if config.autodoc_typehints_format == 'short':
+ mode = 'smart'
+ else:
+ mode = 'fully-qualified-except-typing'
+ if parts[-1] in annotations:
+ short_literals = config.python_display_short_literal_types
+ type_annotation = stringify_annotation(
+ annotations[parts[-1]],
+ mode, # type: ignore[arg-type]
+ short_literals=short_literals,
+ )
+ else:
+ type_annotation = None
+
+ if (
+ obj is RUNTIME_INSTANCE_ATTRIBUTE
+ or obj is SLOTS_ATTR
+ or obj is UNINITIALIZED_ATTR
+ ):
+ obj_sentinel = obj
+ else:
+ obj_sentinel = None
+
+ return _AssignStatementProperties(
+ obj_type=objtype,
+ module_name=module_name,
+ parts=parts,
+ docstring_lines=(),
+ value=...,
+ annotation='',
+ class_var=False,
+ instance_var=False,
+ _obj=obj,
+ _obj___module__=get_attr(parent or obj, '__module__', None) or module_name,
+ _obj_is_generic_alias=inspect.isgenericalias(obj),
+ _obj_is_attribute_descriptor=inspect.isattributedescriptor(obj),
+ _obj_is_mock=ismock(obj),
+ _obj_is_sentinel=obj_sentinel,
+ _obj_repr_rst=inspect.object_description(obj),
+ _obj_type_annotation=type_annotation,
+ )
+
+ if objtype == 'attribute':
+ if _is_slots_attribute(parent=parent, obj_path=parts):
+ obj = SLOTS_ATTR
+ elif inspect.isenumattribute(obj):
+ obj = obj.value
+ if parent:
+ # Update __annotations__ to support type_comment and so on
+ _ensure_annotations_from_type_comments(parent)
+
+ # obtain annotation
+ annotations = get_type_hints(
+ parent,
+ None,
+ config.autodoc_type_aliases,
+ include_extras=True,
+ )
+ if config.autodoc_typehints_format == 'short':
+ mode = 'smart'
+ else:
+ mode = 'fully-qualified-except-typing'
+ if parts[-1] in annotations:
+ short_literals = config.python_display_short_literal_types
+ type_annotation = stringify_annotation(
+ annotations[parts[-1]],
+ mode, # type: ignore[arg-type]
+ short_literals=short_literals,
+ )
+ else:
+ type_annotation = None
+
+ if (
+ obj is RUNTIME_INSTANCE_ATTRIBUTE
+ or obj is SLOTS_ATTR
+ or obj is UNINITIALIZED_ATTR
+ ):
+ obj_sentinel = obj
+ else:
+ obj_sentinel = None
+
+ return _AssignStatementProperties(
+ obj_type=objtype,
+ module_name=module_name,
+ parts=parts,
+ docstring_lines=(),
+ value=...,
+ annotation='',
+ class_var=False,
+ instance_var=False,
+ _obj=obj,
+ _obj___module__=get_attr(obj, '__module__', None),
+ _obj_is_generic_alias=inspect.isgenericalias(obj),
+ _obj_is_attribute_descriptor=inspect.isattributedescriptor(obj),
+ _obj_is_mock=ismock(obj),
+ _obj_is_sentinel=obj_sentinel,
+ _obj_repr_rst=inspect.object_description(obj),
+ _obj_type_annotation=type_annotation,
+ )
+
+ if objtype == 'type':
+ obj_module_name = getattr(obj, '__module__', module_name)
+ if obj_module_name != module_name and module_name.startswith(obj_module_name):
+ bases = module_name[len(obj_module_name) :].strip('.').split('.')
+ parts = tuple(bases) + parts
+ module_name = obj_module_name
+
+ if config.autodoc_typehints_format == 'short':
+ mode = 'smart'
+ else:
+ mode = 'fully-qualified-except-typing'
+ short_literals = config.python_display_short_literal_types
+ ann = stringify_annotation(
+ obj.__value__,
+ mode, # type: ignore[arg-type]
+ short_literals=short_literals,
+ )
+ return _TypeStatementProperties(
+ obj_type=objtype,
+ module_name=module_name,
+ parts=parts,
+ docstring_lines=(),
+ _obj=obj,
+ _obj___module__=get_attr(obj, '__module__', None),
+ _obj___name__=getattr(obj, '__name__', None),
+ _obj___qualname__=getattr(obj, '__qualname__', None),
+ _obj___value__=ann,
+ )
+
+ return _ItemProperties(
+ obj_type=objtype,
+ module_name=module_name,
+ parts=parts,
+ docstring_lines=(),
+ _obj=obj,
+ _obj___module__=get_attr(obj, '__module__', None),
+ )
+
+
+def _is_slots_attribute(*, parent: Any, obj_path: Sequence[str]) -> bool:
+ """Check the subject is an attribute in __slots__."""
+ try:
+ if parent___slots__ := inspect.getslots(parent):
+ return obj_path[-1] in parent___slots__
+ else:
+ return False
+ except (ValueError, TypeError):
+ return False
diff --git a/sphinx/ext/autodoc/_member_finder.py b/sphinx/ext/autodoc/_member_finder.py
index ad6b5fab5ed..db7bb1137e0 100644
--- a/sphinx/ext/autodoc/_member_finder.py
+++ b/sphinx/ext/autodoc/_member_finder.py
@@ -8,9 +8,9 @@
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._property_types import _ClassDefProperties, _ModuleProperties
from sphinx.ext.autodoc._sentinels import ALL, INSTANCE_ATTR, SLOTS_ATTR
-from sphinx.ext.autodoc.importer import _load_object_by_name
from sphinx.ext.autodoc.mock import ismock, undecorate
from sphinx.locale import __
from sphinx.pycode import ModuleAnalyzer
diff --git a/sphinx/ext/autodoc/directive.py b/sphinx/ext/autodoc/directive.py
index 9b296783e92..90e7fc5da7b 100644
--- a/sphinx/ext/autodoc/directive.py
+++ b/sphinx/ext/autodoc/directive.py
@@ -9,7 +9,7 @@
_process_documenter_options,
)
from sphinx.ext.autodoc._generate import _generate_directives
-from sphinx.ext.autodoc.importer import _load_object_by_name
+from sphinx.ext.autodoc._loader import _load_object_by_name
from sphinx.util import logging
from sphinx.util.docutils import SphinxDirective, switch_source_input
from sphinx.util.inspect import safe_getattr
diff --git a/sphinx/ext/autodoc/importer.py b/sphinx/ext/autodoc/importer.py
index 7712dffb671..6ee073428be 100644
--- a/sphinx/ext/autodoc/importer.py
+++ b/sphinx/ext/autodoc/importer.py
@@ -5,63 +5,33 @@
import contextlib
import importlib
import os
-import re
import sys
import traceback
import typing
from importlib.abc import FileLoader
from importlib.machinery import EXTENSION_SUFFIXES
from importlib.util import decode_source, find_spec, module_from_spec, spec_from_loader
-from inspect import Parameter
from pathlib import Path
-from types import SimpleNamespace
-from typing import TYPE_CHECKING, NewType, TypeVar
+from typing import TYPE_CHECKING
from sphinx.errors import PycodeError
-from sphinx.ext.autodoc._docstrings import (
- _docstring_lines_for_props,
- _get_docstring_lines,
-)
-from sphinx.ext.autodoc._names import _parse_name
-from sphinx.ext.autodoc._property_types import (
- _AssignStatementProperties,
- _ClassDefProperties,
- _FunctionDefProperties,
- _ItemProperties,
- _ModuleProperties,
- _TypeStatementProperties,
-)
-from sphinx.ext.autodoc._sentinels import (
- RUNTIME_INSTANCE_ATTRIBUTE,
- SLOTS_ATTR,
- UNINITIALIZED_ATTR,
-)
-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._sentinels import RUNTIME_INSTANCE_ATTRIBUTE, UNINITIALIZED_ATTR
from sphinx.ext.autodoc.mock import ismock, mock, undecorate
-from sphinx.locale import __
from sphinx.pycode import ModuleAnalyzer
from sphinx.util import inspect, logging
from sphinx.util.inspect import (
isclass,
safe_getattr,
)
-from sphinx.util.typing import get_type_hints, restify, stringify_annotation
+from sphinx.util.typing import get_type_hints
if TYPE_CHECKING:
- from collections.abc import Mapping, MutableSet, Sequence
+ from collections.abc import Sequence
from importlib.machinery import ModuleSpec
from types import ModuleType
from typing import Any, Protocol
- from sphinx.config import Config
- from sphinx.environment import _CurrentDocument
- from sphinx.events import EventManager
- from sphinx.ext.autodoc._directive_options import _AutoDocumenterOptions
- from sphinx.ext.autodoc._property_types import _AutodocFuncProperty, _AutodocObjType
+ from sphinx.ext.autodoc._property_types import _AutodocObjType
class _AttrGetter(Protocol):
def __call__(self, obj: Any, name: str, default: Any = ..., /) -> Any: ...
@@ -70,8 +40,6 @@ def __call__(self, obj: Any, name: str, default: Any = ..., /) -> Any: ...
_NATIVE_SUFFIXES: frozenset[str] = frozenset({'.pyx', *EXTENSION_SUFFIXES})
logger = logging.getLogger(__name__)
-_hide_value_re = re.compile(r'^:meta \s*hide-value:( +|$)')
-
class _ImportedObject:
#: module containing the object to document
@@ -318,7 +286,9 @@ def _import_object(
obj_path: Sequence[str],
mock_imports: list[str],
get_attr: _AttrGetter = safe_getattr,
-) -> _ImportedObject:
+ obj_type: _AutodocObjType,
+ type_aliases: dict[str, Any] | None,
+) -> _ImportedObject | None:
"""Import the object given by *module_name* and *obj_path* and set
it as *object*.
@@ -329,11 +299,33 @@ def _import_object(
im = _import_from_module_and_path(
module_name=module_name, obj_path=obj_path, get_attr=get_attr
)
- if ismock(im.obj):
- im.obj = undecorate(im.obj)
- return im
- except ImportError: # NoQA: TRY203
- raise
+ except ImportError as exc:
+ if obj_type == 'data':
+ im_ = _import_data_declaration(
+ module_name=module_name,
+ obj_path=obj_path,
+ mock_imports=mock_imports,
+ type_aliases=type_aliases,
+ )
+ if im_ is not None:
+ return im_
+ elif obj_type == 'attribute':
+ im_ = _import_attribute_declaration(
+ module_name=module_name,
+ obj_path=obj_path,
+ mock_imports=mock_imports,
+ type_aliases=type_aliases,
+ get_attr=get_attr,
+ )
+ if im_ is not None:
+ return im_
+
+ logger.warning(exc.args[0], type='autodoc', subtype='import_object')
+ return None
+
+ if ismock(im.obj):
+ im.obj = undecorate(im.obj)
+ return im
def _import_data_declaration(
@@ -459,466 +451,3 @@ def _is_uninitialized_instance_attribute(
"""Check the subject is an annotation only attribute."""
annotations = get_type_hints(parent, None, type_aliases, include_extras=True)
return obj_path[-1] in annotations
-
-
-def _is_slots_attribute(*, parent: Any, obj_path: Sequence[str]) -> bool:
- """Check the subject is an attribute in __slots__."""
- try:
- if parent___slots__ := inspect.getslots(parent):
- return obj_path[-1] in parent___slots__
- else:
- return False
- except (ValueError, TypeError):
- return False
-
-
-def _load_object_by_name(
- *,
- name: str,
- objtype: _AutodocObjType,
- mock_imports: list[str],
- type_aliases: dict[str, Any] | None,
- current_document: _CurrentDocument,
- config: Config,
- events: EventManager,
- get_attr: _AttrGetter,
- options: _AutoDocumenterOptions,
- parent_modname: str | None = None,
- ref_context: Mapping[str, str | None],
- reread_always: MutableSet[str],
-) -> _ItemProperties | None:
- """Import and load the object given by *name*."""
- parsed = _parse_name(
- name=name,
- objtype=objtype,
- current_document=current_document,
- ref_context=ref_context,
- )
- if parsed is None:
- return None
- module_name, parts, args, retann = parsed
-
- # Import the module and get the object to document
- try:
- im = _import_object(
- module_name=module_name,
- obj_path=parts,
- mock_imports=mock_imports,
- get_attr=get_attr,
- )
- except ImportError as exc:
- if objtype == 'data':
- im_ = _import_data_declaration(
- module_name=module_name,
- obj_path=parts,
- mock_imports=mock_imports,
- type_aliases=type_aliases,
- )
- elif objtype == 'attribute':
- im_ = _import_attribute_declaration(
- module_name=module_name,
- obj_path=parts,
- mock_imports=mock_imports,
- type_aliases=type_aliases,
- get_attr=get_attr,
- )
- else:
- im_ = None
- if im_ is None:
- logger.warning(exc.args[0], type='autodoc', subtype='import_object')
- # See BuildEnvironment.note_reread()
- reread_always.add(current_document.docname)
- return None
- else:
- im = im_
-
- # Assemble object properties from the imported object.
- props: _ItemProperties
- parent = im.parent
- object_name = im.object_name
- obj = im.obj
- obj_properties: set[_AutodocFuncProperty] = set()
- if objtype == 'module':
- try:
- mod_origin = im.module.__spec__.origin # type: ignore[union-attr]
- except AttributeError:
- file_path = None
- else:
- file_path = Path(mod_origin) if mod_origin is not None else None
-
- mod_all = safe_getattr(obj, '__all__', None)
- if isinstance(mod_all, (list, tuple)) and all(
- isinstance(e, str) for e in mod_all
- ):
- mod_all = tuple(mod_all)
- elif mod_all is not None:
- # Invalid __all__ found.
- msg = __('Ignoring invalid __all__ in module %s: %r')
- logger.warning(msg, module_name, mod_all, type='autodoc')
- mod_all = None
-
- props = _ModuleProperties(
- obj_type=objtype,
- module_name=module_name,
- docstring_lines=(),
- file_path=file_path,
- all=mod_all,
- _obj=obj,
- _obj___module__=obj.__name__,
- )
- elif objtype in {'class', 'exception'}:
- if isinstance(obj, (NewType, TypeVar)):
- obj_module_name = getattr(obj, '__module__', module_name)
- if obj_module_name != module_name and module_name.startswith(
- obj_module_name
- ):
- bases = module_name[len(obj_module_name) :].strip('.').split('.')
- parts = tuple(bases) + parts
- module_name = obj_module_name
-
- if orig_bases := inspect.getorigbases(obj):
- # A subclass of generic types
- # refs: PEP-560
- obj_bases = list(orig_bases)
- elif hasattr(obj, '__bases__') and obj.__bases__:
- # A normal class
- obj_bases = list(obj.__bases__)
- else:
- obj_bases = []
- full_name = '.'.join((module_name, *parts))
- events.emit(
- 'autodoc-process-bases',
- full_name,
- obj,
- SimpleNamespace(),
- obj_bases,
- )
- if config.autodoc_typehints_format == 'short':
- mode = 'smart'
- else:
- mode = 'fully-qualified-except-typing'
- base_classes = tuple(restify(cls, mode=mode) for cls in obj_bases) # type: ignore[arg-type]
-
- props = _ClassDefProperties(
- obj_type=objtype, # type: ignore[arg-type]
- module_name=module_name,
- parts=parts,
- docstring_lines=(),
- bases=getattr(obj, '__bases__', None),
- _obj=obj,
- _obj___module__=get_attr(obj, '__module__', None),
- _obj___name__=getattr(obj, '__name__', None),
- _obj___qualname__=getattr(obj, '__qualname__', None),
- _obj_bases=base_classes,
- _obj_is_new_type=isinstance(obj, NewType),
- _obj_is_typevar=isinstance(obj, TypeVar),
- )
- elif objtype in {'function', 'decorator'}:
- if inspect.isstaticmethod(obj, cls=parent, name=object_name):
- obj_properties.add('staticmethod')
- if inspect.isclassmethod(obj):
- obj_properties.add('classmethod')
- if inspect.iscoroutinefunction(obj) or inspect.isasyncgenfunction(obj):
- obj_properties.add('async')
-
- props = _FunctionDefProperties(
- obj_type=objtype, # type: ignore[arg-type]
- module_name=module_name,
- parts=parts,
- docstring_lines=(),
- properties=frozenset(obj_properties),
- _obj=obj,
- _obj___module__=get_attr(obj, '__module__', None),
- _obj___name__=getattr(obj, '__name__', None),
- _obj___qualname__=getattr(obj, '__qualname__', None),
- )
- elif objtype == 'method':
- # to distinguish classmethod/staticmethod
- obj_ = parent.__dict__.get(object_name, obj)
- if inspect.isstaticmethod(obj_, cls=parent, name=object_name):
- obj_properties.add('staticmethod')
- elif (
- inspect.is_classmethod_like(obj_)
- or inspect.is_singledispatch_method(obj_)
- and inspect.is_classmethod_like(obj_.func)
- ):
- obj_properties.add('classmethod')
- if inspect.isabstractmethod(obj_):
- obj_properties.add('abstractmethod')
- if inspect.iscoroutinefunction(obj_) or inspect.isasyncgenfunction(obj_):
- obj_properties.add('async')
-
- props = _FunctionDefProperties(
- obj_type=objtype,
- module_name=module_name,
- parts=parts,
- docstring_lines=(),
- properties=frozenset(obj_properties),
- _obj=obj,
- _obj___module__=get_attr(obj, '__module__', None),
- _obj___name__=getattr(obj, '__name__', None),
- _obj___qualname__=getattr(obj, '__qualname__', None),
- )
- elif objtype == 'property':
- if not inspect.isproperty(obj):
- # Support for class properties. Note: these only work on Python 3.9.
- __dict__ = safe_getattr(parent, '__dict__', {})
- obj = __dict__.get(parts[-1])
- if isinstance(obj, classmethod) and inspect.isproperty(obj.__func__):
- obj = obj.__func__
- obj_properties.add('classmethod')
- else:
- return None
- if inspect.isabstractmethod(obj):
- obj_properties.add('abstractmethod')
-
- # get property return type annotation
- obj_property_type_annotation = None
- if safe_getattr(obj, 'fget', None): # property
- func = obj.fget # type: ignore[union-attr]
- elif safe_getattr(obj, 'func', None): # cached_property
- func = obj.func # type: ignore[union-attr]
- else:
- func = None
- if func is not None:
- # update the annotations of the property getter
- if config.autodoc_use_type_comments:
- _update_annotations_using_type_comments(func, False)
-
- try:
- signature = inspect.signature(
- func, type_aliases=config.autodoc_type_aliases
- )
- except TypeError as exc:
- full_name = '.'.join((module_name, *parts))
- logger.warning(
- __('Failed to get a function signature for %s: %s'),
- full_name,
- exc,
- )
- pass
- except ValueError:
- pass
- else:
- if config.autodoc_typehints_format == 'short':
- mode = 'smart'
- else:
- mode = 'fully-qualified-except-typing'
- if signature.return_annotation is not Parameter.empty:
- short_literals = config.python_display_short_literal_types
- obj_property_type_annotation = stringify_annotation(
- signature.return_annotation,
- mode, # type: ignore[arg-type]
- short_literals=short_literals,
- )
-
- props = _FunctionDefProperties(
- obj_type=objtype,
- module_name=module_name,
- parts=parts,
- docstring_lines=(),
- properties=frozenset(obj_properties),
- _obj=obj,
- _obj___module__=get_attr(parent or obj, '__module__', None) or module_name,
- _obj___name__=getattr(parent or obj, '__name__', None),
- _obj___qualname__=getattr(parent or obj, '__qualname__', None),
- _obj_property_type_annotation=obj_property_type_annotation,
- )
- elif objtype == 'data':
- # Update __annotations__ to support type_comment and so on
- _ensure_annotations_from_type_comments(parent)
-
- # obtain annotation
- annotations = get_type_hints(
- parent,
- None,
- config.autodoc_type_aliases,
- include_extras=True,
- )
- if config.autodoc_typehints_format == 'short':
- mode = 'smart'
- else:
- mode = 'fully-qualified-except-typing'
- if parts[-1] in annotations:
- short_literals = config.python_display_short_literal_types
- type_annotation = stringify_annotation(
- annotations[parts[-1]],
- mode, # type: ignore[arg-type]
- short_literals=short_literals,
- )
- else:
- type_annotation = None
-
- if (
- obj is RUNTIME_INSTANCE_ATTRIBUTE
- or obj is SLOTS_ATTR
- or obj is UNINITIALIZED_ATTR
- ):
- obj_sentinel = obj
- else:
- obj_sentinel = None
-
- props = _AssignStatementProperties(
- obj_type=objtype,
- module_name=module_name,
- parts=parts,
- docstring_lines=(),
- value=...,
- annotation='',
- class_var=False,
- instance_var=False,
- _obj=obj,
- _obj___module__=get_attr(parent or obj, '__module__', None) or module_name,
- _obj_is_generic_alias=inspect.isgenericalias(obj),
- _obj_is_attribute_descriptor=inspect.isattributedescriptor(obj),
- _obj_is_mock=ismock(obj),
- _obj_is_sentinel=obj_sentinel,
- _obj_repr_rst=inspect.object_description(obj),
- _obj_type_annotation=type_annotation,
- )
- elif objtype == 'attribute':
- if _is_slots_attribute(parent=parent, obj_path=parts):
- obj = SLOTS_ATTR
- elif inspect.isenumattribute(obj):
- obj = obj.value
- if parent:
- # Update __annotations__ to support type_comment and so on
- _ensure_annotations_from_type_comments(parent)
-
- # obtain annotation
- annotations = get_type_hints(
- parent,
- None,
- config.autodoc_type_aliases,
- include_extras=True,
- )
- if config.autodoc_typehints_format == 'short':
- mode = 'smart'
- else:
- mode = 'fully-qualified-except-typing'
- if parts[-1] in annotations:
- short_literals = config.python_display_short_literal_types
- type_annotation = stringify_annotation(
- annotations[parts[-1]],
- mode, # type: ignore[arg-type]
- short_literals=short_literals,
- )
- else:
- type_annotation = None
-
- if (
- obj is RUNTIME_INSTANCE_ATTRIBUTE
- or obj is SLOTS_ATTR
- or obj is UNINITIALIZED_ATTR
- ):
- obj_sentinel = obj
- else:
- obj_sentinel = None
-
- props = _AssignStatementProperties(
- obj_type=objtype,
- module_name=module_name,
- parts=parts,
- docstring_lines=(),
- value=...,
- annotation='',
- class_var=False,
- instance_var=False,
- _obj=obj,
- _obj___module__=get_attr(obj, '__module__', None),
- _obj_is_generic_alias=inspect.isgenericalias(obj),
- _obj_is_attribute_descriptor=inspect.isattributedescriptor(obj),
- _obj_is_mock=ismock(obj),
- _obj_is_sentinel=obj_sentinel,
- _obj_repr_rst=inspect.object_description(obj),
- _obj_type_annotation=type_annotation,
- )
- elif objtype == 'type':
- obj_module_name = getattr(obj, '__module__', module_name)
- if obj_module_name != module_name and module_name.startswith(obj_module_name):
- bases = module_name[len(obj_module_name) :].strip('.').split('.')
- parts = tuple(bases) + parts
- module_name = obj_module_name
-
- if config.autodoc_typehints_format == 'short':
- mode = 'smart'
- else:
- mode = 'fully-qualified-except-typing'
- short_literals = config.python_display_short_literal_types
- ann = stringify_annotation(
- obj.__value__,
- mode, # type: ignore[arg-type]
- short_literals=short_literals,
- )
- props = _TypeStatementProperties(
- obj_type=objtype,
- module_name=module_name,
- parts=parts,
- docstring_lines=(),
- _obj=obj,
- _obj___module__=get_attr(obj, '__module__', None),
- _obj___name__=getattr(obj, '__name__', None),
- _obj___qualname__=getattr(obj, '__qualname__', None),
- _obj___value__=ann,
- )
- else:
- props = _ItemProperties(
- obj_type=objtype,
- module_name=module_name,
- parts=parts,
- docstring_lines=(),
- _obj=obj,
- _obj___module__=get_attr(obj, '__module__', None),
- )
-
- if options.class_doc_from is not None:
- class_doc_from = options.class_doc_from
- else:
- class_doc_from = config.autoclass_content
-
- docstrings = _get_docstring_lines(
- props,
- class_doc_from=class_doc_from,
- get_attr=get_attr,
- inherit_docstrings=config.autodoc_inherit_docstrings,
- parent=parent,
- tab_width=options._tab_width,
- )
- if docstrings:
- for docstring_lines in docstrings:
- for line in docstring_lines:
- if _hide_value_re.match(line):
- props._docstrings_has_hide_value = True
- break
-
- # format the object's signature, if any
- try:
- signatures = _format_signatures(
- args=args,
- retann=retann,
- autodoc_annotations=current_document.autodoc_annotations,
- config=config,
- docstrings=docstrings,
- events=events,
- get_attr=get_attr,
- parent=parent,
- options=options,
- props=props,
- )
- except Exception as exc:
- msg = __('error while formatting signature for %s: %s')
- logger.warning(msg, props.full_name, exc, type='autodoc')
- return None
- props.signatures = tuple(
- f'{args} -> {retann}' if retann else str(args) for args, retann in signatures
- )
-
- props.docstring_lines = _docstring_lines_for_props(
- docstrings,
- props=props,
- parent_modname=parent_modname,
- events=events,
- options=options,
- )
-
- return props
diff --git a/sphinx/ext/autosummary/__init__.py b/sphinx/ext/autosummary/__init__.py
index 54f272c9963..72d0cc35394 100644
--- a/sphinx/ext/autosummary/__init__.py
+++ b/sphinx/ext/autosummary/__init__.py
@@ -67,10 +67,11 @@
from sphinx import addnodes
from sphinx.errors import PycodeError
from sphinx.ext.autodoc._directive_options import _AutoDocumenterOptions
+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._sentinels import INSTANCE_ATTR
from sphinx.ext.autodoc.directive import _AutodocAttrGetter
-from sphinx.ext.autodoc.importer import _load_object_by_name, import_module
+from sphinx.ext.autodoc.importer import import_module
from sphinx.ext.autodoc.mock import mock
from sphinx.locale import __
from sphinx.pycode import ModuleAnalyzer
diff --git a/tests/test_ext_autodoc/autodoc_util.py b/tests/test_ext_autodoc/autodoc_util.py
index b96147c9c8f..233ec1829dc 100644
--- a/tests/test_ext_autodoc/autodoc_util.py
+++ b/tests/test_ext_autodoc/autodoc_util.py
@@ -8,7 +8,7 @@
_process_documenter_options,
)
from sphinx.ext.autodoc._generate import _generate_directives
-from sphinx.ext.autodoc.importer import _load_object_by_name
+from sphinx.ext.autodoc._loader import _load_object_by_name
from sphinx.util.inspect import safe_getattr
if TYPE_CHECKING:
diff --git a/tests/test_ext_autodoc/test_ext_autodoc.py b/tests/test_ext_autodoc/test_ext_autodoc.py
index 64fc2d0b1bc..69a0ce9a38f 100644
--- a/tests/test_ext_autodoc/test_ext_autodoc.py
+++ b/tests/test_ext_autodoc/test_ext_autodoc.py
@@ -23,10 +23,10 @@
from sphinx.ext.autodoc._docstrings import _get_docstring_lines
from sphinx.ext.autodoc._documenters import Documenter
from sphinx.ext.autodoc._generate import _generate_directives
+from sphinx.ext.autodoc._loader import _load_object_by_name
from sphinx.ext.autodoc._property_types import _ItemProperties
from sphinx.ext.autodoc._sentinels import ALL
from sphinx.ext.autodoc.directive import _AutodocAttrGetter
-from sphinx.ext.autodoc.importer import _load_object_by_name
from sphinx.util.inspect import safe_getattr
from tests.test_ext_autodoc.autodoc_util import do_autodoc