diff --git a/mypy.ini b/mypy.ini index 569c7f0ace..43bb9d56c9 100644 --- a/mypy.ini +++ b/mypy.ini @@ -1,7 +1,7 @@ [mypy] # CI should test for all versions, local development gets hints for oldest supported -# Some upstream typeshed distutils stubs fixes are necessary before we can start testing on Python 3.12 -python_version = 3.8 +# But our testing setup doesn't allow passing CLI arguments, so local devs have to set this manually. +# python_version = 3.8 strict = False warn_unused_ignores = True warn_redundant_casts = True @@ -30,15 +30,19 @@ disable_error_code = attr-defined [mypy-pkg_resources.tests.*] disable_error_code = import-not-found -# - distutils._modified has different errors on Python 3.8 [import-untyped], on Python 3.9+ [import-not-found] +# - distutils doesn't exist on Python 3.12, unfortunately, this means typing +# will be missing for subclasses of distutils on Python 3.12 until either: +# - support for `SETUPTOOLS_USE_DISTUTILS=stdlib` is dropped (#3625) +# for setuptools to import `_distutils` directly +# - or non-stdlib distutils typings are exposed # - All jaraco modules are still untyped # - _validate_project sometimes complains about trove_classifiers (#4296) # - wheel appears to be untyped -[mypy-distutils._modified,jaraco.*,trove_classifiers,wheel.*] +[mypy-distutils.*,jaraco.*,trove_classifiers,wheel.*] ignore_missing_imports = True # Even when excluding a module, import issues can show up due to following import # https://github.com/python/mypy/issues/11936#issuecomment-1466764006 -[mypy-setuptools.config._validate_pyproject.*] +[mypy-setuptools.config._validate_pyproject.*,setuptools._distutils.*] follow_imports = silent # silent => ignore errors when following imports diff --git a/setuptools/__init__.py b/setuptools/__init__.py index 84294edb31..acf98ef3b7 100644 --- a/setuptools/__init__.py +++ b/setuptools/__init__.py @@ -1,4 +1,9 @@ """Extensions to the 'distutils' for large or complex distributions""" +# mypy: disable_error_code=override +# Command.reinitialize_command has an extra **kw param that distutils doesn't have +# Can't disable on the exact line because distutils doesn't exists on Python 3.12 +# and mypy isn't aware of distutils_hack, causing distutils.core.Command to be Any, +# and a [unused-ignore] to be raised on 3.12+ from __future__ import annotations @@ -114,8 +119,10 @@ def setup(**attrs): setup.__doc__ = distutils.core.setup.__doc__ if TYPE_CHECKING: + from typing_extensions import TypeAlias + # Work around a mypy issue where type[T] can't be used as a base: https://github.com/python/mypy/issues/10962 - _Command = distutils.core.Command + _Command: TypeAlias = distutils.core.Command else: _Command = monkey.get_unpatched(distutils.core.Command) @@ -207,7 +214,7 @@ def ensure_string_list(self, option): "'%s' must be a list of strings (got %r)" % (option, val) ) - @overload # type:ignore[override] # Extra **kw param + @overload def reinitialize_command( self, command: str, reinit_subcommands: bool = False, **kw ) -> _Command: ... diff --git a/setuptools/build_meta.py b/setuptools/build_meta.py index d8ab28be74..8fb4cd7309 100644 --- a/setuptools/build_meta.py +++ b/setuptools/build_meta.py @@ -385,9 +385,10 @@ def _build_with_temp_dir( # Build in a temporary directory, then copy to the target. os.makedirs(result_directory, exist_ok=True) - temp_opts = {"prefix": ".tmp-", "dir": result_directory} - with tempfile.TemporaryDirectory(**temp_opts) as tmp_dist_dir: + with tempfile.TemporaryDirectory( + prefix=".tmp-", dir=result_directory + ) as tmp_dist_dir: sys.argv = [ *sys.argv[:1], *self._global_args(config_settings), diff --git a/setuptools/command/build_ext.py b/setuptools/command/build_ext.py index 0a2af0dd53..31da329545 100644 --- a/setuptools/command/build_ext.py +++ b/setuptools/command/build_ext.py @@ -30,7 +30,7 @@ get_config_var("LDSHARED") # Not publicly exposed in typeshed distutils stubs, but this is done on purpose # See https://github.com/pypa/setuptools/pull/4228#issuecomment-1959856400 -from distutils.sysconfig import _config_vars as _CONFIG_VARS # type: ignore # noqa +from distutils.sysconfig import _config_vars as _CONFIG_VARS # noqa: E402 def _customize_compiler_for_shlib(compiler): diff --git a/setuptools/command/sdist.py b/setuptools/command/sdist.py index ca0f712792..bfcfb251c6 100644 --- a/setuptools/command/sdist.py +++ b/setuptools/command/sdist.py @@ -1,8 +1,10 @@ +from __future__ import annotations from distutils import log import distutils.command.sdist as orig import os import contextlib from itertools import chain +from typing import ClassVar from .._importlib import metadata from ..dist import Distribution @@ -45,7 +47,7 @@ class sdist(orig.sdist): ] distribution: Distribution # override distutils.dist.Distribution with setuptools.dist.Distribution - negative_opt = {} + negative_opt: ClassVar[dict[str, str]] = {} # type: ignore[misc] # Fixed upstream in typeshed to be a ClassVar. Should be included in mypy 1.12 README_EXTENSIONS = ['', '.rst', '.txt', '.md'] READMES = tuple('README{0}'.format(ext) for ext in README_EXTENSIONS) diff --git a/setuptools/config/setupcfg.py b/setuptools/config/setupcfg.py index 5a3b7478a0..0978311005 100644 --- a/setuptools/config/setupcfg.py +++ b/setuptools/config/setupcfg.py @@ -25,9 +25,11 @@ Generic, Iterable, Iterator, + List, Tuple, TypeVar, Union, + cast, ) from .._path import StrPath @@ -108,7 +110,8 @@ def _apply( filenames = [*other_files, filepath] try: - _Distribution.parse_config_files(dist, filenames=filenames) # type: ignore[arg-type] # TODO: fix in distutils stubs + # TODO: Temporary cast until mypy 1.12 is released with upstream fixes from typeshed + _Distribution.parse_config_files(dist, filenames=cast(List[str], filenames)) handlers = parse_configuration( dist, dist.command_options, ignore_option_errors=ignore_option_errors ) diff --git a/setuptools/dist.py b/setuptools/dist.py index 03017e56e1..2079068b3d 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -194,8 +194,10 @@ def check_packages(dist, attr, value): if TYPE_CHECKING: + from typing_extensions import TypeAlias + # Work around a mypy issue where type[T] can't be used as a base: https://github.com/python/mypy/issues/10962 - _Distribution = distutils.core.Distribution + _Distribution: TypeAlias = distutils.core.Distribution else: _Distribution = get_unpatched(distutils.core.Distribution) diff --git a/setuptools/errors.py b/setuptools/errors.py index 67a5a1df10..6d00c65c12 100644 --- a/setuptools/errors.py +++ b/setuptools/errors.py @@ -3,30 +3,35 @@ Provides exceptions used by setuptools modules. """ +from __future__ import annotations + +from typing import TYPE_CHECKING from distutils import errors as _distutils_errors +if TYPE_CHECKING: + from typing_extensions import TypeAlias # Re-export errors from distutils to facilitate the migration to PEP632 -ByteCompileError = _distutils_errors.DistutilsByteCompileError -CCompilerError = _distutils_errors.CCompilerError -ClassError = _distutils_errors.DistutilsClassError -CompileError = _distutils_errors.CompileError -ExecError = _distutils_errors.DistutilsExecError -FileError = _distutils_errors.DistutilsFileError -InternalError = _distutils_errors.DistutilsInternalError -LibError = _distutils_errors.LibError -LinkError = _distutils_errors.LinkError -ModuleError = _distutils_errors.DistutilsModuleError -OptionError = _distutils_errors.DistutilsOptionError -PlatformError = _distutils_errors.DistutilsPlatformError -PreprocessError = _distutils_errors.PreprocessError -SetupError = _distutils_errors.DistutilsSetupError -TemplateError = _distutils_errors.DistutilsTemplateError -UnknownFileError = _distutils_errors.UnknownFileError +ByteCompileError: TypeAlias = _distutils_errors.DistutilsByteCompileError +CCompilerError: TypeAlias = _distutils_errors.CCompilerError +ClassError: TypeAlias = _distutils_errors.DistutilsClassError +CompileError: TypeAlias = _distutils_errors.CompileError +ExecError: TypeAlias = _distutils_errors.DistutilsExecError +FileError: TypeAlias = _distutils_errors.DistutilsFileError +InternalError: TypeAlias = _distutils_errors.DistutilsInternalError +LibError: TypeAlias = _distutils_errors.LibError +LinkError: TypeAlias = _distutils_errors.LinkError +ModuleError: TypeAlias = _distutils_errors.DistutilsModuleError +OptionError: TypeAlias = _distutils_errors.DistutilsOptionError +PlatformError: TypeAlias = _distutils_errors.DistutilsPlatformError +PreprocessError: TypeAlias = _distutils_errors.PreprocessError +SetupError: TypeAlias = _distutils_errors.DistutilsSetupError +TemplateError: TypeAlias = _distutils_errors.DistutilsTemplateError +UnknownFileError: TypeAlias = _distutils_errors.UnknownFileError # The root error class in the hierarchy -BaseError = _distutils_errors.DistutilsError +BaseError: TypeAlias = _distutils_errors.DistutilsError class InvalidConfigError(OptionError): diff --git a/setuptools/extension.py b/setuptools/extension.py index 96d392ef2b..907170d836 100644 --- a/setuptools/extension.py +++ b/setuptools/extension.py @@ -25,8 +25,10 @@ def _have_cython(): # for compatibility have_pyrex = _have_cython if TYPE_CHECKING: + from typing_extensions import TypeAlias + # Work around a mypy issue where type[T] can't be used as a base: https://github.com/python/mypy/issues/10962 - _Extension = distutils.core.Extension + _Extension: TypeAlias = distutils.core.Extension else: _Extension = get_unpatched(distutils.core.Extension)