Sphinx

# Source code for sphinx.ext.autodoc

    """Extension to create automatic documentation from code docstrings.

    Automatically insert docstrings for functions, classes or whole modules into
    the doctree, thus avoiding duplication between docstrings and documentation
    for those who like elaborate docstrings.
    """

    from __future__ import annotations

    import functools
    import operator
    import re
    import sys
    from inspect import Parameter, Signature
    from typing import TYPE_CHECKING, Any, NewType, TypeVar

    from docutils.statemachine import StringList

    import sphinx
    from sphinx.config import ENUM
    from sphinx.errors import PycodeError
    from sphinx.ext.autodoc.importer import get_class_members, import_module, import_object
    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.docstrings import prepare_docstring, separate_metadata
    from sphinx.util.inspect import (
        evaluate_signature,
        getdoc,
        object_description,
        safe_getattr,
        stringify_signature,
    )
    from sphinx.util.typing import get_type_hints, restify, stringify_annotation

    if TYPE_CHECKING:
        from collections.abc import Callable, Iterator, Sequence
        from types import ModuleType
        from typing import ClassVar, Literal, TypeAlias

        from sphinx.application import Sphinx
        from sphinx.config import Config
        from sphinx.environment import BuildEnvironment, _CurrentDocument
        from sphinx.events import EventManager
        from sphinx.ext.autodoc.directive import DocumenterBridge
        from sphinx.registry import SphinxComponentRegistry
        from sphinx.util.typing import ExtensionMetadata, OptionSpec, _RestifyMode

        _AutodocObjType = Literal[
            'module', 'class', 'exception', 'function', 'method', 'attribute'
        ]
        _AutodocProcessDocstringListener: TypeAlias = Callable[
            [Sphinx, _AutodocObjType, str, Any, dict[str, bool], list[str]], None
        ]

    logger = logging.getLogger(__name__)

    # This type isn't exposed directly in any modules, but can be found
    # here in most Python versions
    MethodDescriptorType = type(type.__subclasses__)

    #: extended signature RE: with explicit module name separated by ::
    py_ext_sig_re = re.compile(
        r"""^ ([\w.]+::)?            # explicit module name
              ([\w.]+\.)?            # module and/or class name(s)
              (\w+)  \s*             # thing name
              (?: \[\s*(.*?)\s*])?   # optional: type parameters list
              (?: \((.*)\)           # optional: arguments
               (?:\s* -> \s* (.*))?  #           return annotation
              )? $                   # and nothing more
        """,
        re.VERBOSE,
    )
    special_member_re = re.compile(r'^__\S+__$')

    def _get_render_mode(
        typehints_format: Literal['fully-qualified', 'short'],
    ) -> _RestifyMode:
        if typehints_format == 'short':
            return 'smart'
        return 'fully-qualified-except-typing'

    def identity(x: Any) -> Any:
        return x

    class _All:
        """A special value for :*-members: that matches to any member."""

        def __contains__(self, item: Any) -> bool:
            return True

        def append(self, item: Any) -> None:
            pass  # nothing

    class _Empty:
        """A special value for :exclude-members: that never matches to any member."""

        def __contains__(self, item: Any) -> bool:
            return False

    ALL = _All()
    EMPTY = _Empty()
    UNINITIALIZED_ATTR = object()
    INSTANCEATTR = object()
    SLOTSATTR = object()

    def members_option(arg: Any) -> object | list[str]:
        """Used to convert the :members: option to auto directives."""
        if arg in {None, True}:
            return ALL
        elif arg is False:
            return None
        else:
            return [x.strip() for x in arg.split(',') if x.strip()]

    def exclude_members_option(arg: Any) -> object | set[str]:
        """Used to convert the :exclude-members: option."""
        if arg in {None, True}:
            return EMPTY
        return {x.strip() for x in arg.split(',') if x.strip()}

    def inherited_members_option(arg: Any) -> set[str]:
        """Used to convert the :inherited-members: option to auto directives."""
        if arg in {None, True}:
            return {'object'}
        elif arg:
            return {x.strip() for x in arg.split(',')}
        else:
            return set()

    def member_order_option(arg: Any) -> str | None:
        """Used to convert the :member-order: option to auto directives."""
        if arg in {None, True}:
            return None
        elif arg in {'alphabetical', 'bysource', 'groupwise'}:
            return arg
        else:
            raise ValueError(__('invalid value for member-order option: %s') % arg)

    def class_doc_from_option(arg: Any) -> str | None:
        """Used to convert the :class-doc-from: option to autoclass directives."""
        if arg in {'both', 'class', 'init'}:
            return arg
        else:
            raise ValueError(__('invalid value for class-doc-from option: %s') % arg)

    SUPPRESS = object()

    def annotation_option(arg: Any) -> Any:
        if arg in {None, True}:
            # suppress showing the representation of the object
            return SUPPRESS
        else:
            return arg

    def bool_option(arg: Any) -> bool:
        """Used to convert flag options to auto directives.  (Instead of
        directives.flag(), which returns None).
        """
        return True

    def merge_members_option(options: dict[str, Any]) -> None:
        """Merge :private-members: and :special-members: options to the
        :members: option.
        """
        if options.get('members') is ALL:
            # merging is not needed when members: ALL
            return

        members = options.setdefault('members', [])
        for key in ('private-members', 'special-members'):
            other_members = options.get(key)
            if other_members is not None and other_members is not ALL:
                for member in other_members:
                    if member not in members:
                        members.append(member)

    # Some useful event listener factories for autodoc-process-docstring.

    def cut_lines(
        pre: int, post: int = 0, what: Sequence[str] | None = None
    ) -> _AutodocProcessDocstringListener:
        """Return a listener that removes the first *pre* and last *post*
        lines of every docstring.  If *what* is a sequence of strings,
        only docstrings of a type in *what* will be processed.

        Use like this (e.g. in the ``setup()`` function of :file:`conf.py`)::

           from sphinx.ext.autodoc import cut_lines

           app.connect('autodoc-process-docstring', cut_lines(4, what={'module'}))

        This can (and should) be used in place of :confval:`automodule_skip_lines`.
        """
        if not what:
            what_unique: frozenset[str] = frozenset()
        elif isinstance(what, str):  # strongly discouraged
            what_unique = frozenset({what})
        else:
            what_unique = frozenset(what)

        def process(
            app: Sphinx,
            what_: _AutodocObjType,
            name: str,
            obj: Any,
            options: dict[str, bool],
            lines: list[str],
        ) -> None:
            if what_unique and what_ not in what_unique:
                return
            del lines[:pre]
            if post:
                # remove one trailing blank line.
                if lines and not lines[-1]:
                    lines.pop(-1)
                del lines[-post:]
            # make sure there is a blank line at the end
            if lines and lines[-1]:
                lines.append('')

        return process

    def between(
        marker: str,
        what: Sequence[str] | None = None,
        keepempty: bool = False,
        exclude: bool = False,
    ) -> _AutodocProcessDocstringListener:
        """Return a listener that either keeps, or if *exclude* is True excludes,
        lines between lines that match the *marker* regular expression.  If no line
        matches, the resulting docstring would be empty, so no change will be made
        unless *keepempty* is true.

        If *what* is a sequence of strings, only docstrings of a type in *what* will
        be processed.
        """
        marker_re = re.compile(marker)

        def process(
            app: Sphinx,
            what_: _AutodocObjType,
            name: str,
            obj: Any,
            options: dict[str, bool],
            lines: list[str],
        ) -> None:
            if what and what_ not in what:
                return
            deleted = 0
            delete = not exclude
            orig_lines = lines.copy()
            for i, line in enumerate(orig_lines):
                if delete:
                    lines.pop(i - deleted)
                    deleted += 1
                if marker_re.match(line):
                    delete = not delete
                    if delete:
                        lines.pop(i - deleted)
                        deleted += 1
            if not lines and not keepempty:
                lines[:] = orig_lines
            # make sure there is a blank line at the end
            if lines and lines[-1]:
                lines.append('')

        return process

    # This class is used only in ``sphinx.ext.autodoc.directive``,
    # But we define this class here to keep compatibility
    # See: https://github.com/sphinx-doc/sphinx/issues/4538
    class Options(dict[str, Any]):  # NoQA: FURB189
        """A dict/attribute hybrid that returns None on nonexisting keys."""

        def copy(self) -> Options:
            return Options(super().copy())

        def __getattr__(self, name: str) -> Any:
            try:
                return self[name.replace('_', '-')]
            except KeyError:
                return None

    class ObjectMember:
        """A member of object.

        This is used for the result of `Documenter.get_module_members()` to
        represent each member of the object.
        """

        __slots__ = '__name__', 'object', 'docstring', 'class_', 'skipped'

        __name__: str
        object: Any
        docstring: str | None
        class_: Any
        skipped: bool

        def __init__(
            self,
            name: str,
            obj: Any,
            *,
            docstring: str | None = None,
            class_: Any = None,
            skipped: bool = False,
        ) -> None:
            self.__name__ = name
            self.object = obj
            self.docstring = docstring
            self.class_ = class_
            self.skipped = skipped

        def __repr__(self) -> str:
            return (
                f'ObjectMember('
                f'name={self.__name__!r}, '
                f'obj={self.object!r}, '
                f'docstring={self.docstring!r}, '
                f'class_={self.class_!r}, '
                f'skipped={self.skipped!r}'
                f')'
            )

    class Documenter:
        """A Documenter knows how to autodocument a single object type.  When
        registered with the AutoDirective, it will be used to document objects
        of that type when needed by autodoc.

        Its *objtype* attribute selects what auto directive it is assigned to
        (the directive name is 'auto' + objtype), and what directive it generates
        by default, though that can be overridden by an attribute called
        *directivetype*.

        A Documenter has an *option_spec* that works like a docutils directive's;
        in fact, it will be used to parse an auto directive's options that matches
        the Documenter.
        """

        #: name by which the directive is called (auto...) and the default
        #: generated directive name
        objtype = 'object'
        #: indentation by which to indent the directive content
        content_indent = '   '
        #: priority if multiple documenters return True from can_document_member
        priority = 0
        #: order if autodoc_member_order is set to 'groupwise'
        member_order = 0
        #: true if the generated content may contain titles
        titles_allowed = True

        option_spec: ClassVar[OptionSpec] = {
            'no-index': bool_option,
            'no-index-entry': bool_option,
            'noindex': bool_option,
        }

        def get_attr(self, obj: Any, name: str, *defargs: Any) -> Any:
            """getattr() override for types such as Zope interfaces."""
            return autodoc_attrgetter(obj, name, *defargs, registry=self.env._registry)

        @classmethod
        def can_document_member(
            cls: type[Documenter], member: Any, membername: str, isattr: bool, parent: Any
        ) -> bool:
            """Called to see if a member can be documented by this Documenter."""
            msg = 'must be implemented in subclasses'
            raise NotImplementedError(msg)

        def __init__(
            self, directive: DocumenterBridge, name: str, indent: str = ''
        ) -> None:
            self.directive = directive
            self.config: Config = directive.env.config
            self.env: BuildEnvironment = directive.env
            self._current_document: _CurrentDocument = directive.env.current_document
            self._events: EventManager = directive.env.events
            self.options = directive.genopt
            self.name = name
            self.indent = indent
            # the module and object path within the module, and the fully
            # qualified name (all set after resolve_name succeeds)
            self.modname: str = ''
            self.module: ModuleType | None = None
            self.objpath: list[str] = []
            self.fullname = ''
            # extra signature items (arguments and return annotation,
            # also set after resolve_name succeeds)
            self.args: str | None = None
            self.retann: str = ''
            # the object to document (set after import_object succeeds)
            self.object: Any = None
            self.object_name = ''
            # the parent/owner of the object to document
            self.parent: Any = None
            # the module analyzer to get at attribute docs, or None
            self.analyzer: ModuleAnalyzer | None = None

        @property
        def documenters(self) -> dict[str, type[Documenter]]:
            """Returns registered Documenter classes"""
            return self.env._registry.documenters

        def add_line(self, line: str, source: str, *lineno: int) -> None:
            """Append one line of generated reST to the output."""
            if line.strip():  # not a blank line
                self.directive.result.append(self.indent + line, source, *lineno)
            else:
                self.directive.result.append('', source, *lineno)

        def resolve_name(
            self, modname: str | None, parents: Any, path: str, base: str
        ) -> tuple[str | None, list[str]]:
            """Resolve the module and name of the object to document given by the
            arguments and the current module/class.

            Must return a pair of the module name and a chain of attributes; for
            example, it would return ``('zipfile', ['ZipFile', 'open'])`` for the
            ``zipfile.ZipFile.open`` method.
            """
            msg = 'must be implemented in subclasses'
            raise NotImplementedError(msg)

        def parse_name(self) -> bool:
            """Determine what module to import and what attribute to document.

            Returns True and sets *self.modname*, *self.objpath*, *self.fullname*,
            *self.args* and *self.retann* if parsing and resolving was successful.
            """
            # first, parse the definition -- auto directives for classes and
            # functions can contain a signature which is then used instead of
            # an autogenerated one
            matched = py_ext_sig_re.match(self.name)
            if matched is None:
                logger.warning(
                    __('invalid signature for auto%s (%r)'),
                    self.objtype,
                    self.name,
                    type='autodoc',
                )
                return False
            explicit_modname, path, base, tp_list, args, retann = matched.groups()

            # support explicit module and class name separation via ::
            if explicit_modname is not None:
                modname = explicit_modname[:-2]
                parents = path.rstrip('.').split('.') if path else []
            else:
                modname = None
                parents = []

            with mock(self.config.autodoc_mock_imports):
                modname, self.objpath = self.resolve_name(modname, parents, path, base)

            if not modname:
                return False

            self.modname = modname
            self.args = args
            self.retann = retann
            self.fullname = '.'.join((self.modname or '', *self.objpath))
            return True

        def import_object(self, raiseerror: bool = False) -> bool:
            """Import the object given by *self.modname* and *self.objpath* and set
            it as *self.object*.

            Returns True if successful, False if an error occurred.
            """
            with mock(self.config.autodoc_mock_imports):
                try:
                    ret = import_object(
                        self.modname, self.objpath, self.objtype, attrgetter=self.get_attr
                    )
                    self.module, self.parent, self.object_name, self.object = ret
                    if ismock(self.object):
                        self.object = undecorate(self.object)
                    return True
                except ImportError as exc:
                    if raiseerror:
                        raise
                    logger.warning(exc.args[0], type='autodoc', subtype='import_object')
                    self.env.note_reread()
                    return False

        def get_real_modname(self) -> str:
            """Get the real module name of an object to document.

            It can differ from the name of the module through which the object was
            imported.
            """
            return self.get_attr(self.object, '__module__', None) or self.modname

        def check_module(self) -> bool:
            """Check if *self.object* is really defined in the module given by
            *self.modname*.
            """
            if self.options.imported_members:
                return True

            subject = inspect.unpartial(self.object)
            modname = self.get_attr(subject, '__module__', None)
            return not modname or modname == self.modname

        def format_args(self, **kwargs: Any) -> str:
            """Format the argument signature of *self.object*.

            Should return None if the object does not have a signature.
            """
            return ''

        def format_name(self) -> str:
            """Format the name of *self.object*.

            This normally should be something that can be parsed by the generated
            directive, but doesn't need to be (Sphinx will display it unparsed
            then).
            """
            # normally the name doesn't contain the module (except for module
            # directives of course)
            return '.'.join(self.objpath) or self.modname

        def _call_format_args(self, **kwargs: Any) -> str:
            if kwargs:
                try:
                    return self.format_args(**kwargs)
                except TypeError:
                    # avoid chaining exceptions, by putting nothing here
                    pass

            # retry without arguments for old documenters
            return self.format_args()

        def format_signature(self, **kwargs: Any) -> str:
            """Format the signature (arguments and return annotation) of the object.

            Let the user process it via the ``autodoc-process-signature`` event.
            """
            if self.args is not None:
                # signature given explicitly
                args = f'({self.args})'
                retann = self.retann
            else:
                # try to introspect the signature
                try:
                    retann = None
                    args = self._call_format_args(**kwargs)
                    if args:
                        matched = re.match(r'^(\(.*\))\s+->\s+(.*)$', args)
                        if matched:
                            args = matched.group(1)
                            retann = matched.group(2)
                except Exception as exc:
                    logger.warning(
                        __('error while formatting arguments for %s: %s'),
                        self.fullname,
                        exc,
                        type='autodoc',
                    )
                    args = None

            result = self._events.emit_firstresult(
                'autodoc-process-signature',
                self.objtype,
                self.fullname,
                self.object,
                self.options,
                args,
                retann,
            )
            if result:
                args, retann = result

            if args is not None:
                return args + ((' -> %s' % retann) if retann else '')
            else:
                return ''

        def add_directive_header(self, sig: str) -> None:
            """Add the directive header and options to the generated content."""
            domain = getattr(self, 'domain', 'py')
            directive = getattr(self, 'directivetype', self.objtype)
            name = self.format_name()
            sourcename = self.get_sourcename()

            # one signature per line, indented by column
            prefix = f'.. {domain}:{directive}:: '
            for i, sig_line in enumerate(sig.split('\n')):
                self.add_line(f'{prefix}{name}{sig_line}', sourcename)
                if i == 0:
                    prefix = ' ' * len(prefix)

            if self.options.no_index or self.options.noindex:
                self.add_line('   :no-index:', sourcename)
            if self.options.no_index_entry:
                self.add_line('   :no-index-entry:', sourcename)
            if self.objpath:
                # Be explicit about the module, this is necessary since .. class::
                # etc. don't support a prepended module name
                self.add_line('   :module: %s' % self.modname, sourcename)

        def get_doc(self) -> list[list[str]] | None:
            """Decode and return lines of the docstring(s) for the object.

            When it returns None, autodoc-process-docstring will not be called for this
            object.
            """
            docstring = getdoc(
                self.object,
                self.get_attr,
                self.config.autodoc_inherit_docstrings,
                self.parent,
                self.object_name,
            )
            if docstring:
                tab_width = self.directive.state.document.settings.tab_width
                return [prepare_docstring(docstring, tab_width)]
            return []

        def process_doc(self, docstrings: list[list[str]]) -> Iterator[str]:
            """Let the user process the docstrings before adding them."""
            for docstringlines in docstrings:
                if self._events is not None:
                    # let extensions preprocess docstrings
                    self._events.emit(
                        'autodoc-process-docstring',
                        self.objtype,
                        self.fullname,
                        self.object,
                        self.options,
                        docstringlines,
                    )

                    if docstringlines and docstringlines[-1]:
                        # append a blank line to the end of the docstring
                        docstringlines.append('')

                yield from docstringlines

        def get_sourcename(self) -> str:
            obj_module = inspect.safe_getattr(self.object, '__module__', None)
            obj_qualname = inspect.safe_getattr(self.object, '__qualname__', None)
            if obj_module and obj_qualname:
                # Get the correct location of docstring from self.object
                # to support inherited methods
                fullname = f'{self.object.__module__}.{self.object.__qualname__}'
            else:
                fullname = self.fullname

            if self.analyzer:
                return f'{self.analyzer.srcname}:docstring of {fullname}'
            else:
                return 'docstring of %s' % fullname

        def add_content(self, more_content: StringList | None) -> None:
            """Add content from docstrings, attribute documentation and user."""
            docstring = True

            # set sourcename and add content from attribute documentation
            sourcename = self.get_sourcename()
            if self.analyzer:
                attr_docs = self.analyzer.find_attr_docs()
                if self.objpath:
                    key = ('.'.join(self.objpath[:-1]), self.objpath[-1])
                    if key in attr_docs:
                        docstring = False
                        # make a copy of docstring for attributes to avoid cache
                        # the change of autodoc-process-docstring event.
                        attribute_docstrings = [list(attr_docs[key])]

                        for i, line in enumerate(self.process_doc(attribute_docstrings)):
                            self.add_line(line, sourcename, i)

            # add content from docstrings
            if docstring:
                docstrings = self.get_doc()
                if docstrings is None:
                    # Do not call autodoc-process-docstring on get_doc() returns None.
                    pass
                else:
                    if not docstrings:
                        # append at least a dummy docstring, so that the event
                        # autodoc-process-docstring is fired and can add some
                        # content if desired
                        docstrings.append([])
                    for i, line in enumerate(self.process_doc(docstrings)):
                        self.add_line(line, sourcename, i)

            # add additional content (e.g. from document), if present
            if more_content:
                for line, src in zip(more_content.data, more_content.items, strict=True):
                    self.add_line(line, src[0], src[1])

        def get_object_members(self, want_all: bool) -> tuple[bool, list[ObjectMember]]:
            """Return `(members_check_module, members)` where `members` is a
            list of `(membername, member)` pairs of the members of *self.object*.

            If *want_all* is True, return all members.  Else, only return those
            members given by *self.options.members* (which may also be None).
            """
            msg = 'must be implemented in subclasses'
            raise NotImplementedError(msg)

        def filter_members(
            self, members: list[ObjectMember], want_all: bool
        ) -> list[tuple[str, Any, bool]]:
            """Filter the given member list.

            Members are skipped if

            - they are private (except if given explicitly or the private-members
              option is set)
            - they are special methods (except if given explicitly or the
              special-members option is set)
            - they are undocumented (except if the undoc-members option is set)

            The user can override the skipping decision by connecting to the
            ``autodoc-skip-member`` event.
            """

            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 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__:
                            return False
                        if name in self.get_attr(cls, '__annotations__', {}):
                            return False
                        if isinstance(obj, ObjectMember) and obj.class_ is cls:
                            return False

                return False

            ret = []

            # search for members in source code too
            namespace = '.'.join(self.objpath)  # will be empty for modules

            if self.analyzer:
                attr_docs = self.analyzer.find_attr_docs()
            else:
                attr_docs = {}

            # process members and determine which to skip
            for obj in members:
                membername = obj.__name__
                member = obj.object

                # if isattr is True, the member is documented as an attribute
                isattr = member is INSTANCEATTR or (namespace, membername) in attr_docs

                try:
                    doc = getdoc(
                        member,
                        self.get_attr,
                        self.config.autodoc_inherit_docstrings,
                        self.object,
                        membername,
                    )
                    if not isinstance(doc, str):
                        # Ignore non-string __doc__
                        doc = None

                    # if the member __doc__ is the same as self's __doc__, it's just
                    # inherited and therefore not the member's doc
                    cls = self.get_attr(member, '__class__', None)
                    if cls:
                        cls_doc = self.get_attr(cls, '__doc__', None)
                        if cls_doc == doc:
                            doc = None

                    if isinstance(obj, ObjectMember) and obj.docstring:
                        # hack for ClassDocumenter to inject docstring via ObjectMember
                        doc = obj.docstring

                    doc, metadata = separate_metadata(doc)
                    has_doc = bool(doc)

                    if 'private' in metadata:
                        # consider a member private if docstring has "private" metadata
                        isprivate = True
                    elif 'public' in metadata:
                        # consider a member public if docstring has "public" metadata
                        isprivate = False
                    else:
                        isprivate = membername.startswith('_')

                    keep = False
                    if ismock(member) and (namespace, membername) not in attr_docs:
                        # mocked module or object
                        pass
                    elif (
                        self.options.exclude_members
                        and membername in self.options.exclude_members
                    ):
                        # remove members given by exclude-members
                        keep = False
                    elif want_all and special_member_re.match(membername):
                        # special __methods__
                        if (
                            self.options.special_members
                            and membername in self.options.special_members
                        ):
                            if membername == '__doc__':  # NoQA: SIM114
                                keep = False
                            elif is_filtered_inherited_member(membername, obj):
                                keep = False
                            else:
                                keep = has_doc or self.options.undoc_members
                        else:
                            keep = False
                    elif (namespace, membername) in attr_docs:
                        if want_all and isprivate:
                            if self.options.private_members is None:
                                keep = False
                            else:
                                keep = membername in self.options.private_members
                        else:
                            # keep documented attributes
                            keep = True
                    elif want_all and isprivate:
                        if has_doc or self.options.undoc_members:
                            if self.options.private_members is None:  # NoQA: SIM114
                                keep = False
                            elif is_filtered_inherited_member(membername, obj):
                                keep = False
                            else:
                                keep = membername in self.options.private_members
                        else:
                            keep = False
                    else:
                        if self.options.members is ALL and is_filtered_inherited_member(
                            membername, obj
                        ):
                            keep = False
                        else:
                            # ignore undocumented members if :undoc-members: is not given
                            keep = has_doc or self.options.undoc_members

                    if isinstance(obj, ObjectMember) and obj.skipped:
                        # forcedly skipped member (ex. a module attribute not defined in __all__)
                        keep = False

                    # give the user a chance to decide whether this member
                    # should be skipped
                    if self._events is not None:
                        # let extensions preprocess docstrings
                        skip_user = self._events.emit_firstresult(
                            'autodoc-skip-member',
                            self.objtype,
                            membername,
                            member,
                            not keep,
                            self.options,
                        )
                        if skip_user is not None:
                            keep = not skip_user
                except Exception as exc:
                    logger.warning(
                        __(
                            'autodoc: failed to determine %s.%s (%r) to be documented, '
                            'the following exception was raised:\n%s'
                        ),
                        self.name,
                        membername,
                        member,
                        exc,
                        type='autodoc',
                    )
                    keep = False

                if keep:
                    ret.append((membername, member, isattr))

            return ret

        def document_members(self, all_members: bool = False) -> None:
            """Generate reST for member documentation.

            If *all_members* is True, document all members, else those given by
            *self.options.members*.
            """
            # set current namespace for finding members
            self._current_document.autodoc_module = self.modname
            if self.objpath:
                self._current_document.autodoc_class = self.objpath[0]

            want_all = (
                all_members or self.options.inherited_members or self.options.members is ALL
            )
            # find out which members are documentable
            members_check_module, members = self.get_object_members(want_all)

            # document non-skipped members
            member_documenters: list[tuple[Documenter, bool]] = []
            for mname, member, isattr in self.filter_members(members, want_all):
                classes = [
                    cls
                    for cls in self.documenters.values()
                    if cls.can_document_member(member, mname, isattr, self)
                ]
                if not classes:
                    # don't know how to document this member
                    continue
                # prefer the documenter with the highest priority
                classes.sort(key=lambda cls: cls.priority)
                # give explicitly separated module name, so that members
                # of inner classes can be documented
                full_mname = f'{self.modname}::' + '.'.join((*self.objpath, mname))
                documenter = classes[-1](self.directive, full_mname, self.indent)
                member_documenters.append((documenter, isattr))

            member_order = self.options.member_order or self.config.autodoc_member_order
            # We now try to import all objects before ordering them. This is to
            # avoid possible circular imports if we were to import objects after
            # their associated documenters have been sorted.
            member_documenters = [
                (documenter, isattr)
                for documenter, isattr in member_documenters
                if documenter.parse_name() and documenter.import_object()
            ]
            member_documenters = self.sort_members(member_documenters, member_order)

            for documenter, isattr in member_documenters:
                assert documenter.modname
                # We can directly call ._generate() since the documenters
                # already called parse_name() and import_object() before.
                #
                # Note that those two methods above do not emit events, so
                # whatever objects we deduced should not have changed.
                documenter._generate(
                    all_members=True,
                    real_modname=self.real_modname,
                    check_module=members_check_module and not isattr,
                )

            # reset current objects
            self._current_document.autodoc_module = ''
            self._current_document.autodoc_class = ''

        def sort_members(
            self, documenters: list[tuple[Documenter, bool]], order: str
        ) -> list[tuple[Documenter, bool]]:
            """Sort the given member list."""
            if order == 'groupwise':
                # sort by group; alphabetically within groups
                documenters.sort(key=lambda e: (e[0].member_order, e[0].name))
            elif order == 'bysource':
                # By default, member discovery order matches source order,
                # as dicts are insertion-ordered from Python 3.7.
                if self.analyzer:
                    # sort by source order, by virtue of the module analyzer
                    tagorder = self.analyzer.tagorder

                    def keyfunc(entry: tuple[Documenter, bool]) -> int:
                        fullname = entry[0].name.split('::')[1]
                        return tagorder.get(fullname, len(tagorder))

                    documenters.sort(key=keyfunc)
            else:  # alphabetical
                documenters.sort(key=lambda e: e[0].name)

            return documenters

        def generate(
            self,
            more_content: StringList | None = None,
            real_modname: str | None = None,
            check_module: bool = False,
            all_members: bool = False,
        ) -> None:
            """Generate reST for the object given by *self.name*, and possibly for
            its members.

            If *more_content* is given, include that content. If *real_modname* is
            given, use that module name to find attribute docs. If *check_module* is
            True, only generate if the object is defined in the module name it is
            imported from. If *all_members* is True, document all members.
            """
            if not self.parse_name():
                # need a module to import
                logger.warning(
                    __(
                        "don't know which module to import for autodocumenting "
                        '%r (try placing a "module" or "currentmodule" directive '
                        'in the document, or giving an explicit module name)'
                    ),
                    self.name,
                    type='autodoc',
                )
                return

            # now, import the module and get object to document
            if not self.import_object():
                return

            self._generate(more_content, real_modname, check_module, all_members)

        def _generate(
            self,
            more_content: StringList | None = None,
            real_modname: str | None = None,
            check_module: bool = False,
            all_members: bool = False,
        ) -> None:
            # If there is no real module defined, figure out which to use.
            # The real module is used in the module analyzer to look up the module
            # where the attribute documentation would actually be found in.
            # This is used for situations where you have a module that collects the
            # functions and classes of internal submodules.
            guess_modname = self.get_real_modname()
            self.real_modname: str = real_modname or guess_modname

            # try to also get a source code analyzer for attribute docs
            try:
                self.analyzer = ModuleAnalyzer.for_module(self.real_modname)
                # parse right now, to get PycodeErrors on parsing (results will
                # be cached anyway)
                self.analyzer.find_attr_docs()
            except PycodeError as exc:
                logger.debug('[autodoc] module analyzer failed: %s', exc)
                # no source file -- e.g. for builtin and C modules
                self.analyzer = None
                # at least add the module.__file__ as a dependency
                if module___file__ := getattr(self.module, '__file__', ''):
                    self.directive.record_dependencies.add(module___file__)
            else:
                self.directive.record_dependencies.add(self.analyzer.srcname)

            if self.real_modname != guess_modname:
                # Add module to dependency list if target object is defined in other module.
                try:
                    analyzer = ModuleAnalyzer.for_module(guess_modname)
                    self.directive.record_dependencies.add(analyzer.srcname)
                except PycodeError:
                    pass

            docstrings: list[str] = functools.reduce(
                operator.iadd, self.get_doc() or [], []
            )
            if ismock(self.object) and not docstrings:
                logger.warning(
                    __('A mocked object is detected: %r'),
                    self.name,
                    type='autodoc',
                    subtype='mocked_object',
                )

            # check __module__ of object (for members not given explicitly)
            if check_module:
                if not self.check_module():
                    return

            sourcename = self.get_sourcename()

            # make sure that the result starts with an empty line.  This is
            # necessary for some situations where another directive preprocesses
            # reST and no starting newline is present
            self.add_line('', sourcename)

            # format the object's signature, if any
            try:
                sig = self.format_signature()
            except Exception as exc:
                logger.warning(
                    __('error while formatting signature for %s: %s'),
                    self.fullname,
                    exc,
                    type='autodoc',
                )
                return

            # generate the directive header and options, if applicable
            self.add_directive_header(sig)
            self.add_line('', sourcename)

            # e.g. the module directive doesn't have content
            self.indent += self.content_indent

            # add all content (from docstrings, attribute docs etc.)
            self.add_content(more_content)

            # document members, if possible
            self.document_members(all_members)

    class ModuleDocumenter(Documenter):
        """Specialized Documenter subclass for modules."""

        objtype = 'module'
        content_indent = ''
        _extra_indent = '   '

        option_spec: ClassVar[OptionSpec] = {
            'members': members_option,
            'undoc-members': bool_option,
            'no-index': bool_option,
            'no-index-entry': bool_option,
            'inherited-members': inherited_members_option,
            'show-inheritance': bool_option,
            'synopsis': identity,
            'platform': identity,
            'deprecated': bool_option,
            'member-order': member_order_option,
            'exclude-members': exclude_members_option,
            'private-members': members_option,
            'special-members': members_option,
            'imported-members': bool_option,
            'ignore-module-all': bool_option,
            'no-value': bool_option,
            'noindex': bool_option,
        }

        def __init__(self, *args: Any) -> None:
            super().__init__(*args)
            merge_members_option(self.options)
            self.__all__: Sequence[str] | None = None

        def add_content(self, more_content: StringList | None) -> None:
            old_indent = self.indent
            self.indent += self._extra_indent
            super().add_content(None)
            self.indent = old_indent
            if more_content:
                for line, src in zip(more_content.data, more_content.items, strict=True):
                    self.add_line(line, src[0], src[1])

        @classmethod
        def can_document_member(
            cls: type[Documenter], member: Any, membername: str, isattr: bool, parent: Any
        ) -> bool:
            # don't document submodules automatically
            return False

        def resolve_name(
            self, modname: str | None, parents: Any, path: str, base: str
        ) -> tuple[str | None, list[str]]:
            if modname is not None:
                logger.warning(
                    __('"::" in automodule name doesn\'t make sense'), type='autodoc'
                )
            return (path or '') + base, []

        def parse_name(self) -> bool:
            ret = super().parse_name()
            if self.args or self.retann:
                logger.warning(
                    __('signature arguments or return annotation given for automodule %s'),
                    self.fullname,
                    type='autodoc',
                )
            return ret

        def import_object(self, raiseerror: bool = False) -> bool:
            ret = super().import_object(raiseerror)

            try:
                if not self.options.ignore_module_all:
                    self.__all__ = inspect.getall(self.object)
            except ValueError as exc:
                # invalid __all__ found.
                logger.warning(
                    __(
                        '__all__ should be a list of strings, not %r '
                        '(in module %s) -- ignoring __all__'
                    ),
                    exc.args[0],
                    self.fullname,
                    type='autodoc',
                )

            return ret

        def add_directive_header(self, sig: str) -> None:
            Documenter.add_directive_header(self, sig)

            sourcename = self.get_sourcename()

            # add some module-specific options
            if self.options.synopsis:
                self.add_line('   :synopsis: ' + self.options.synopsis, sourcename)
            if self.options.platform:
                self.add_line('   :platform: ' + self.options.platform, sourcename)
            if self.options.deprecated:
                self.add_line('   :deprecated:', sourcename)
            if self.options.no_index_entry:
                self.add_line('   :no-index-entry:', sourcename)

        def get_module_members(self) -> dict[str, ObjectMember]:
            """Get members of target module."""
            if self.analyzer:
                attr_docs = self.analyzer.attr_docs
            else:
                attr_docs = {}

            members: dict[str, ObjectMember] = {}
            for name in dir(self.object):
                try:
                    value = safe_getattr(self.object, name, None)
                    if ismock(value):
                        value = undecorate(value)
                    docstring = attr_docs.get(('', name), [])
                    members[name] = ObjectMember(
                        name, value, docstring='\n'.join(docstring)
                    )
                except AttributeError:
                    continue

            # annotation only member (ex. attr: int)
            for name in inspect.getannotations(self.object):
                if name not in members:
                    docstring = attr_docs.get(('', name), [])
                    members[name] = ObjectMember(
                        name, INSTANCEATTR, docstring='\n'.join(docstring)
                    )

            return members

        def get_object_members(self, want_all: bool) -> tuple[bool, list[ObjectMember]]:
            members = self.get_module_members()
            if want_all:
                if self.__all__ is None:
                    # for implicit module members, check __module__ to avoid
                    # documenting imported objects
                    return True, list(members.values())
                else:
                    for member in members.values():
                        if member.__name__ not in self.__all__:
                            member.skipped = True

                    return False, list(members.values())
            else:
                memberlist = self.options.members or []
                ret = []
                for name in memberlist:
                    if name in members:
                        ret.append(members[name])
                    else:
                        logger.warning(
                            __(
                                'missing attribute mentioned in :members: option: '
                                'module %s, attribute %s'
                            ),
                            safe_getattr(self.object, '__name__', '???'),
                            name,
                            type='autodoc',
                        )
                return False, ret

        def sort_members(
            self, documenters: list[tuple[Documenter, bool]], order: str
        ) -> list[tuple[Documenter, bool]]:
            if order == 'bysource' and self.__all__:
                assert self.__all__ is not None
                module_all = self.__all__
                module_all_set = set(module_all)
                module_all_len = len(module_all)

                # Sort alphabetically first (for members not listed on the __all__)
                documenters.sort(key=lambda e: e[0].name)

                # Sort by __all__
                def keyfunc(entry: tuple[Documenter, bool]) -> int:
                    name = entry[0].name.split('::')[1]
                    if name in module_all_set:
                        return module_all.index(name)
                    else:
                        return module_all_len

                documenters.sort(key=keyfunc)

                return documenters
            else:
                return super().sort_members(documenters, order)

    class ModuleLevelDocumenter(Documenter):
        """Specialized Documenter subclass for objects on module level (functions,
        classes, data/constants).
        """

        def resolve_name(
            self, modname: str | None, parents: Any, path: str, base: str
        ) -> tuple[str | None, list[str]]:
            if modname is not None:
                return modname, [*parents, base]
            if path:
                modname = path.rstrip('.')
                return modname, [*parents, base]

            # if documenting a toplevel object without explicit module,
            # it can be contained in another auto directive ...
            modname = self._current_document.autodoc_module
            # ... or in the scope of a module directive
            if not modname:
                modname = self.env.ref_context.get('py:module')
            # ... else, it stays None, which means invalid
            return modname, [*parents, base]

    class ClassLevelDocumenter(Documenter):
        """Specialized Documenter subclass for objects on class level (methods,
        attributes).
        """

        def resolve_name(
            self, modname: str | None, parents: Any, path: str, base: str
        ) -> tuple[str | None, list[str]]:
            if modname is not None:
                return modname, [*parents, base]

            if path:
                mod_cls = path.rstrip('.')
            else:
                # if documenting a class-level object without path,
                # there must be a current class, either from a parent
                # auto directive ...
                mod_cls = self._current_document.autodoc_class
                # ... or from a class directive
                if not mod_cls:
                    mod_cls = self.env.ref_context.get('py:class', '')
                    # ... if still falsy, there's no way to know
                    if not mod_cls:
                        return None, []
            modname, sep, cls = mod_cls.rpartition('.')
            parents = [cls]
            # if the module name is still missing, get it like above
            if not modname:
                modname = self._current_document.autodoc_module
            if not modname:
                modname = self.env.ref_context.get('py:module')
            # ... else, it stays None, which means invalid
            return modname, [*parents, base]

    class DocstringSignatureMixin:
        """Mixin for FunctionDocumenter and MethodDocumenter to provide the
        feature of reading the signature from the docstring.
        """

        _new_docstrings: list[list[str]] | None = None
        _signatures: list[str] = []

        def _find_signature(self) -> tuple[str | None, str | None] | None:
            # candidates of the object name
            valid_names = [self.objpath[-1]]  # type: ignore[attr-defined]
            if isinstance(self, ClassDocumenter):
                valid_names.append('__init__')
                if hasattr(self.object, '__mro__'):
                    valid_names.extend(cls.__name__ for cls in self.object.__mro__)

            docstrings = self.get_doc()
            if docstrings is None:
                return None, None
            self._new_docstrings = docstrings[:]
            self._signatures = []
            result = None
            for i, doclines in enumerate(docstrings):
                for j, line in enumerate(doclines):
                    if not line:
                        # no lines in docstring, no match
                        break

                    if line.endswith('\\'):
                        line = line.rstrip('\\').rstrip()

                    # match first line of docstring against signature RE
                    match = py_ext_sig_re.match(line)
                    if not match:
                        break
                    exmod, path, base, tp_list, args, retann = match.groups()

                    # the base name must match ours
                    if base not in valid_names:
                        break

                    # re-prepare docstring to ignore more leading indentation
                    directive = self.directive  # type: ignore[attr-defined]
                    tab_width = directive.state.document.settings.tab_width
                    self._new_docstrings[i] = prepare_docstring(
                        '\n'.join(doclines[j + 1 :]), tab_width
                    )

                    if result is None:
                        # first signature
                        result = args, retann
                    else:
                        # subsequent signatures
                        self._signatures.append(f'({args}) -> {retann}')

                if result is not None:
                    # finish the loop when signature found
                    break

            return result

        def get_doc(self) -> list[list[str]] | None:
            if self._new_docstrings is not None:
                return self._new_docstrings
            return super().get_doc()  # type: ignore[misc]

        def format_signature(self, **kwargs: Any) -> str:
            self.args: str | None
            if self.args is None and self.config.autodoc_docstring_signature:  # type: ignore[attr-defined]
                # only act if a signature is not explicitly given already, and if
                # the feature is enabled
                result = self._find_signature()
                if result is not None:
                    self.args, self.retann = result
            sig = super().format_signature(**kwargs)  # type: ignore[misc]
            if self._signatures:
                return '\n'.join((sig, *self._signatures))
            else:
                return sig

    class DocstringStripSignatureMixin(DocstringSignatureMixin):
        """Mixin for AttributeDocumenter to provide the
        feature of stripping any function signature from the docstring.
        """

        def format_signature(self, **kwargs: Any) -> str:
            if self.args is None and self.config.autodoc_docstring_signature:  # type: ignore[attr-defined]
                # only act if a signature is not explicitly given already, and if
                # the feature is enabled
                result = self._find_signature()
                if result is not None:
                    # Discarding _args is a only difference with
                    # DocstringSignatureMixin.format_signature.
                    # Documenter.format_signature use self.args value to format.
                    _args, self.retann = result
            return super().format_signature(**kwargs)

    class FunctionDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter):  # type: ignore[misc]
        """Specialized Documenter subclass for functions."""

        objtype = 'function'
        member_order = 30

        @classmethod
        def can_document_member(
            cls: type[Documenter], member: Any, membername: str, isattr: bool, parent: Any
        ) -> bool:
            # supports functions, builtins and bound methods exported at the module level
            return (
                inspect.isfunction(member)
                or inspect.isbuiltin(member)
                or (inspect.isroutine(member) and isinstance(parent, ModuleDocumenter))
            )

        def format_args(self, **kwargs: Any) -> str:
            if self.config.autodoc_typehints in {'none', 'description'}:
                kwargs.setdefault('show_annotation', False)
            if self.config.autodoc_typehints_format == 'short':
                kwargs.setdefault('unqualified_typehints', True)
            if self.config.python_display_short_literal_types:
                kwargs.setdefault('short_literals', True)

            try:
                self._events.emit('autodoc-before-process-signature', self.object, False)
                sig = inspect.signature(
                    self.object, type_aliases=self.config.autodoc_type_aliases
                )
                args = stringify_signature(sig, **kwargs)
            except TypeError as exc:
                logger.warning(
                    __('Failed to get a function signature for %s: %s'), self.fullname, exc
                )
                return ''
            except ValueError:
                args = ''

            if self.config.strip_signature_backslash:
                # escape backslashes for reST
                args = args.replace('\\', '\\\\')
            return args

        def document_members(self, all_members: bool = False) -> None:
            pass

        def add_directive_header(self, sig: str) -> None:
            sourcename = self.get_sourcename()
            super().add_directive_header(sig)

            is_coro = inspect.iscoroutinefunction(self.object)
            is_acoro = inspect.isasyncgenfunction(self.object)
            if is_coro or is_acoro:
                self.add_line('   :async:', sourcename)

        def format_signature(self, **kwargs: Any) -> str:
            if self.config.autodoc_typehints_format == 'short':
                kwargs.setdefault('unqualified_typehints', True)
            if self.config.python_display_short_literal_types:
                kwargs.setdefault('short_literals', True)

            sigs = []
            if (
                self.analyzer
                and '.'.join(self.objpath) in self.analyzer.overloads
                and self.config.autodoc_typehints != 'none'
            ):
                # Use signatures for overloaded functions instead of the implementation function.
                overloaded = True
            else:
                overloaded = False
                sig = super().format_signature(**kwargs)
                sigs.append(sig)

            if inspect.is_singledispatch_function(self.object):
                # append signature of singledispatch'ed functions
                for typ, func in self.object.registry.items():
                    if typ is object:
                        pass  # default implementation. skipped.
                    else:
                        dispatchfunc = self.annotate_to_first_argument(func, typ)
                        if dispatchfunc:
                            documenter = FunctionDocumenter(self.directive, '')
                            documenter.object = dispatchfunc
                            documenter.objpath = ['']
                            sigs.append(documenter.format_signature())
            if overloaded and self.analyzer is not None:
                actual = inspect.signature(
                    self.object, type_aliases=self.config.autodoc_type_aliases
                )
                __globals__ = safe_getattr(self.object, '__globals__', {})
                for overload in self.analyzer.overloads['.'.join(self.objpath)]:
                    overload = self.merge_default_value(actual, overload)
                    overload = evaluate_signature(
                        overload, __globals__, self.config.autodoc_type_aliases
                    )

                    sig = stringify_signature(overload, **kwargs)
                    sigs.append(sig)

            return '\n'.join(sigs)

        def merge_default_value(self, actual: Signature, overload: Signature) -> Signature:
            """Merge default values of actual implementation to the overload variants."""
            parameters = list(overload.parameters.values())
            for i, param in enumerate(parameters):
                actual_param = actual.parameters.get(param.name)
                if actual_param and param.default == '...':
                    parameters[i] = param.replace(default=actual_param.default)

            return overload.replace(parameters=parameters)

        def annotate_to_first_argument(
            self, func: Callable[..., Any], typ: type
        ) -> Callable[..., Any] | None:
            """Annotate type hint to the first argument of function if needed."""
            try:
                sig = inspect.signature(func, type_aliases=self.config.autodoc_type_aliases)
            except TypeError as exc:
                logger.warning(
                    __('Failed to get a function signature for %s: %s'), self.fullname, exc
                )
                return None
            except ValueError:
                return None

            if len(sig.parameters) == 0:
                return None

            def dummy():  # type: ignore[no-untyped-def]  # NoQA: ANN202
                pass

            params = list(sig.parameters.values())
            if params[0].annotation is Parameter.empty:
                params[0] = params[0].replace(annotation=typ)
                try:
                    dummy.__signature__ = sig.replace(parameters=params)  # type: ignore[attr-defined]
                    return dummy
                except (AttributeError, TypeError):
                    # failed to update signature (ex. built-in or extension types)
                    return None

            return func

    class DecoratorDocumenter(FunctionDocumenter):
        """Specialized Documenter subclass for decorator functions."""

        objtype = 'decorator'

        # must be lower than FunctionDocumenter
        priority = -1

        def format_args(self, **kwargs: Any) -> str:
            args = super().format_args(**kwargs)
            if ',' in args:
                return args
            else:
                return ''

    # Types which have confusing metaclass signatures it would be best not to show.
    # These are listed by name, rather than storing the objects themselves, to avoid
    # needing to import the modules.
    _METACLASS_CALL_BLACKLIST = frozenset({
        'enum.EnumType.__call__',
    })

    # Types whose __new__ signature is a pass-through.
    _CLASS_NEW_BLACKLIST = frozenset({
        'typing.Generic.__new__',
    })

    class ClassDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter):  # type: ignore[misc]
        """Specialized Documenter subclass for classes."""

        objtype = 'class'
        member_order = 20
        option_spec: ClassVar[OptionSpec] = {
            'members': members_option,
            'undoc-members': bool_option,
            'no-index': bool_option,
            'no-index-entry': bool_option,
            'inherited-members': inherited_members_option,
            'show-inheritance': bool_option,
            'member-order': member_order_option,
            'exclude-members': exclude_members_option,
            'private-members': members_option,
            'special-members': members_option,
            'class-doc-from': class_doc_from_option,
            'noindex': bool_option,
        }

        # Must be higher than FunctionDocumenter, ClassDocumenter, and
        # AttributeDocumenter as NewType can be an attribute and is a class
        # after Python 3.10.
        priority = 15

        _signature_class: Any = None
        _signature_method_name: str = ''

        def __init__(self, *args: Any) -> None:
            super().__init__(*args)

            if self.config.autodoc_class_signature == 'separated':
                self.options = self.options.copy()

                # show __init__() method
                if self.options.special_members is None:
                    self.options['special-members'] = ['__new__', '__init__']
                else:
                    self.options.special_members.append('__new__')
                    self.options.special_members.append('__init__')

            merge_members_option(self.options)

        @classmethod
        def can_document_member(
            cls: type[Documenter], member: Any, membername: str, isattr: bool, parent: Any
        ) -> bool:
            return isinstance(member, type) or (
                isattr and isinstance(member, NewType | TypeVar)
            )

        def import_object(self, raiseerror: bool = False) -> bool:
            ret = super().import_object(raiseerror)
            # if the class is documented under another name, document it
            # as data/attribute
            if ret:
                if hasattr(self.object, '__name__'):
                    self.doc_as_attr = self.objpath[-1] != self.object.__name__
                else:
                    self.doc_as_attr = True
                if isinstance(self.object, NewType | TypeVar):
                    modname = getattr(self.object, '__module__', self.modname)
                    if modname != self.modname and self.modname.startswith(modname):
                        bases = self.modname[len(modname) :].strip('.').split('.')
                        self.objpath = bases + self.objpath
                        self.modname = modname
            return ret

        def _get_signature(self) -> tuple[Any | None, str | None, Signature | None]:
            if isinstance(self.object, NewType | TypeVar):
                # Suppress signature
                return None, None, None

            def get_user_defined_function_or_method(obj: Any, attr: str) -> Any:
                """Get the `attr` function or method from `obj`, if it is user-defined."""
                if inspect.is_builtin_class_method(obj, attr):
                    return None
                attr = self.get_attr(obj, attr, None)
                if not (inspect.ismethod(attr) or inspect.isfunction(attr)):
                    return None
                return attr

            # This sequence is copied from inspect._signature_from_callable.
            # ValueError means that no signature could be found, so we keep going.

            # First, we check if obj has a __signature__ attribute
            if hasattr(self.object, '__signature__'):
                object_sig = self.object.__signature__
                if isinstance(object_sig, Signature):
                    return None, None, object_sig
                if sys.version_info[:2] in {(3, 12), (3, 13)} and callable(object_sig):
                    # Support for enum.Enum.__signature__ in Python 3.12
                    if isinstance(object_sig_str := object_sig(), str):
                        return None, None, inspect.signature_from_str(object_sig_str)

            # Next, let's see if it has an overloaded __call__ defined
            # in its metaclass
            call = get_user_defined_function_or_method(type(self.object), '__call__')

            if call is not None:
                if f'{call.__module__}.{call.__qualname__}' in _METACLASS_CALL_BLACKLIST:
                    call = None

            if call is not None:
                self._events.emit('autodoc-before-process-signature', call, True)
                try:
                    sig = inspect.signature(
                        call,
                        bound_method=True,
                        type_aliases=self.config.autodoc_type_aliases,
                    )
                    return type(self.object), '__call__', sig
                except ValueError:
                    pass

            # Now we check if the 'obj' class has a '__new__' method
            new = get_user_defined_function_or_method(self.object, '__new__')

            if new is not None:
                if f'{new.__module__}.{new.__qualname__}' in _CLASS_NEW_BLACKLIST:
                    new = None

            if new is not None:
                self._events.emit('autodoc-before-process-signature', new, True)
                try:
                    sig = inspect.signature(
                        new,
                        bound_method=True,
                        type_aliases=self.config.autodoc_type_aliases,
                    )
                    return self.object, '__new__', sig
                except ValueError:
                    pass

            # Finally, we should have at least __init__ implemented
            init = get_user_defined_function_or_method(self.object, '__init__')
            if init is not None:
                self._events.emit('autodoc-before-process-signature', init, True)
                try:
                    sig = inspect.signature(
                        init,
                        bound_method=True,
                        type_aliases=self.config.autodoc_type_aliases,
                    )
                    return self.object, '__init__', sig
                except ValueError:
                    pass

            # None of the attributes are user-defined, so fall back to let inspect
            # handle it.
            # We don't know the exact method that inspect.signature will read
            # the signature from, so just pass the object itself to our hook.
            self._events.emit('autodoc-before-process-signature', self.object, False)
            try:
                sig = inspect.signature(
                    self.object,
                    bound_method=False,
                    type_aliases=self.config.autodoc_type_aliases,
                )
                return None, None, sig
            except ValueError:
                pass

            # Still no signature: happens e.g. for old-style classes
            # with __init__ in C and no `__text_signature__`.
            return None, None, None

        def format_args(self, **kwargs: Any) -> str:
            if self.config.autodoc_typehints in {'none', 'description'}:
                kwargs.setdefault('show_annotation', False)
            if self.config.autodoc_typehints_format == 'short':
                kwargs.setdefault('unqualified_typehints', True)
            if self.config.python_display_short_literal_types:
                kwargs.setdefault('short_literals', True)

            try:
                self._signature_class, _signature_method_name, sig = self._get_signature()
            except TypeError as exc:
                # __signature__ attribute contained junk
                logger.warning(
                    __('Failed to get a constructor signature for %s: %s'),
                    self.fullname,
                    exc,
                )
                return ''
            self._signature_method_name = _signature_method_name or ''

            if sig is None:
                return ''

            return stringify_signature(sig, show_return_annotation=False, **kwargs)

        def _find_signature(self) -> tuple[str | None, str | None] | None:
            result = super()._find_signature()
            if result is not None:
                # Strip a return value from signature of constructor in docstring (first entry)
                result = (result[0], None)

            for i, sig in enumerate(self._signatures):
                if sig.endswith(' -> None'):
                    # Strip a return value from signatures of constructor in docstring (subsequent
                    # entries)
                    self._signatures[i] = sig[:-8]

            return result

        def format_signature(self, **kwargs: Any) -> str:
            if self.doc_as_attr:
                return ''
            if self.config.autodoc_class_signature == 'separated':
                # do not show signatures
                return ''

            if self.config.autodoc_typehints_format == 'short':
                kwargs.setdefault('unqualified_typehints', True)
            if self.config.python_display_short_literal_types:
                kwargs.setdefault('short_literals', True)

            sig = super().format_signature()
            sigs = []

            overloads = self.get_overloaded_signatures()
            if overloads and self.config.autodoc_typehints != 'none':
                # Use signatures for overloaded methods instead of the implementation method.
                method = safe_getattr(
                    self._signature_class, self._signature_method_name, None
                )
                __globals__ = safe_getattr(method, '__globals__', {})
                for overload in overloads:
                    overload = evaluate_signature(
                        overload, __globals__, self.config.autodoc_type_aliases
                    )

                    parameters = list(overload.parameters.values())
                    overload = overload.replace(
                        parameters=parameters[1:], return_annotation=Parameter.empty
                    )
                    sig = stringify_signature(overload, **kwargs)
                    sigs.append(sig)
            else:
                sigs.append(sig)

            return '\n'.join(sigs)

        def get_overloaded_signatures(self) -> list[Signature]:
            if self._signature_class and self._signature_method_name:
                for cls in self._signature_class.__mro__:
                    try:
                        analyzer = ModuleAnalyzer.for_module(cls.__module__)
                        analyzer.analyze()
                        qualname = f'{cls.__qualname__}.{self._signature_method_name}'
                        if qualname in analyzer.overloads:
                            return analyzer.overloads.get(qualname, [])
                        elif qualname in analyzer.tagorder:
                            # the constructor is defined in the class, but not overridden.
                            return []
                    except PycodeError:
                        pass

            return []

        def get_canonical_fullname(self) -> str | None:
            __modname__ = safe_getattr(self.object, '__module__', self.modname)
            __qualname__ = safe_getattr(self.object, '__qualname__', None)
            if __qualname__ is None:
                __qualname__ = safe_getattr(self.object, '__name__', None)
            if __qualname__ and '<locals>' in __qualname__:
                # No valid qualname found if the object is defined as locals
                __qualname__ = None

            if __modname__ and __qualname__:
                return f'{__modname__}.{__qualname__}'
            else:
                return None

        def add_directive_header(self, sig: str) -> None:
            sourcename = self.get_sourcename()

            if self.doc_as_attr:
                self.directivetype = 'attribute'
            super().add_directive_header(sig)

            if isinstance(self.object, NewType | TypeVar):
                return

            if self.analyzer and '.'.join(self.objpath) in self.analyzer.finals:
                self.add_line('   :final:', sourcename)

            canonical_fullname = self.get_canonical_fullname()
            if (
                not self.doc_as_attr
                and not isinstance(self.object, NewType)
                and canonical_fullname
                and self.fullname != canonical_fullname
            ):
                self.add_line('   :canonical: %s' % canonical_fullname, sourcename)

            # add inheritance info, if wanted
            if not self.doc_as_attr and self.options.show_inheritance:
                if inspect.getorigbases(self.object):
                    # A subclass of generic types
                    # refs: PEP-560 <https://peps.python.org/pep-0560/>
                    bases = list(self.object.__orig_bases__)
                elif hasattr(self.object, '__bases__') and len(self.object.__bases__):
                    # A normal class
                    bases = list(self.object.__bases__)
                else:
                    bases = []

                self._events.emit(
                    'autodoc-process-bases', self.fullname, self.object, self.options, bases
                )

                mode = _get_render_mode(self.config.autodoc_typehints_format)
                base_classes = [restify(cls, mode=mode) for cls in bases]

                sourcename = self.get_sourcename()
                self.add_line('', sourcename)
                self.add_line('   ' + _('Bases: %s') % ', '.join(base_classes), sourcename)

        def get_object_members(self, want_all: bool) -> tuple[bool, list[ObjectMember]]:
            members = get_class_members(
                self.object,
                self.objpath,
                self.get_attr,
                self.config.autodoc_inherit_docstrings,
            )
            if not want_all:
                if not self.options.members:
                    return False, []
                # specific members given
                selected = []
                for name in self.options.members:
                    if name in members:
                        selected.append(members[name])
                    else:
                        logger.warning(
                            __('missing attribute %s in object %s'),
                            name,
                            self.fullname,
                            type='autodoc',
                        )
                return False, selected
            elif self.options.inherited_members:
                return False, list(members.values())
            else:
                return False, [m for m in members.values() if m.class_ == self.object]

        def get_doc(self) -> list[list[str]] | None:
            if isinstance(self.object, TypeVar):
                if self.object.__doc__ == TypeVar.__doc__:
                    return []
            if self.doc_as_attr:
                # Don't show the docstring of the class when it is an alias.
                if self.get_variable_comment():
                    return []
                else:
                    return None

            lines = getattr(self, '_new_docstrings', None)
            if lines is not None:
                return lines

            classdoc_from = self.options.get(
                'class-doc-from', self.config.autoclass_content
            )

            docstrings = []
            attrdocstring = getdoc(self.object, self.get_attr)
            if attrdocstring:
                docstrings.append(attrdocstring)

            # for classes, what the "docstring" is can be controlled via a
            # config value; the default is only the class docstring
            if classdoc_from in {'both', 'init'}:
                __init__ = self.get_attr(self.object, '__init__', None)
                initdocstring = getdoc(
                    __init__,
                    self.get_attr,
                    self.config.autodoc_inherit_docstrings,
                    self.object,
                    '__init__',
                )
                # for new-style classes, no __init__ means default __init__
                if initdocstring is not None and (
                    initdocstring == object.__init__.__doc__  # for pypy
                    or initdocstring.strip() == object.__init__.__doc__  # for !pypy
                ):
                    initdocstring = None
                if not initdocstring:
                    # try __new__
                    __new__ = self.get_attr(self.object, '__new__', None)
                    initdocstring = getdoc(
                        __new__,
                        self.get_attr,
                        self.config.autodoc_inherit_docstrings,
                        self.object,
                        '__new__',
                    )
                    # for new-style classes, no __new__ means default __new__
                    if initdocstring is not None and (
                        initdocstring == object.__new__.__doc__  # for pypy
                        or initdocstring.strip() == object.__new__.__doc__  # for !pypy
                    ):
                        initdocstring = None
                if initdocstring:
                    if classdoc_from == 'init':
                        docstrings = [initdocstring]
                    else:
                        docstrings.append(initdocstring)

            tab_width = self.directive.state.document.settings.tab_width
            return [prepare_docstring(docstring, tab_width) for docstring in docstrings]

        def get_variable_comment(self) -> list[str] | None:
            try:
                key = ('', '.'.join(self.objpath))
                if self.doc_as_attr:
                    analyzer = ModuleAnalyzer.for_module(self.modname)
                else:
                    analyzer = ModuleAnalyzer.for_module(self.get_real_modname())
                analyzer.analyze()
                return list(analyzer.attr_docs.get(key, []))
            except PycodeError:
                return None

        def add_content(self, more_content: StringList | None) -> None:
            mode = _get_render_mode(self.config.autodoc_typehints_format)
            short_literals = self.config.python_display_short_literal_types

            if isinstance(self.object, NewType):
                supertype = restify(self.object.__supertype__, mode=mode)

                more_content = StringList([_('alias of %s') % supertype, ''], source='')
            if isinstance(self.object, TypeVar):
                attrs = [repr(self.object.__name__)]
                attrs.extend(
                    stringify_annotation(constraint, mode, short_literals=short_literals)
                    for constraint in self.object.__constraints__
                )
                if self.object.__bound__:
                    bound = restify(self.object.__bound__, mode=mode)
                    attrs.append(r'bound=\ ' + bound)
                if self.object.__covariant__:
                    attrs.append('covariant=True')
                if self.object.__contravariant__:
                    attrs.append('contravariant=True')

                more_content = StringList(
                    [_('alias of TypeVar(%s)') % ', '.join(attrs), ''], source=''
                )
            if self.doc_as_attr and self.modname != self.get_real_modname():
                try:
                    # override analyzer to obtain doccomment around its definition.
                    self.analyzer = ModuleAnalyzer.for_module(self.modname)
                    self.analyzer.analyze()
                except PycodeError:
                    pass

            if self.doc_as_attr and not self.get_variable_comment():
                try:
                    alias = restify(self.object, mode=mode)
                    more_content = StringList([_('alias of %s') % alias], source='')
                except AttributeError:
                    pass  # Invalid class object is passed.

            super().add_content(more_content)

        def document_members(self, all_members: bool = False) -> None:
            if self.doc_as_attr:
                return
            super().document_members(all_members)

        def generate(
            self,
            more_content: StringList | None = None,
            real_modname: str | None = None,
            check_module: bool = False,
            all_members: bool = False,
        ) -> None:
            # Do not pass real_modname and use the name from the __module__
            # attribute of the class.
            # If a class gets imported into the module real_modname
            # the analyzer won't find the source of the class, if
            # it looks in real_modname.
            return super().generate(
                more_content=more_content,
                check_module=check_module,
                all_members=all_members,
            )

    class ExceptionDocumenter(ClassDocumenter):
        """Specialized ClassDocumenter subclass for exceptions."""

        objtype = 'exception'
        member_order = 10

        # needs a higher priority than ClassDocumenter
        priority = ClassDocumenter.priority + 5

        @classmethod
        def can_document_member(
            cls: type[Documenter], member: Any, membername: str, isattr: bool, parent: Any
        ) -> bool:
            try:
                return isinstance(member, type) and issubclass(member, BaseException)
            except TypeError as exc:
                # It's possible for a member to be considered a type, but fail
                # issubclass checks due to not being a class. For example:
                # https://github.com/sphinx-doc/sphinx/issues/11654#issuecomment-1696790436
                msg = (
                    f'{cls.__name__} failed to discern if member {member} with'
                    f' membername {membername} is a BaseException subclass.'
                )
                raise ValueError(msg) from exc

    class DataDocumenterMixinBase:
        # define types of instance variables
        config: Config
        env: BuildEnvironment
        modname: str
        parent: Any
        object: Any
        objpath: list[str]

        def should_suppress_directive_header(self) -> bool:
            """Check directive header should be suppressed."""
            return False

        def should_suppress_value_header(self) -> bool:
            """Check :value: header should be suppressed."""
            return False

        def update_content(self, more_content: StringList) -> None:
            """Update docstring, for example with TypeVar variance."""
            pass

    class GenericAliasMixin(DataDocumenterMixinBase):
        """Mixin for DataDocumenter and AttributeDocumenter to provide the feature for
        supporting GenericAliases.
        """

        def should_suppress_directive_header(self) -> bool:
            return (
                inspect.isgenericalias(self.object)
                or super().should_suppress_directive_header()
            )

        def update_content(self, more_content: StringList) -> None:
            if inspect.isgenericalias(self.object):
                mode = _get_render_mode(self.config.autodoc_typehints_format)
                alias = restify(self.object, mode=mode)

                more_content.append(_('alias of %s') % alias, '')
                more_content.append('', '')

            super().update_content(more_content)

    class UninitializedGlobalVariableMixin(DataDocumenterMixinBase):
        """Mixin for DataDocumenter to provide the feature for supporting uninitialized
        (type annotation only) global variables.
        """

        def import_object(self, raiseerror: bool = False) -> bool:
            try:
                return super().import_object(raiseerror=True)  # type: ignore[misc]
            except ImportError as exc:
                # annotation only instance variable (PEP-526)
                try:
                    with mock(self.config.autodoc_mock_imports):
                        parent = import_module(self.modname)
                        annotations = get_type_hints(
                            parent,
                            None,
                            self.config.autodoc_type_aliases,
                            include_extras=True,
                        )
                        if self.objpath[-1] in annotations:
                            self.object = UNINITIALIZED_ATTR
                            self.parent = parent
                            return True
                except ImportError:
                    pass

                if raiseerror:
                    raise
                logger.warning(exc.args[0], type='autodoc', subtype='import_object')
                self.env.note_reread()
                return False

        def should_suppress_value_header(self) -> bool:
            return (
                self.object is UNINITIALIZED_ATTR or super().should_suppress_value_header()
            )

        def get_doc(self) -> list[list[str]] | None:
            if self.object is UNINITIALIZED_ATTR:
                return []
            else:
                return super().get_doc()  # type: ignore[misc]

    class DataDocumenter(
        GenericAliasMixin, UninitializedGlobalVariableMixin, ModuleLevelDocumenter
    ):
        """Specialized Documenter subclass for data items."""

        objtype = 'data'
        member_order = 40
        priority = -10
        option_spec: ClassVar[OptionSpec] = dict(ModuleLevelDocumenter.option_spec)
        option_spec['annotation'] = annotation_option
        option_spec['no-value'] = bool_option

        @classmethod
        def can_document_member(
            cls: type[Documenter], member: Any, membername: str, isattr: bool, parent: Any
        ) -> bool:
            return isinstance(parent, ModuleDocumenter) and isattr

        def update_annotations(self, parent: Any) -> None:
            """Update __annotations__ to support type_comment and so on."""
            annotations = dict(inspect.getannotations(parent))
            parent.__annotations__ = annotations

            try:
                analyzer = ModuleAnalyzer.for_module(self.modname)
                analyzer.analyze()
                for (classname, attrname), annotation in analyzer.annotations.items():
                    if not classname and attrname not in annotations:
                        annotations[attrname] = annotation
            except PycodeError:
                pass

        def import_object(self, raiseerror: bool = False) -> bool:
            ret = super().import_object(raiseerror)
            if self.parent:
                self.update_annotations(self.parent)

            return ret

        def should_suppress_value_header(self) -> bool:
            if super().should_suppress_value_header():
                return True
            else:
                doc = self.get_doc() or []
                docstring, metadata = separate_metadata(
                    '\n'.join(functools.reduce(operator.iadd, doc, []))
                )
                if 'hide-value' in metadata:
                    return True

            return False

        def add_directive_header(self, sig: str) -> None:
            super().add_directive_header(sig)
            sourcename = self.get_sourcename()
            if (
                self.options.annotation is SUPPRESS
                or self.should_suppress_directive_header()
            ):
                pass
            elif self.options.annotation:
                self.add_line('   :annotation: %s' % self.options.annotation, sourcename)
            else:
                if self.config.autodoc_typehints != 'none':
                    # obtain annotation for this data
                    annotations = get_type_hints(
                        self.parent,
                        None,
                        self.config.autodoc_type_aliases,
                        include_extras=True,
                    )
                    if self.objpath[-1] in annotations:
                        mode = _get_render_mode(self.config.autodoc_typehints_format)
                        short_literals = self.config.python_display_short_literal_types
                        objrepr = stringify_annotation(
                            annotations.get(self.objpath[-1]),
                            mode,
                            short_literals=short_literals,
                        )
                        self.add_line('   :type: ' + objrepr, sourcename)

                try:
                    if (
                        self.options.no_value
                        or self.should_suppress_value_header()
                        or ismock(self.object)
                    ):
                        pass
                    else:
                        objrepr = object_description(self.object)
                        self.add_line('   :value: ' + objrepr, sourcename)
                except ValueError:
                    pass

        def document_members(self, all_members: bool = False) -> None:
            pass

        def get_real_modname(self) -> str:
            real_modname = self.get_attr(self.parent or self.object, '__module__', None)
            return real_modname or self.modname

        def get_module_comment(self, attrname: str) -> list[str] | None:
            try:
                analyzer = ModuleAnalyzer.for_module(self.modname)
                analyzer.analyze()
                key = ('', attrname)
                if key in analyzer.attr_docs:
                    return list(analyzer.attr_docs[key])
            except PycodeError:
                pass

            return None

        def get_doc(self) -> list[list[str]] | None:
            # Check the variable has a docstring-comment
            comment = self.get_module_comment(self.objpath[-1])
            if comment:
                return [comment]
            else:
                return super().get_doc()

        def add_content(self, more_content: StringList | None) -> None:
            # Disable analyzing variable comment on Documenter.add_content() to control it on
            # DataDocumenter.add_content()
            self.analyzer = None

            if not more_content:
                more_content = StringList()

            self.update_content(more_content)
            super().add_content(more_content)

    class MethodDocumenter(DocstringSignatureMixin, ClassLevelDocumenter):  # type: ignore[misc]
        """Specialized Documenter subclass for methods (normal, static and class)."""

        objtype = 'method'
        directivetype = 'method'
        member_order = 50
        priority = 1  # must be more than FunctionDocumenter

        @classmethod
        def can_document_member(
            cls: type[Documenter], member: Any, membername: str, isattr: bool, parent: Any
        ) -> bool:
            return inspect.isroutine(member) and not isinstance(parent, ModuleDocumenter)

        def import_object(self, raiseerror: bool = False) -> bool:
            ret = super().import_object(raiseerror)
            if not ret:
                return ret

            # to distinguish classmethod/staticmethod
            obj = self.parent.__dict__.get(self.object_name, self.object)
            if inspect.isstaticmethod(obj, cls=self.parent, name=self.object_name):
                # document static members before regular methods
                self.member_order -= 1
            elif inspect.isclassmethod(obj):
                # document class methods before static methods as
                # they usually behave as alternative constructors
                self.member_order -= 2
            return ret

        def format_args(self, **kwargs: Any) -> str:
            if self.config.autodoc_typehints in {'none', 'description'}:
                kwargs.setdefault('show_annotation', False)
            if self.config.autodoc_typehints_format == 'short':
                kwargs.setdefault('unqualified_typehints', True)
            if self.config.python_display_short_literal_types:
                kwargs.setdefault('short_literals', True)

            try:
                if self.object == object.__init__ and self.parent != object:  # NoQA: E721
                    # Classes not having own __init__() method are shown as no arguments.
                    #
                    # Note: The signature of object.__init__() is (self, /, *args, **kwargs).
                    #       But it makes users confused.
                    args = '()'
                else:
                    if inspect.isstaticmethod(
                        self.object, cls=self.parent, name=self.object_name
                    ):
                        self._events.emit(
                            'autodoc-before-process-signature', self.object, False
                        )
                        sig = inspect.signature(
                            self.object,
                            bound_method=False,
                            type_aliases=self.config.autodoc_type_aliases,
                        )
                    else:
                        self._events.emit(
                            'autodoc-before-process-signature', self.object, True
                        )
                        sig = inspect.signature(
                            self.object,
                            bound_method=True,
                            type_aliases=self.config.autodoc_type_aliases,
                        )
                    args = stringify_signature(sig, **kwargs)
            except TypeError as exc:
                logger.warning(
                    __('Failed to get a method signature for %s: %s'), self.fullname, exc
                )
                return ''
            except ValueError:
                args = ''

            if self.config.strip_signature_backslash:
                # escape backslashes for reST
                args = args.replace('\\', '\\\\')
            return args

        def add_directive_header(self, sig: str) -> None:
            super().add_directive_header(sig)

            sourcename = self.get_sourcename()
            obj = self.parent.__dict__.get(self.object_name, self.object)
            if inspect.isabstractmethod(obj):
                self.add_line('   :abstractmethod:', sourcename)
            if inspect.iscoroutinefunction(obj) or inspect.isasyncgenfunction(obj):
                self.add_line('   :async:', sourcename)
            if (
                inspect.is_classmethod_like(obj)
                or inspect.is_singledispatch_method(obj)
                and inspect.is_classmethod_like(obj.func)
            ):
                self.add_line('   :classmethod:', sourcename)
            if inspect.isstaticmethod(obj, cls=self.parent, name=self.object_name):
                self.add_line('   :staticmethod:', sourcename)
            if self.analyzer and '.'.join(self.objpath) in self.analyzer.finals:
                self.add_line('   :final:', sourcename)

        def document_members(self, all_members: bool = False) -> None:
            pass

        def format_signature(self, **kwargs: Any) -> str:
            if self.config.autodoc_typehints_format == 'short':
                kwargs.setdefault('unqualified_typehints', True)
            if self.config.python_display_short_literal_types:
                kwargs.setdefault('short_literals', True)

            sigs = []
            if (
                self.analyzer
                and '.'.join(self.objpath) in self.analyzer.overloads
                and self.config.autodoc_typehints != 'none'
            ):
                # Use signatures for overloaded methods instead of the implementation method.
                overloaded = True
            else:
                overloaded = False
                sig = super().format_signature(**kwargs)
                sigs.append(sig)

            meth = self.parent.__dict__.get(self.objpath[-1])
            if inspect.is_singledispatch_method(meth):
                # append signature of singledispatch'ed functions
                for typ, func in meth.dispatcher.registry.items():
                    if typ is object:
                        pass  # default implementation. skipped.
                    else:
                        if inspect.isclassmethod(func):
                            func = func.__func__
                        dispatchmeth = self.annotate_to_first_argument(func, typ)
                        if dispatchmeth:
                            documenter = MethodDocumenter(self.directive, '')
                            documenter.parent = self.parent
                            documenter.object = dispatchmeth
                            documenter.objpath = ['']
                            sigs.append(documenter.format_signature())
            if overloaded and self.analyzer is not None:
                if inspect.isstaticmethod(
                    self.object, cls=self.parent, name=self.object_name
                ):
                    actual = inspect.signature(
                        self.object,
                        bound_method=False,
                        type_aliases=self.config.autodoc_type_aliases,
                    )
                else:
                    actual = inspect.signature(
                        self.object,
                        bound_method=True,
                        type_aliases=self.config.autodoc_type_aliases,
                    )

                __globals__ = safe_getattr(self.object, '__globals__', {})
                for overload in self.analyzer.overloads['.'.join(self.objpath)]:
                    overload = self.merge_default_value(actual, overload)
                    overload = evaluate_signature(
                        overload, __globals__, self.config.autodoc_type_aliases
                    )

                    if not inspect.isstaticmethod(
                        self.object, cls=self.parent, name=self.object_name
                    ):
                        parameters = list(overload.parameters.values())
                        overload = overload.replace(parameters=parameters[1:])
                    sig = stringify_signature(overload, **kwargs)
                    sigs.append(sig)

            return '\n'.join(sigs)

        def merge_default_value(self, actual: Signature, overload: Signature) -> Signature:
            """Merge default values of actual implementation to the overload variants."""
            parameters = list(overload.parameters.values())
            for i, param in enumerate(parameters):
                actual_param = actual.parameters.get(param.name)
                if actual_param and param.default == '...':
                    parameters[i] = param.replace(default=actual_param.default)

            return overload.replace(parameters=parameters)

        def annotate_to_first_argument(
            self, func: Callable[..., Any], typ: type
        ) -> Callable[..., Any] | None:
            """Annotate type hint to the first argument of function if needed."""
            try:
                sig = inspect.signature(func, type_aliases=self.config.autodoc_type_aliases)
            except TypeError as exc:
                logger.warning(
                    __('Failed to get a method signature for %s: %s'), self.fullname, exc
                )
                return None
            except ValueError:
                return None

            if len(sig.parameters) == 1:
                return None

            def dummy():  # type: ignore[no-untyped-def]  # NoQA: ANN202
                pass

            params = list(sig.parameters.values())
            if params[1].annotation is Parameter.empty:
                params[1] = params[1].replace(annotation=typ)
                try:
                    dummy.__signature__ = sig.replace(  # type: ignore[attr-defined]
                        parameters=params
                    )
                    return dummy
                except (AttributeError, TypeError):
                    # failed to update signature (ex. built-in or extension types)
                    return None

            return func

        def get_doc(self) -> list[list[str]] | None:
            if self._new_docstrings is not None:
                # docstring already returned previously, then modified by
                # `DocstringSignatureMixin`.  Just return the previously-computed
                # result, so that we don't lose the processing done by
                # `DocstringSignatureMixin`.
                return self._new_docstrings
            if self.objpath[-1] == '__init__':
                docstring = getdoc(
                    self.object,
                    self.get_attr,
                    self.config.autodoc_inherit_docstrings,
                    self.parent,
                    self.object_name,
                )
                if docstring is not None and (
                    docstring == object.__init__.__doc__  # for pypy
                    or docstring.strip() == object.__init__.__doc__  # for !pypy
                ):
                    docstring = None
                if docstring:
                    tab_width = self.directive.state.document.settings.tab_width
                    return [prepare_docstring(docstring, tabsize=tab_width)]
                else:
                    return []
            elif self.objpath[-1] == '__new__':
                docstring = getdoc(
                    self.object,
                    self.get_attr,
                    self.config.autodoc_inherit_docstrings,
                    self.parent,
                    self.object_name,
                )
                if docstring is not None and (
                    docstring == object.__new__.__doc__  # for pypy
                    or docstring.strip() == object.__new__.__doc__  # for !pypy
                ):
                    docstring = None
                if docstring:
                    tab_width = self.directive.state.document.settings.tab_width
                    return [prepare_docstring(docstring, tabsize=tab_width)]
                else:
                    return []
            else:
                return super().get_doc()

    class NonDataDescriptorMixin(DataDocumenterMixinBase):
        """Mixin for AttributeDocumenter to provide the feature for supporting non
        data-descriptors.

        .. note:: This mix-in must be inherited after other mix-ins.  Otherwise, docstring
                  and :value: header will be suppressed unexpectedly.
        """

        def import_object(self, raiseerror: bool = False) -> bool:
            ret = super().import_object(raiseerror)  # type: ignore[misc]
            if ret and not inspect.isattributedescriptor(self.object):
                self.non_data_descriptor = True
            else:
                self.non_data_descriptor = False

            return ret

        def should_suppress_value_header(self) -> bool:
            return (
                not getattr(self, 'non_data_descriptor', False)
                or super().should_suppress_directive_header()
            )

        def get_doc(self) -> list[list[str]] | None:
            if getattr(self, 'non_data_descriptor', False):
                # the docstring of non datadescriptor is very probably the wrong thing
                # to display
                return None
            else:
                return super().get_doc()  # type: ignore[misc]

    class SlotsMixin(DataDocumenterMixinBase):
        """Mixin for AttributeDocumenter to provide the feature for supporting __slots__."""

        def isslotsattribute(self) -> bool:
            """Check the subject is an attribute in __slots__."""
            try:
                if parent___slots__ := inspect.getslots(self.parent):
                    return self.objpath[-1] in parent___slots__
                else:
                    return False
            except (ValueError, TypeError):
                return False

        def import_object(self, raiseerror: bool = False) -> bool:
            ret = super().import_object(raiseerror)  # type: ignore[misc]
            if self.isslotsattribute():
                self.object = SLOTSATTR

            return ret

        def should_suppress_value_header(self) -> bool:
            if self.object is SLOTSATTR:
                return True
            else:
                return super().should_suppress_value_header()

        def get_doc(self) -> list[list[str]] | None:
            if self.object is SLOTSATTR:
                try:
                    parent___slots__ = inspect.getslots(self.parent)
                    if parent___slots__ and (
                        docstring := parent___slots__.get(self.objpath[-1])
                    ):
                        docstring = prepare_docstring(docstring)
                        return [docstring]
                    else:
                        return []
                except ValueError as exc:
                    logger.warning(
                        __('Invalid __slots__ found on %s. Ignored.'),
                        (self.parent.__qualname__, exc),
                        type='autodoc',
                    )
                    return []
            else:
                return super().get_doc()  # type: ignore[misc]

    class RuntimeInstanceAttributeMixin(DataDocumenterMixinBase):
        """Mixin for AttributeDocumenter to provide the feature for supporting runtime
        instance attributes (that are defined in __init__() methods with doc-comments).

        Example::

            class Foo:
                def __init__(self):
                    self.attr = None  #: This is a target of this mix-in.
        """

        RUNTIME_INSTANCE_ATTRIBUTE = object()

        def is_runtime_instance_attribute(self, parent: Any) -> bool:
            """Check the subject is an attribute defined in __init__()."""
            # An instance variable defined in __init__().
            if self.get_attribute_comment(parent, self.objpath[-1]):  # type: ignore[attr-defined]
                return True
            return self.is_runtime_instance_attribute_not_commented(parent)

        def is_runtime_instance_attribute_not_commented(self, parent: Any) -> bool:
            """Check the subject is an attribute defined in __init__() without comment."""
            for cls in inspect.getmro(parent):
                try:
                    module = safe_getattr(cls, '__module__')
                    qualname = safe_getattr(cls, '__qualname__')

                    analyzer = ModuleAnalyzer.for_module(module)
                    analyzer.analyze()
                    if qualname and self.objpath:
                        key = f'{qualname}.{self.objpath[-1]}'
                        if key in analyzer.tagorder:
                            return True
                except (AttributeError, PycodeError):
                    pass

            return False

        def import_object(self, raiseerror: bool = False) -> bool:
            """Check the existence of runtime instance attribute after failing to import the
            attribute.
            """
            try:
                return super().import_object(raiseerror=True)  # type: ignore[misc]
            except ImportError as exc:
                try:
                    with mock(self.config.autodoc_mock_imports):
                        ret = import_object(
                            self.modname,
                            self.objpath[:-1],
                            'class',
                            attrgetter=self.get_attr,  # type: ignore[attr-defined]
                        )
                        parent = ret[3]
                        if self.is_runtime_instance_attribute(parent):
                            self.object = self.RUNTIME_INSTANCE_ATTRIBUTE
                            self.parent = parent
                            return True
                except ImportError:
                    pass

                if raiseerror:
                    raise
                logger.warning(exc.args[0], type='autodoc', subtype='import_object')
                self.env.note_reread()
                return False

        def should_suppress_value_header(self) -> bool:
            return (
                self.object is self.RUNTIME_INSTANCE_ATTRIBUTE
                or super().should_suppress_value_header()
            )

        def get_doc(self) -> list[list[str]] | None:
            if (
                self.object is self.RUNTIME_INSTANCE_ATTRIBUTE
                and self.is_runtime_instance_attribute_not_commented(self.parent)
            ):
                return None
            else:
                return super().get_doc()  # type: ignore[misc]

    class UninitializedInstanceAttributeMixin(DataDocumenterMixinBase):
        """Mixin for AttributeDocumenter to provide the feature for supporting uninitialized
        instance attributes (PEP-526 styled, annotation only attributes).

        Example::

            class Foo:
                attr: int  #: This is a target of this mix-in.
        """

        def is_uninitialized_instance_attribute(self, parent: Any) -> bool:
            """Check the subject is an annotation only attribute."""
            annotations = get_type_hints(
                parent, None, self.config.autodoc_type_aliases, include_extras=True
            )
            return self.objpath[-1] in annotations

        def import_object(self, raiseerror: bool = False) -> bool:
            """Check the existence of uninitialized instance attribute when failed to import
            the attribute.
            """
            try:
                return super().import_object(raiseerror=True)  # type: ignore[misc]
            except ImportError as exc:
                try:
                    ret = import_object(
                        self.modname,
                        self.objpath[:-1],
                        'class',
                        attrgetter=self.get_attr,  # type: ignore[attr-defined]
                    )
                    parent = ret[3]
                    if self.is_uninitialized_instance_attribute(parent):
                        self.object = UNINITIALIZED_ATTR
                        self.parent = parent
                        return True
                except ImportError:
                    pass

                if raiseerror:
                    raise
                logger.warning(exc.args[0], type='autodoc', subtype='import_object')
                self.env.note_reread()
                return False

        def should_suppress_value_header(self) -> bool:
            return (
                self.object is UNINITIALIZED_ATTR or super().should_suppress_value_header()
            )

        def get_doc(self) -> list[list[str]] | None:
            if self.object is UNINITIALIZED_ATTR:
                return None
            return super().get_doc()  # type: ignore[misc]

    class AttributeDocumenter(  # type: ignore[misc]
        GenericAliasMixin,
        SlotsMixin,
        RuntimeInstanceAttributeMixin,
        UninitializedInstanceAttributeMixin,
        NonDataDescriptorMixin,
        DocstringStripSignatureMixin,
        ClassLevelDocumenter,
    ):
        """Specialized Documenter subclass for attributes."""

        objtype = 'attribute'
        member_order = 60
        option_spec: ClassVar[OptionSpec] = dict(ModuleLevelDocumenter.option_spec)
        option_spec['annotation'] = annotation_option
        option_spec['no-value'] = bool_option

        # must be higher than the MethodDocumenter, else it will recognize
        # some non-data descriptors as methods
        priority = 10

        @staticmethod
        def is_function_or_method(obj: Any) -> bool:
            return (
                inspect.isfunction(obj) or inspect.isbuiltin(obj) or inspect.ismethod(obj)
            )

        @classmethod
        def can_document_member(
            cls: type[Documenter], member: Any, membername: str, isattr: bool, parent: Any
        ) -> bool:
            if isinstance(parent, ModuleDocumenter):
                return False
            if inspect.isattributedescriptor(member):
                return True
            return not inspect.isroutine(member) and not isinstance(member, type)

        def document_members(self, all_members: bool = False) -> None:
            pass

        def update_annotations(self, parent: Any) -> None:
            """Update __annotations__ to support type_comment and so on."""
            try:
                annotations = dict(inspect.getannotations(parent))
                parent.__annotations__ = annotations

                for cls in inspect.getmro(parent):
                    try:
                        module = safe_getattr(cls, '__module__')
                        qualname = safe_getattr(cls, '__qualname__')

                        analyzer = ModuleAnalyzer.for_module(module)
                        analyzer.analyze()
                        anns = analyzer.annotations
                        for (classname, attrname), annotation in anns.items():
                            if classname == qualname and attrname not in annotations:
                                annotations[attrname] = annotation
                    except (AttributeError, PycodeError):
                        pass
            except (AttributeError, TypeError):
                # Failed to set __annotations__ (built-in, extensions, etc.)
                pass

        def import_object(self, raiseerror: bool = False) -> bool:
            ret = super().import_object(raiseerror)
            if inspect.isenumattribute(self.object):
                self.object = self.object.value
            if self.parent:
                self.update_annotations(self.parent)

            return ret

        def get_real_modname(self) -> str:
            real_modname = self.get_attr(self.parent or self.object, '__module__', None)
            return real_modname or self.modname

        def should_suppress_value_header(self) -> bool:
            if super().should_suppress_value_header():
                return True
            else:
                doc = self.get_doc()
                if doc:
                    docstring, metadata = separate_metadata(
                        '\n'.join(functools.reduce(operator.iadd, doc, []))
                    )
                    if 'hide-value' in metadata:
                        return True

            return False

        def add_directive_header(self, sig: str) -> None:
            super().add_directive_header(sig)
            sourcename = self.get_sourcename()
            if (
                self.options.annotation is SUPPRESS
                or self.should_suppress_directive_header()
            ):
                pass
            elif self.options.annotation:
                self.add_line('   :annotation: %s' % self.options.annotation, sourcename)
            else:
                if self.config.autodoc_typehints != 'none':
                    # obtain type annotation for this attribute
                    annotations = get_type_hints(
                        self.parent,
                        None,
                        self.config.autodoc_type_aliases,
                        include_extras=True,
                    )
                    if self.objpath[-1] in annotations:
                        mode = _get_render_mode(self.config.autodoc_typehints_format)
                        short_literals = self.config.python_display_short_literal_types
                        objrepr = stringify_annotation(
                            annotations.get(self.objpath[-1]),
                            mode,
                            short_literals=short_literals,
                        )
                        self.add_line('   :type: ' + objrepr, sourcename)

                try:
                    if (
                        self.options.no_value
                        or self.should_suppress_value_header()
                        or ismock(self.object)
                    ):
                        pass
                    else:
                        objrepr = object_description(self.object)
                        self.add_line('   :value: ' + objrepr, sourcename)
                except ValueError:
                    pass

        def get_attribute_comment(self, parent: Any, attrname: str) -> list[str] | None:
            for cls in inspect.getmro(parent):
                try:
                    module = safe_getattr(cls, '__module__')
                    qualname = safe_getattr(cls, '__qualname__')

                    analyzer = ModuleAnalyzer.for_module(module)
                    analyzer.analyze()
                    if qualname and self.objpath:
                        key = (qualname, attrname)
                        if key in analyzer.attr_docs:
                            return list(analyzer.attr_docs[key])
                except (AttributeError, PycodeError):
                    pass

            return None

        def get_doc(self) -> list[list[str]] | None:
            # Check the attribute has a docstring-comment
            comment = self.get_attribute_comment(self.parent, self.objpath[-1])
            if comment:
                return [comment]

            try:
                # Disable `autodoc_inherit_docstring` temporarily to avoid to obtain
                # a docstring from the value which descriptor returns unexpectedly.
                # See: https://github.com/sphinx-doc/sphinx/issues/7805
                orig = self.config.autodoc_inherit_docstrings
                self.config.autodoc_inherit_docstrings = False
                return super().get_doc()
            finally:
                self.config.autodoc_inherit_docstrings = orig

        def add_content(self, more_content: StringList | None) -> None:
            # Disable analyzing attribute comment on Documenter.add_content() to control it on
            # AttributeDocumenter.add_content()
            self.analyzer = None

            if more_content is None:
                more_content = StringList()
            self.update_content(more_content)
            super().add_content(more_content)

    class PropertyDocumenter(DocstringStripSignatureMixin, ClassLevelDocumenter):  # type: ignore[misc]
        """Specialized Documenter subclass for properties."""

        objtype = 'property'
        member_order = 60

        # before AttributeDocumenter
        priority = AttributeDocumenter.priority + 1

        @classmethod
        def can_document_member(
            cls: type[Documenter], member: Any, membername: str, isattr: bool, parent: Any
        ) -> bool:
            if isinstance(parent, ClassDocumenter):
                if inspect.isproperty(member):
                    return True
                else:
                    __dict__ = safe_getattr(parent.object, '__dict__', {})
                    obj = __dict__.get(membername)
                    return isinstance(obj, classmethod) and inspect.isproperty(obj.__func__)
            else:
                return False

        def import_object(self, raiseerror: bool = False) -> bool:
            """Check the existence of uninitialized instance attribute when failed to import
            the attribute.
            """
            ret = super().import_object(raiseerror)
            if ret and not inspect.isproperty(self.object):
                __dict__ = safe_getattr(self.parent, '__dict__', {})
                obj = __dict__.get(self.objpath[-1])
                if isinstance(obj, classmethod) and inspect.isproperty(obj.__func__):
                    self.object = obj.__func__
                    self.isclassmethod: bool = True
                    return True
                else:
                    return False

            self.isclassmethod = False
            return ret

        def format_args(self, **kwargs: Any) -> str:
            func = self._get_property_getter()
            if func is None:
                return ''

            # update the annotations of the property getter
            self._events.emit('autodoc-before-process-signature', func, False)
            # correctly format the arguments for a property
            return super().format_args(**kwargs)

        def document_members(self, all_members: bool = False) -> None:
            pass

        def get_real_modname(self) -> str:
            real_modname = self.get_attr(self.parent or self.object, '__module__', None)
            return real_modname or self.modname

        def add_directive_header(self, sig: str) -> None:
            super().add_directive_header(sig)
            sourcename = self.get_sourcename()
            if inspect.isabstractmethod(self.object):
                self.add_line('   :abstractmethod:', sourcename)
            if self.isclassmethod:
                self.add_line('   :classmethod:', sourcename)

            func = self._get_property_getter()
            if func is None or self.config.autodoc_typehints == 'none':
                return

            try:
                signature = inspect.signature(
                    func, type_aliases=self.config.autodoc_type_aliases
                )
                if signature.return_annotation is not Parameter.empty:
                    mode = _get_render_mode(self.config.autodoc_typehints_format)
                    short_literals = self.config.python_display_short_literal_types
                    objrepr = stringify_annotation(
                        signature.return_annotation, mode, short_literals=short_literals
                    )
                    self.add_line('   :type: ' + objrepr, sourcename)
            except TypeError as exc:
                logger.warning(
                    __('Failed to get a function signature for %s: %s'), self.fullname, exc
                )
                pass
            except ValueError:
                pass

        def _get_property_getter(self) -> Callable[..., Any] | None:
            if safe_getattr(self.object, 'fget', None):  # property
                return self.object.fget
            if safe_getattr(self.object, 'func', None):  # cached_property
                return self.object.func
            return None

    def autodoc_attrgetter(
        obj: Any, name: str, *defargs: Any, registry: SphinxComponentRegistry
    ) -> Any:
        """Alternative getattr() for types"""
        for typ, func in registry.autodoc_attrgetters.items():
            if isinstance(obj, typ):
                return func(obj, name, *defargs)

        return safe_getattr(obj, name, *defargs)

    def setup(app: Sphinx) -> ExtensionMetadata:
        app.add_autodocumenter(ModuleDocumenter)
        app.add_autodocumenter(ClassDocumenter)
        app.add_autodocumenter(ExceptionDocumenter)
        app.add_autodocumenter(DataDocumenter)
        app.add_autodocumenter(FunctionDocumenter)
        app.add_autodocumenter(DecoratorDocumenter)
        app.add_autodocumenter(MethodDocumenter)
        app.add_autodocumenter(AttributeDocumenter)
        app.add_autodocumenter(PropertyDocumenter)

        app.add_config_value(
            'autoclass_content',
            'class',
            'env',
            types=ENUM('both', 'class', 'init'),
        )
        app.add_config_value(
            'autodoc_member_order',
            'alphabetical',
            'env',
            types=ENUM('alphabetical', 'bysource', 'groupwise'),
        )
        app.add_config_value(
            'autodoc_class_signature',
            'mixed',
            'env',
            types=ENUM('mixed', 'separated'),
        )
        app.add_config_value('autodoc_default_options', {}, 'env', types=frozenset({dict}))
        app.add_config_value(
            'autodoc_docstring_signature', True, 'env', types=frozenset({bool})
        )
        app.add_config_value(
            'autodoc_mock_imports', [], 'env', types=frozenset({list, tuple})
        )
        app.add_config_value(
            'autodoc_typehints',
            'signature',
            'env',
            types=ENUM('signature', 'description', 'none', 'both'),
        )
        app.add_config_value(
            'autodoc_typehints_description_target',
            'all',
            'env',
            types=ENUM('all', 'documented', 'documented_params'),
        )
        app.add_config_value('autodoc_type_aliases', {}, 'env', types=frozenset({dict}))
        app.add_config_value(
            'autodoc_typehints_format',
            'short',
            'env',
            types=ENUM('fully-qualified', 'short'),
        )
        app.add_config_value('autodoc_warningiserror', True, 'env', types=frozenset({bool}))
        app.add_config_value(
            'autodoc_inherit_docstrings', True, 'env', types=frozenset({bool})
        )
        app.add_event('autodoc-before-process-signature')
        app.add_event('autodoc-process-docstring')
        app.add_event('autodoc-process-signature')
        app.add_event('autodoc-skip-member')
        app.add_event('autodoc-process-bases')

        app.setup_extension('sphinx.ext.autodoc.preserve_defaults')
        app.setup_extension('sphinx.ext.autodoc.type_comment')
        app.setup_extension('sphinx.ext.autodoc.typehints')

        return {
            'version': sphinx.__display_version__,
            'parallel_read_safe': True,
        }