From 97df13dc6875d5d9caf331834db3644d4fb7ffcf Mon Sep 17 00:00:00 2001 From: Adam Turner <9087854+AA-Turner@users.noreply.github.com> Date: Mon, 22 Jul 2024 17:27:48 +0100 Subject: [PATCH] Remove ``sphinx.util.inspect.isNewType`` (#12646) --- CHANGES.rst | 2 ++ sphinx/ext/autodoc/__init__.py | 14 +++++++------- sphinx/util/inspect.py | 5 ----- sphinx/util/typing.py | 19 +++++++++---------- 4 files changed, 18 insertions(+), 22 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index 7a2b32dc1..8ef5ad224 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -72,6 +72,8 @@ Incompatible changes Use :py:func:`os.fspath` to convert :py:class:`~pathlib.Path` objects to strings, or :py:class:`~pathlib.Path`'s methods to work with path objects. Patch by Adam Turner. +* #12646: Remove :py:func:`!sphinx.util.inspect.isNewType`. + Patch by Adam Turner. Deprecated ---------- diff --git a/sphinx/ext/autodoc/__init__.py b/sphinx/ext/autodoc/__init__.py index 173407401..c87562619 100644 --- a/sphinx/ext/autodoc/__init__.py +++ b/sphinx/ext/autodoc/__init__.py @@ -11,7 +11,7 @@ import functools import operator import re from inspect import Parameter, Signature -from typing import TYPE_CHECKING, Any, ClassVar, TypeVar +from typing import TYPE_CHECKING, Any, ClassVar, NewType, TypeVar from docutils.statemachine import StringList @@ -1475,7 +1475,7 @@ class ClassDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # type: cls: type[Documenter], member: Any, membername: str, isattr: bool, parent: Any, ) -> bool: return isinstance(member, type) or ( - isattr and (inspect.isNewType(member) or isinstance(member, TypeVar))) + isattr and isinstance(member, NewType | TypeVar)) def import_object(self, raiseerror: bool = False) -> bool: ret = super().import_object(raiseerror) @@ -1486,7 +1486,7 @@ class ClassDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # type: self.doc_as_attr = (self.objpath[-1] != self.object.__name__) else: self.doc_as_attr = True - if inspect.isNewType(self.object) or isinstance(self.object, TypeVar): + 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('.') @@ -1495,7 +1495,7 @@ class ClassDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # type: return ret def _get_signature(self) -> tuple[Any | None, str | None, Signature | None]: - if inspect.isNewType(self.object) or isinstance(self.object, TypeVar): + if isinstance(self.object, NewType | TypeVar): # Suppress signature return None, None, None @@ -1680,14 +1680,14 @@ class ClassDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # type: self.directivetype = 'attribute' super().add_directive_header(sig) - if inspect.isNewType(self.object) or isinstance(self.object, TypeVar): + 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 inspect.isNewType(self.object) + 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) @@ -1802,7 +1802,7 @@ class ClassDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # type: return None def add_content(self, more_content: StringList | None) -> None: - if inspect.isNewType(self.object): + if isinstance(self.object, NewType): if self.config.autodoc_typehints_format == "short": supertype = restify(self.object.__supertype__, "smart") else: diff --git a/sphinx/util/inspect.py b/sphinx/util/inspect.py index c58178e44..3176f63c4 100644 --- a/sphinx/util/inspect.py +++ b/sphinx/util/inspect.py @@ -198,11 +198,6 @@ def getslots(obj: Any) -> dict[str, Any] | dict[str, None] | None: raise ValueError -def isNewType(obj: Any) -> bool: - """Check the if object is a kind of :class:`~typing.NewType`.""" - return isinstance(obj, typing.NewType) - - def isenumclass(x: Any) -> TypeIs[type[enum.Enum]]: """Check if the object is an :class:`enumeration class `.""" return isclass(x) and issubclass(x, enum.Enum) diff --git a/sphinx/util/typing.py b/sphinx/util/typing.py index 8f48fbdfc..b8021d49a 100644 --- a/sphinx/util/typing.py +++ b/sphinx/util/typing.py @@ -14,6 +14,7 @@ from typing import ( Annotated, Any, ForwardRef, + NewType, TypedDict, TypeVar, Union, @@ -229,7 +230,7 @@ def restify(cls: Any, mode: _RestifyMode = 'fully-qualified-except-typing') -> s Show the name of the annotation. """ from sphinx.ext.autodoc.mock import ismock, ismockmodule # lazy loading - from sphinx.util import inspect # lazy loading + from sphinx.util.inspect import isgenericalias, object_description # lazy loading valid_modes = {'fully-qualified-except-typing', 'smart'} if mode not in valid_modes: @@ -283,9 +284,8 @@ def restify(cls: Any, mode: _RestifyMode = 'fully-qualified-except-typing') -> s return fr':py:class:`~typing.Annotated`\ [{args}, {meta}]' return (f':py:class:`{module_prefix}{cls.__module__}.{cls.__name__}`' fr'\ [{args}, {meta}]') - elif inspect.isNewType(cls): - # newtypes have correct module info since Python 3.10+ - return f':py:class:`{module_prefix}{cls.__module__}.{cls.__name__}`' + elif isinstance(cls, NewType): + return f':py:class:`{module_prefix}{cls.__module__}.{cls.__name__}`' # type: ignore[attr-defined] elif isinstance(cls, types.UnionType): # Union types (PEP 585) retain their definition order when they # are printed natively and ``None``-like types are kept as is. @@ -298,14 +298,14 @@ def restify(cls: Any, mode: _RestifyMode = 'fully-qualified-except-typing') -> s concatenated_args = ', '.join(restify(arg, mode) for arg in cls.__args__) return fr':py:class:`{cls.__name__}`\ [{concatenated_args}]' return f':py:class:`{cls.__name__}`' - elif (inspect.isgenericalias(cls) + elif (isgenericalias(cls) and cls_module_is_typing and cls.__origin__ is Union): # *cls* is defined in ``typing``, and thus ``__args__`` must exist return ' | '.join(restify(a, mode) for a in cls.__args__) - elif inspect.isgenericalias(cls): + elif isgenericalias(cls): # A generic alias always has an __origin__, but it is difficult to - # use a type guard on inspect.isgenericalias() + # use a type guard on ``isgenericalias()`` # (ideally, we would use ``TypeIs`` introduced in Python 3.13). cls_name = _typing_internal_name(cls) @@ -356,7 +356,7 @@ def restify(cls: Any, mode: _RestifyMode = 'fully-qualified-except-typing') -> s # not a class (ex. TypeVar) but should have a __name__ return f':py:obj:`{module_prefix}{cls.__module__}.{cls.__name__}`' except (AttributeError, TypeError): - return inspect.object_description(cls) + return object_description(cls) def _format_literal_arg_restify(arg: Any, /, *, mode: str) -> str: @@ -391,7 +391,6 @@ def stringify_annotation( Show the module name and qualified name of the annotation. """ from sphinx.ext.autodoc.mock import ismock, ismockmodule # lazy loading - from sphinx.util.inspect import isNewType # lazy loading valid_modes = {'fully-qualified-except-typing', 'fully-qualified', 'smart'} if mode not in valid_modes: @@ -426,7 +425,7 @@ def stringify_annotation( if annotation_module_is_typing and mode in {'fully-qualified-except-typing', 'smart'}: return annotation_name return module_prefix + f'{annotation_module}.{annotation_name}' - elif isNewType(annotation): + elif isinstance(annotation, NewType): return module_prefix + f'{annotation_module}.{annotation_name}' elif ismockmodule(annotation): return module_prefix + annotation_name