Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions .ruff.toml
Original file line number Diff line number Diff line change
Expand Up @@ -546,7 +546,6 @@ exclude = [
"sphinx/util/cfamily.py",
"sphinx/util/math.py",
"sphinx/util/logging.py",
"sphinx/util/inspect.py",
"sphinx/util/parallel.py",
"sphinx/util/inventory.py",
"sphinx/util/__init__.py",
Expand All @@ -555,7 +554,6 @@ exclude = [
"sphinx/util/http_date.py",
"sphinx/util/matching.py",
"sphinx/util/index_entries.py",
"sphinx/util/typing.py",
"sphinx/util/images.py",
"sphinx/util/exceptions.py",
"sphinx/util/requests.py",
Expand Down
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ lint = [
"sphinx-lint",
"types-docutils",
"types-requests",
"typing-extensions", # implicitly required by mypy
"importlib_metadata", # for mypy (Python<=3.9)
"tomli", # for mypy (Python<=3.10)
"pytest>=6.0",
Expand Down
2 changes: 1 addition & 1 deletion sphinx/ext/autodoc/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2267,7 +2267,7 @@ def format_signature(self, **kwargs: Any) -> str:
pass # default implementation. skipped.
else:
if inspect.isclassmethod(func):
func = func.__func__
func = func.__func__ # type: ignore[attr-defined]
dispatchmeth = self.annotate_to_first_argument(func, typ)
if dispatchmeth:
documenter = MethodDocumenter(self.directive, '')
Expand Down
7 changes: 5 additions & 2 deletions sphinx/ext/autodoc/mock.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,16 @@
from importlib.abc import Loader, MetaPathFinder
from importlib.machinery import ModuleSpec
from types import MethodType, ModuleType
from typing import TYPE_CHECKING, Any
from typing import TYPE_CHECKING

from sphinx.util import logging
from sphinx.util.inspect import isboundmethod, safe_getattr

if TYPE_CHECKING:
from collections.abc import Iterator, Sequence
from typing import Any

from typing_extensions import TypeGuard

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -153,7 +156,7 @@ def mock(modnames: list[str]) -> Iterator[None]:
finder.invalidate_caches()


def ismockmodule(subject: Any) -> bool:
def ismockmodule(subject: Any) -> TypeGuard[_MockModule]:
"""Check if the object is a mocked module."""
return isinstance(subject, _MockModule)

Expand Down
45 changes: 35 additions & 10 deletions sphinx/util/inspect.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,34 @@

if TYPE_CHECKING:
from collections.abc import Callable, Sequence
from enum import Enum
from inspect import _ParameterKind
from types import MethodType, ModuleType
from typing import Final
from typing import Final, Protocol, Union

from typing_extensions import TypeGuard

class _SupportsGet(Protocol):
def __get__(self, __instance: Any, __owner: type | None = ...) -> Any: ... # NoQA: E704

class _SupportsSet(Protocol):
# instance and value are contravariants but we do not need that precision
def __set__(self, __instance: Any, __value: Any) -> None: ... # NoQA: E704

class _SupportsDelete(Protocol):
# instance is contravariant but we do not need that precision
def __delete__(self, __instance: Any) -> None: ... # NoQA: E704

_RoutineType = Union[
types.FunctionType,
types.LambdaType,
types.MethodType,
types.BuiltinFunctionType,
types.BuiltinMethodType,
types.WrapperDescriptorType,
types.MethodDescriptorType,
types.ClassMethodDescriptorType,
]

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -184,12 +209,12 @@ def isNewType(obj: Any) -> bool:
return __module__ == 'typing' and __qualname__ == 'NewType.<locals>.new_type'


def isenumclass(x: Any) -> bool:
def isenumclass(x: Any) -> TypeGuard[type[Enum]]:
"""Check if the object is an :class:`enumeration class <enum.Enum>`."""
return isclass(x) and issubclass(x, enum.Enum)


def isenumattribute(x: Any) -> bool:
def isenumattribute(x: Any) -> TypeGuard[Enum]:
"""Check if the object is an enumeration attribute."""
return isinstance(x, enum.Enum)

Expand All @@ -206,7 +231,7 @@ def unpartial(obj: Any) -> Any:
return obj


def ispartial(obj: Any) -> bool:
def ispartial(obj: Any) -> TypeGuard[partial | partialmethod]:
"""Check if the object is a partial function or method."""
return isinstance(obj, (partial, partialmethod))

Expand Down Expand Up @@ -239,7 +264,7 @@ def isstaticmethod(obj: Any, cls: Any = None, name: str | None = None) -> bool:
return False


def isdescriptor(x: Any) -> bool:
def isdescriptor(x: Any) -> TypeGuard[_SupportsGet | _SupportsSet | _SupportsDelete]:
"""Check if the object is a :external+python:term:`descriptor`."""
return any(
callable(safe_getattr(x, item, None)) for item in ('__get__', '__set__', '__delete__')
Expand Down Expand Up @@ -306,12 +331,12 @@ def is_singledispatch_function(obj: Any) -> bool:
)


def is_singledispatch_method(obj: Any) -> bool:
def is_singledispatch_method(obj: Any) -> TypeGuard[singledispatchmethod]:
"""Check if the object is a :class:`~functools.singledispatchmethod`."""
return isinstance(obj, singledispatchmethod)


def isfunction(obj: Any) -> bool:
def isfunction(obj: Any) -> TypeGuard[types.FunctionType]:
"""Check if the object is a user-defined function.

Partial objects are unwrapped before checking them.
Expand All @@ -321,7 +346,7 @@ def isfunction(obj: Any) -> bool:
return inspect.isfunction(unpartial(obj))


def isbuiltin(obj: Any) -> bool:
def isbuiltin(obj: Any) -> TypeGuard[types.BuiltinFunctionType]:
"""Check if the object is a built-in function or method.

Partial objects are unwrapped before checking them.
Expand All @@ -331,7 +356,7 @@ def isbuiltin(obj: Any) -> bool:
return inspect.isbuiltin(unpartial(obj))


def isroutine(obj: Any) -> bool:
def isroutine(obj: Any) -> TypeGuard[_RoutineType]:
"""Check if the object is a kind of function or method.

Partial objects are unwrapped before checking them.
Expand All @@ -356,7 +381,7 @@ def _is_wrapped_coroutine(obj: Any) -> bool:
return hasattr(obj, '__wrapped__')


def isproperty(obj: Any) -> bool:
def isproperty(obj: Any) -> TypeGuard[property | cached_property]:
"""Check if the object is property (possibly cached)."""
return isinstance(obj, (property, cached_property))

Expand Down
Loading