Skip to content

Commit 120dbb7

Browse files
committed
Use TypeGuards
1 parent a45813d commit 120dbb7

File tree

6 files changed

+49
-49
lines changed

6 files changed

+49
-49
lines changed

.ruff.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -553,6 +553,7 @@ exclude = [
553553
"sphinx/util/cfamily.py",
554554
"sphinx/util/math.py",
555555
"sphinx/util/logging.py",
556+
"sphinx/util/inspect.py",
556557
"sphinx/util/parallel.py",
557558
"sphinx/util/inventory.py",
558559
"sphinx/util/__init__.py",

pyproject.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,6 @@ lint = [
8888
"sphinx-lint",
8989
"types-docutils",
9090
"types-requests",
91-
"typing-extensions",
9291
"importlib_metadata", # for mypy (Python<=3.9)
9392
"tomli", # for mypy (Python<=3.10)
9493
"pytest>=6.0",

sphinx/ext/autodoc/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2267,7 +2267,7 @@ def format_signature(self, **kwargs: Any) -> str:
22672267
pass # default implementation. skipped.
22682268
else:
22692269
if inspect.isclassmethod(func):
2270-
func = func.__func__ # type: ignore[attr-defined]
2270+
func = func.__func__
22712271
dispatchmeth = self.annotate_to_first_argument(func, typ)
22722272
if dispatchmeth:
22732273
documenter = MethodDocumenter(self.directive, '')

sphinx/ext/autodoc/mock.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
from collections.abc import Iterator, Sequence
1818
from typing import Any
1919

20+
from typing_extensions import TypeGuard
21+
2022
logger = logging.getLogger(__name__)
2123

2224

@@ -154,7 +156,7 @@ def mock(modnames: list[str]) -> Iterator[None]:
154156
finder.invalidate_caches()
155157

156158

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

sphinx/util/inspect.py

Lines changed: 34 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,13 @@
55
import ast
66
import builtins
77
import contextlib
8+
import enum
89
import inspect
910
import re
1011
import sys
1112
import types
1213
import typing
1314
from collections.abc import Mapping
14-
from enum import Enum
1515
from functools import cached_property, partial, partialmethod, singledispatchmethod
1616
from importlib import import_module
1717
from inspect import Parameter, Signature
@@ -25,10 +25,12 @@
2525

2626
if TYPE_CHECKING:
2727
from collections.abc import Callable, Sequence
28+
from enum import Enum
2829
from inspect import _ParameterKind
30+
from types import MethodType, ModuleType
2931
from typing import Final, Protocol, Union
3032

31-
from typing_extensions import TypeGuard
33+
from typing_extensions import TypeAlias, TypeGuard
3234

3335
class _SupportsGet(Protocol):
3436
def __get__(self, __instance: Any, __owner: type | None = ...) -> Any: ... # NoQA: E704
@@ -41,20 +43,7 @@ class _SupportsDelete(Protocol):
4143
# instance is contravariant but we do not need that precision
4244
def __delete__(self, __instance: Any) -> None: ... # NoQA: E704
4345

44-
class _GenericAliasLike(Protocol):
45-
# Minimalist interface for a generic alias in typing.py.
46-
47-
# The ``__origin__`` is defined both on the base type for generics
48-
# in typing.py *and* on the public ``types.GenericAlias`` type.
49-
__origin__: type
50-
51-
# Note that special generic alias types (tuple and callables) do
52-
# not directly define ``__args__``. At runtime, however, they are
53-
# actually instances of ``typing._GenericAlias`` which does have
54-
# an ``__args__`` field.
55-
__args__: tuple[Any, ...]
56-
57-
_RoutineType = Union[
46+
_RoutineType: TypeAlias = Union[
5847
types.FunctionType,
5948
types.LambdaType,
6049
types.MethodType,
@@ -64,6 +53,11 @@ class _GenericAliasLike(Protocol):
6453
types.MethodDescriptorType,
6554
types.ClassMethodDescriptorType,
6655
]
56+
_SignatureType: TypeAlias = Union[
57+
Callable[..., Any],
58+
staticmethod,
59+
classmethod,
60+
]
6761

6862
logger = logging.getLogger(__name__)
6963

@@ -126,7 +120,7 @@ class methods and static methods.
126120

127121

128122
def getall(obj: Any) -> Sequence[str] | None:
129-
"""Get the ``__all__`` attribute of an object as a sequence.
123+
"""Get the ``__all__`` attribute of an object as sequence.
130124
131125
This returns ``None`` if the given ``obj.__all__`` does not exist and
132126
raises :exc:`ValueError` if ``obj.__all__`` is not a list or tuple of
@@ -222,12 +216,12 @@ def isNewType(obj: Any) -> bool:
222216

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

227221

228222
def isenumattribute(x: Any) -> TypeGuard[Enum]:
229223
"""Check if the object is an enumeration attribute."""
230-
return isinstance(x, Enum)
224+
return isinstance(x, enum.Enum)
231225

232226

233227
def unpartial(obj: Any) -> Any:
@@ -247,7 +241,11 @@ def ispartial(obj: Any) -> TypeGuard[partial | partialmethod]:
247241
return isinstance(obj, (partial, partialmethod))
248242

249243

250-
def isclassmethod(obj: Any, cls: Any = None, name: str | None = None) -> bool:
244+
def isclassmethod(
245+
obj: Any,
246+
cls: Any = None,
247+
name: str | None = None,
248+
) -> TypeGuard[classmethod]:
251249
"""Check if the object is a :class:`classmethod`."""
252250
if isinstance(obj, classmethod):
253251
return True
@@ -263,7 +261,11 @@ def isclassmethod(obj: Any, cls: Any = None, name: str | None = None) -> bool:
263261
return False
264262

265263

266-
def isstaticmethod(obj: Any, cls: Any = None, name: str | None = None) -> bool:
264+
def isstaticmethod(
265+
obj: Any,
266+
cls: Any = None,
267+
name: str | None = None,
268+
) -> TypeGuard[staticmethod]:
267269
"""Check if the object is a :class:`staticmethod`."""
268270
if isinstance(obj, staticmethod):
269271
return True
@@ -289,7 +291,7 @@ def isabstractmethod(obj: Any) -> bool:
289291
return safe_getattr(obj, '__isabstractmethod__', False) is True
290292

291293

292-
def isboundmethod(method: types.MethodType) -> bool:
294+
def isboundmethod(method: MethodType) -> bool:
293295
"""Check if the method is a bound method."""
294296
return safe_getattr(method, '__self__', None) is not None
295297

@@ -379,7 +381,7 @@ def isroutine(obj: Any) -> TypeGuard[_RoutineType]:
379381
return inspect.isroutine(unpartial(obj))
380382

381383

382-
def iscoroutinefunction(obj: Any) -> bool:
384+
def iscoroutinefunction(obj: Any) -> TypeGuard[Callable[..., types.CoroutineType]]:
383385
"""Check if the object is a :external+python:term:`coroutine` function."""
384386
obj = unwrap_all(obj, stop=_is_wrapped_coroutine)
385387
return inspect.iscoroutinefunction(obj)
@@ -399,7 +401,7 @@ def isproperty(obj: Any) -> TypeGuard[property | cached_property]:
399401
return isinstance(obj, (property, cached_property))
400402

401403

402-
def isgenericalias(obj: Any) -> bool:
404+
def isgenericalias(obj: Any) -> TypeGuard[types.GenericAlias]:
403405
"""Check if the object is a generic alias."""
404406
return isinstance(obj, (types.GenericAlias, typing._BaseGenericAlias)) # type: ignore[attr-defined]
405407

@@ -469,8 +471,8 @@ def object_description(obj: Any, *, _seen: frozenset[int] = frozenset()) -> str:
469471
return 'frozenset({%s})' % ', '.join(
470472
object_description(x, _seen=seen) for x in sorted_values
471473
)
472-
elif isinstance(obj, Enum):
473-
if obj.__repr__.__func__ is not Enum.__repr__: # type: ignore[attr-defined]
474+
elif isinstance(obj, enum.Enum):
475+
if obj.__repr__.__func__ is not enum.Enum.__repr__: # type: ignore[attr-defined]
474476
return repr(obj)
475477
return f'{obj.__class__.__name__}.{obj.name}'
476478
elif isinstance(obj, tuple):
@@ -565,7 +567,7 @@ def __init__(self, modname: str, mapping: Mapping[str, str]) -> None:
565567
self.__modname = modname
566568
self.__mapping = mapping
567569

568-
self.__module: types.ModuleType | None = None
570+
self.__module: ModuleType | None = None
569571

570572
def __getattr__(self, name: str) -> Any:
571573
fullname = '.'.join(filter(None, [self.__modname, name]))
@@ -615,7 +617,7 @@ def __getitem__(self, key: str) -> Any:
615617
raise KeyError
616618

617619

618-
def _should_unwrap(subject: Callable[..., Any]) -> bool:
620+
def _should_unwrap(subject: _SignatureType) -> bool:
619621
"""Check the function should be unwrapped on getting signature."""
620622
__globals__ = getglobals(subject)
621623
# contextmanger should be unwrapped
@@ -626,7 +628,7 @@ def _should_unwrap(subject: Callable[..., Any]) -> bool:
626628

627629

628630
def signature(
629-
subject: Callable[..., Any],
631+
subject: _SignatureType,
630632
bound_method: bool = False,
631633
type_aliases: Mapping[str, str] | None = None,
632634
) -> Signature:
@@ -639,12 +641,12 @@ def signature(
639641

640642
try:
641643
if _should_unwrap(subject):
642-
signature = inspect.signature(subject)
644+
signature = inspect.signature(subject) # type: ignore[arg-type]
643645
else:
644-
signature = inspect.signature(subject, follow_wrapped=True)
646+
signature = inspect.signature(subject, follow_wrapped=True) # type: ignore[arg-type]
645647
except ValueError:
646648
# follow built-in wrappers up (ex. functools.lru_cache)
647-
signature = inspect.signature(subject)
649+
signature = inspect.signature(subject) # type: ignore[arg-type]
648650
parameters = list(signature.parameters.values())
649651
return_annotation = signature.return_annotation
650652

sphinx/util/typing.py

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515

1616
if TYPE_CHECKING:
1717
from collections.abc import Mapping
18-
from typing import Final, Literal, Protocol
18+
from typing import Final, Literal
1919

2020
from typing_extensions import TypeAlias
2121

@@ -31,10 +31,6 @@
3131
'smart',
3232
]
3333

34-
class _SpecialFormInterface(Protocol):
35-
_name: str
36-
37-
3834
if sys.version_info >= (3, 10):
3935
from types import UnionType
4036
else:
@@ -168,7 +164,7 @@ def is_system_TypeVar(typ: Any) -> bool:
168164
return modname == 'typing' and isinstance(typ, TypeVar)
169165

170166

171-
def restify(cls: type | None, mode: _RestifyMode = 'fully-qualified-except-typing') -> str:
167+
def restify(cls: Any, mode: _RestifyMode = 'fully-qualified-except-typing') -> str:
172168
"""Convert python class to a reST reference.
173169
174170
:param mode: Specify a method how annotations will be stringified.
@@ -233,25 +229,25 @@ def restify(cls: type | None, mode: _RestifyMode = 'fully-qualified-except-typin
233229
return f':py:class:`{cls.__name__}`'
234230
elif (inspect.isgenericalias(cls)
235231
and cls.__module__ == 'typing'
236-
and cls.__origin__ is Union): # type: ignore[attr-defined]
232+
and cls.__origin__ is Union):
237233
# *cls* is defined in ``typing``, and thus ``__args__`` must exist
238-
return ' | '.join(restify(a, mode) for a in cls.__args__) # type: ignore[attr-defined]
234+
return ' | '.join(restify(a, mode) for a in cls.__args__)
239235
elif inspect.isgenericalias(cls):
240-
if isinstance(cls.__origin__, typing._SpecialForm): # type: ignore[attr-defined]
241-
text = restify(cls.__origin__, mode) # type: ignore[attr-defined,arg-type]
236+
if isinstance(cls.__origin__, typing._SpecialForm):
237+
text = restify(cls.__origin__, mode)
242238
elif getattr(cls, '_name', None):
243-
cls_name = cls._name # type: ignore[attr-defined]
239+
cls_name = cls._name
244240
text = f':py:class:`{modprefix}{cls.__module__}.{cls_name}`'
245241
else:
246-
text = restify(cls.__origin__, mode) # type: ignore[attr-defined]
242+
text = restify(cls.__origin__, mode)
247243

248244
origin = getattr(cls, '__origin__', None)
249245
if not hasattr(cls, '__args__'): # NoQA: SIM114
250246
pass
251247
elif all(is_system_TypeVar(a) for a in cls.__args__):
252248
# Suppress arguments if all system defined TypeVars (ex. Dict[KT, VT])
253249
pass
254-
elif cls.__module__ == 'typing' and cls._name == 'Callable': # type: ignore[attr-defined]
250+
elif cls.__module__ == 'typing' and cls._name == 'Callable':
255251
args = ', '.join(restify(a, mode) for a in cls.__args__[:-1])
256252
text += fr'\ [[{args}], {restify(cls.__args__[-1], mode)}]'
257253
elif cls.__module__ == 'typing' and getattr(origin, '_name', None) == 'Literal':
@@ -263,7 +259,7 @@ def restify(cls: type | None, mode: _RestifyMode = 'fully-qualified-except-typin
263259

264260
return text
265261
elif isinstance(cls, typing._SpecialForm):
266-
return f':py:obj:`~{cls.__module__}.{cls._name}`'
262+
return f':py:obj:`~{cls.__module__}.{cls._name}`' # type: ignore[attr-defined]
267263
elif sys.version_info[:2] >= (3, 11) and cls is typing.Any:
268264
# handle bpo-46998
269265
return f':py:obj:`~{cls.__module__}.{cls.__name__}`'

0 commit comments

Comments
 (0)