diff --git a/docs/conf.py b/docs/conf.py index 0a82ff2fe2..be8856849b 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -161,22 +161,23 @@ # Ref: https://stackoverflow.com/a/30624034/595220 nitpick_ignore = [ ('c:func', 'SHGetSpecialFolderPath'), # ref to MS docs + ('envvar', 'DIST_EXTRA_CONFIG'), # undocumented ('envvar', 'DISTUTILS_DEBUG'), # undocumented ('envvar', 'HOME'), # undocumented ('envvar', 'PLAT'), # undocumented - ('envvar', 'DIST_EXTRA_CONFIG'), # undocumented ('py:attr', 'CCompiler.language_map'), # undocumented ('py:attr', 'CCompiler.language_order'), # undocumented - ('py:class', 'distutils.dist.Distribution'), # undocumented - ('py:class', 'distutils.extension.Extension'), # undocumented ('py:class', 'BorlandCCompiler'), # undocumented ('py:class', 'CCompiler'), # undocumented ('py:class', 'CygwinCCompiler'), # undocumented + ('py:class', 'distutils.dist.Distribution'), # undocumented ('py:class', 'distutils.dist.DistributionMetadata'), # undocumented + ('py:class', 'distutils.extension.Extension'), # undocumented ('py:class', 'FileList'), # undocumented ('py:class', 'IShellLink'), # ref to MS docs ('py:class', 'MSVCCompiler'), # undocumented ('py:class', 'OptionDummy'), # undocumented + ('py:class', 'setuptools.dist.Distribution'), # undocumented ('py:class', 'UnixCCompiler'), # undocumented ('py:exc', 'CompileError'), # undocumented ('py:exc', 'DistutilsExecError'), # undocumented @@ -186,8 +187,7 @@ ('py:exc', 'PreprocessError'), # undocumented ('py:exc', 'setuptools.errors.PlatformError'), # sphinx cannot find it ('py:func', 'distutils.CCompiler.new_compiler'), # undocumented - # undocumented: - ('py:func', 'distutils.dist.DistributionMetadata.read_pkg_file'), + ('py:func', 'distutils.dist.DistributionMetadata.read_pkg_file'), # undocumented ('py:func', 'distutils.file_util._copy_file_contents'), # undocumented ('py:func', 'distutils.log.debug'), # undocumented ('py:func', 'distutils.spawn.find_executable'), # undocumented diff --git a/mypy.ini b/mypy.ini index b6f972769e..e0fa8e5c47 100644 --- a/mypy.ini +++ b/mypy.ini @@ -1,5 +1,42 @@ [mypy] -ignore_missing_imports = True -# required to support namespace packages -# https://github.com/python/mypy/issues/14057 +# CI should test for all versions, local development gets hints for oldest supported +python_version = 3.8 +strict = False +warn_unused_ignores = True +# required to support namespace packages: https://github.com/python/mypy/issues/14057 explicit_package_bases = True +exclude = (?x)( + ^build/ + | ^.tox/ + | ^pkg_resources/tests/data/my-test-package-source/setup.py$ # Duplicate module name + | ^.+?/(_vendor|extern)/ # Vendored + | ^setuptools/_distutils/ # Vendored + | ^setuptools/config/_validate_pyproject/ # Auto-generated + ) +disable_error_code = + # TODO: Test environment is not yet properly configured to install all imported packages + # import-not-found, # This can be left commented out for local runs until we enforce running mypy in the CI + # TODO: Not all dependencies are typed. Namely: distutils._modified, wheel.wheelfile, and jaraco.* + import-untyped, + # Ignoring attr-defined because setuptools wraps a lot of distutils classes, adding new attributes, + # w/o updating all the attributes and return types from the base classes for type-checkers to understand + # Especially with setuptools.dist.command vs distutils.dist.command vs setuptools._distutils.dist.command + # *.extern modules that actually live in *._vendor will also cause attr-defined issues on import + attr-defined, + +# Avoid raising issues when importing from "extern" modules, as those are added to path dynamically. +# https://github.com/pypa/setuptools/pull/3979#discussion_r1367968993 +[mypy-pkg_resources.extern.*,setuptools.extern.*] +ignore_missing_imports = True + +[mypy-pkg_resources.tests.*,setuptools.tests.*] +disable_error_code = + # Tests include creating dynamic modules that won't exists statically before the test is run. + # Let's ignore all "import-not-found", as if an import really wasn't found, then the test would fail. + import-not-found, + # mmany untyped "jaraco" modules + import-untyped, + +# Mypy issue, this vendored module is already excluded! +[mypy-setuptools._vendor.packaging._manylinux] +disable_error_code = import-not-found diff --git a/pkg_resources/tests/test_pkg_resources.py b/pkg_resources/tests/test_pkg_resources.py index 0883642080..bfbf619c85 100644 --- a/pkg_resources/tests/test_pkg_resources.py +++ b/pkg_resources/tests/test_pkg_resources.py @@ -9,6 +9,7 @@ import stat import distutils.dist import distutils.command.install_egg_info +from typing import List from unittest import mock @@ -32,7 +33,7 @@ def __call__(self): class TestZipProvider: - finalizers = [] + finalizers: List[EggRemover] = [] ref_time = datetime.datetime(2013, 5, 12, 13, 25, 0) "A reference time for a file modification" diff --git a/setup.py b/setup.py index 075d7c405f..1a6074766a 100755 --- a/setup.py +++ b/setup.py @@ -88,5 +88,6 @@ def _restore_install_lib(self): if __name__ == '__main__': # allow setup.py to run from another directory - here and os.chdir(here) + # TODO: Use a proper conditonal statement here + here and os.chdir(here) # type: ignore[func-returns-value] dist = setuptools.setup(**setup_params) diff --git a/setuptools/__init__.py b/setuptools/__init__.py index 563ca1c4ba..7c88c7e19b 100644 --- a/setuptools/__init__.py +++ b/setuptools/__init__.py @@ -3,6 +3,7 @@ import functools import os import re +from typing import TYPE_CHECKING import _distutils_hack.override # noqa: F401 import distutils.core @@ -105,8 +106,11 @@ def setup(**attrs): setup.__doc__ = distutils.core.setup.__doc__ - -_Command = monkey.get_unpatched(distutils.core.Command) +if TYPE_CHECKING: + # 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 +else: + _Command = monkey.get_unpatched(distutils.core.Command) class Command(_Command): @@ -165,8 +169,9 @@ class Command(_Command): """ command_consumes_arguments = False + distribution: Distribution # override distutils.dist.Distribution with setuptools.dist.Distribution - def __init__(self, dist, **kw): + def __init__(self, dist: Distribution, **kw): """ Construct the command for dist, updating vars(self) with any keyword parameters. diff --git a/setuptools/command/_requirestxt.py b/setuptools/command/_requirestxt.py index 7b732b11ab..b0c2d7059a 100644 --- a/setuptools/command/_requirestxt.py +++ b/setuptools/command/_requirestxt.py @@ -35,7 +35,7 @@ def _prepare( def _convert_extras_requirements( - extras_require: _StrOrIter, + extras_require: Mapping[str, _StrOrIter], ) -> Mapping[str, _Ordered[Requirement]]: """ Convert requirements in `extras_require` of the form diff --git a/setuptools/command/build_ext.py b/setuptools/command/build_ext.py index 780afe3aec..1301433a32 100644 --- a/setuptools/command/build_ext.py +++ b/setuptools/command/build_ext.py @@ -26,7 +26,9 @@ # make sure _config_vars is initialized get_config_var("LDSHARED") -from distutils.sysconfig import _config_vars as _CONFIG_VARS # noqa +# 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 def _customize_compiler_for_shlib(compiler): @@ -58,7 +60,7 @@ def _customize_compiler_for_shlib(compiler): use_stubs = True elif os.name != 'nt': try: - import dl + import dl # type: ignore[import-not-found] # https://github.com/python/mypy/issues/13002 use_stubs = have_rtld = hasattr(dl, 'RTLD_NOW') except ImportError: @@ -378,7 +380,10 @@ def _compile_and_remove_stub(self, stub_file: str): optimize = self.get_finalized_command('install_lib').optimize if optimize > 0: byte_compile( - [stub_file], optimize=optimize, force=True, dry_run=self.dry_run + [stub_file], + optimize=optimize, + force=True, + dry_run=self.dry_run, ) if os.path.exists(stub_file) and not self.dry_run: os.unlink(stub_file) diff --git a/setuptools/command/dist_info.py b/setuptools/command/dist_info.py index f5061afaaf..52c0721903 100644 --- a/setuptools/command/dist_info.py +++ b/setuptools/command/dist_info.py @@ -9,8 +9,10 @@ from distutils import log from distutils.core import Command from pathlib import Path +from typing import cast from .. import _normalization +from .egg_info import egg_info as egg_info_cls class dist_info(Command): @@ -50,7 +52,7 @@ def finalize_options(self): project_dir = dist.src_root or os.curdir self.output_dir = Path(self.output_dir or project_dir) - egg_info = self.reinitialize_command("egg_info") + egg_info = cast(egg_info_cls, self.reinitialize_command("egg_info")) egg_info.egg_base = str(self.output_dir) if self.tag_date: diff --git a/setuptools/command/easy_install.py b/setuptools/command/easy_install.py index cc0c409123..402355bd81 100644 --- a/setuptools/command/easy_install.py +++ b/setuptools/command/easy_install.py @@ -25,6 +25,7 @@ from distutils.command import install import sys import os +from typing import Dict, List import zipimport import shutil import tempfile @@ -43,7 +44,6 @@ import configparser import sysconfig - from sysconfig import get_path from setuptools import Command @@ -1765,7 +1765,7 @@ def _wrap_lines(cls, lines): if os.environ.get('SETUPTOOLS_SYS_PATH_TECHNIQUE', 'raw') == 'rewrite': - PthDistributions = RewritePthDistributions + PthDistributions = RewritePthDistributions # type: ignore[misc] # Overwriting type def _first_line_re(): @@ -2015,7 +2015,7 @@ def is_python_script(script_text, filename): from os import chmod as _chmod except ImportError: # Jython compatibility - def _chmod(*args): + def _chmod(*args: object, **kwargs: object) -> None: # type: ignore[misc] # Mypy re-uses the imported definition anyway pass @@ -2033,8 +2033,8 @@ class CommandSpec(list): those passed to Popen. """ - options = [] - split_args = dict() + options: List[str] = [] + split_args: Dict[str, bool] = dict() @classmethod def best(cls): diff --git a/setuptools/command/editable_wheel.py b/setuptools/command/editable_wheel.py index a81fcd5bf9..24980edcf4 100644 --- a/setuptools/command/editable_wheel.py +++ b/setuptools/command/editable_wheel.py @@ -33,6 +33,7 @@ Protocol, Tuple, TypeVar, + cast, ) from .. import ( @@ -50,7 +51,12 @@ SetuptoolsDeprecationWarning, SetuptoolsWarning, ) +from .build import build as build_cls from .build_py import build_py as build_py_cls +from .dist_info import dist_info as dist_info_cls +from .egg_info import egg_info as egg_info_cls +from .install import install as install_cls +from .install_scripts import install_scripts as install_scripts_cls if TYPE_CHECKING: from wheel.wheelfile import WheelFile # noqa @@ -155,7 +161,7 @@ def run(self): def _ensure_dist_info(self): if self.dist_info_dir is None: - dist_info = self.reinitialize_command("dist_info") + dist_info = cast(dist_info_cls, self.reinitialize_command("dist_info")) dist_info.output_dir = self.dist_dir dist_info.ensure_finalized() dist_info.run() @@ -202,12 +208,18 @@ def _configure_build( scripts = str(Path(unpacked_wheel, f"{name}.data", "scripts")) # egg-info may be generated again to create a manifest (used for package data) - egg_info = dist.reinitialize_command("egg_info", reinit_subcommands=True) + egg_info = cast( + egg_info_cls, dist.reinitialize_command("egg_info", reinit_subcommands=True) + ) egg_info.egg_base = str(tmp_dir) egg_info.ignore_egg_info_in_manifest = True - build = dist.reinitialize_command("build", reinit_subcommands=True) - install = dist.reinitialize_command("install", reinit_subcommands=True) + build = cast( + build_cls, dist.reinitialize_command("build", reinit_subcommands=True) + ) + install = cast( + install_cls, dist.reinitialize_command("install", reinit_subcommands=True) + ) build.build_platlib = build.build_purelib = build.build_lib = build_lib install.install_purelib = install.install_platlib = install.install_lib = wheel @@ -215,12 +227,14 @@ def _configure_build( install.install_headers = headers install.install_data = data - install_scripts = dist.get_command_obj("install_scripts") + install_scripts = cast( + install_scripts_cls, dist.get_command_obj("install_scripts") + ) install_scripts.no_ep = True build.build_temp = str(tmp_dir) - build_py = dist.get_command_obj("build_py") + build_py = cast(build_py_cls, dist.get_command_obj("build_py")) build_py.compile = False build_py.existing_egg_info_dir = self._find_egg_info_dir() @@ -233,6 +247,7 @@ def _set_editable_mode(self): """Set the ``editable_mode`` flag in the build sub-commands""" dist = self.distribution build = dist.get_command_obj("build") + # TODO: Update typeshed distutils stubs to overload non-None return type by default for cmd_name in build.get_sub_commands(): cmd = dist.get_command_obj(cmd_name) if hasattr(cmd, "editable_mode"): @@ -269,7 +284,7 @@ def _run_build_commands( self._run_install("data") return files, mapping - def _run_build_subcommands(self): + def _run_build_subcommands(self) -> None: """ Issue #3501 indicates that some plugins/customizations might rely on: @@ -283,7 +298,7 @@ def _run_build_subcommands(self): # TODO: Once plugins/customisations had the chance to catch up, replace # `self._run_build_subcommands()` with `self.run_command("build")`. # Also remove _safely_run, TestCustomBuildPy. Suggested date: Aug/2023. - build: Command = self.get_finalized_command("build") + build = self.get_finalized_command("build") for name in build.get_sub_commands(): cmd = self.get_finalized_command(name) if name == "build_py" and type(cmd) != build_py_cls: @@ -432,7 +447,8 @@ def __init__( ): self.auxiliary_dir = Path(auxiliary_dir) self.build_lib = Path(build_lib).resolve() - self._file = dist.get_command_obj("build_py").copy_file + # TODO: Update typeshed distutils stubs to overload non-None return type by default + self._file = dist.get_command_obj("build_py").copy_file # type: ignore[union-attr] super().__init__(dist, name, [self.auxiliary_dir]) def __call__(self, wheel: "WheelFile", files: List[str], mapping: Dict[str, str]): @@ -450,7 +466,9 @@ def _create_file(self, relative_output: str, src_file: str, link=None): dest = self.auxiliary_dir / relative_output if not dest.parent.is_dir(): dest.parent.mkdir(parents=True) - self._file(src_file, dest, link=link) + # TODO: Update typeshed distutils stubs so distutils.cmd.Command.copy_file, accepts PathLike + # same with methods used by copy_file + self._file(src_file, dest, link=link) # type: ignore[arg-type] def _create_links(self, outputs, output_mapping): self.auxiliary_dir.mkdir(parents=True, exist_ok=True) @@ -603,7 +621,8 @@ def _simple_layout( layout = {pkg: find_package_path(pkg, package_dir, project_dir) for pkg in packages} if not layout: return set(package_dir) in ({}, {""}) - parent = os.path.commonpath(starmap(_parent_path, layout.items())) + # TODO: has been fixed upstream, waiting for new mypy release https://github.com/python/typeshed/pull/11310 + parent = os.path.commonpath(starmap(_parent_path, layout.items())) # type: ignore[call-overload] return all( _path.same_path(Path(parent, *key.split('.')), value) for key, value in layout.items() diff --git a/setuptools/command/install.py b/setuptools/command/install.py index b97a9b4713..56c1155b50 100644 --- a/setuptools/command/install.py +++ b/setuptools/command/install.py @@ -3,9 +3,11 @@ import glob import platform import distutils.command.install as orig +from typing import cast import setuptools from ..warnings import SetuptoolsDeprecationWarning, SetuptoolsWarning +from .bdist_egg import bdist_egg as bdist_egg_cls # Prior to numpy 1.9, NumPy relies on the '_install' name, so provide it for # now. See https://github.com/pypa/setuptools/issues/199/ @@ -135,7 +137,8 @@ def do_egg_install(self): cmd.package_index.scan(glob.glob('*.egg')) self.run_command('bdist_egg') - args = [self.distribution.get_command_obj('bdist_egg').egg_output] + bdist_egg = cast(bdist_egg_cls, self.distribution.get_command_obj('bdist_egg')) + args = [bdist_egg.egg_output] if setuptools.bootstrap_install_from: # Bootstrap self-installation of setuptools diff --git a/setuptools/command/rotate.py b/setuptools/command/rotate.py index cfb78ce52d..6f73721c70 100644 --- a/setuptools/command/rotate.py +++ b/setuptools/command/rotate.py @@ -3,6 +3,7 @@ from distutils.errors import DistutilsOptionError import os import shutil +from typing import List from setuptools import Command @@ -17,7 +18,7 @@ class rotate(Command): ('keep=', 'k', "number of matching distributions to keep"), ] - boolean_options = [] + boolean_options: List[str] = [] def initialize_options(self): self.match = None diff --git a/setuptools/command/upload_docs.py b/setuptools/command/upload_docs.py index 32c9abd796..3fbbb62553 100644 --- a/setuptools/command/upload_docs.py +++ b/setuptools/command/upload_docs.py @@ -50,7 +50,7 @@ def has_sphinx(self): and metadata.entry_points(group='distutils.commands', name='build_sphinx') ) - sub_commands = [('build_sphinx', has_sphinx)] + sub_commands = [('build_sphinx', has_sphinx)] # type: ignore[list-item] # TODO: Fix in typeshed distutils stubs def initialize_options(self): upload.initialize_options(self) diff --git a/setuptools/config/_apply_pyprojecttoml.py b/setuptools/config/_apply_pyprojecttoml.py index 7301bc65c1..3626282a79 100644 --- a/setuptools/config/_apply_pyprojecttoml.py +++ b/setuptools/config/_apply_pyprojecttoml.py @@ -34,6 +34,7 @@ from ..warnings import SetuptoolsWarning if TYPE_CHECKING: + from distutils.dist import _OptionsList from setuptools._importlib import metadata # noqa from setuptools.dist import Distribution # noqa @@ -293,7 +294,7 @@ def _normalise_cmd_option_key(name: str) -> str: return json_compatible_key(name).strip("_=") -def _normalise_cmd_options(desc: List[Tuple[str, Optional[str], str]]) -> Set[str]: +def _normalise_cmd_options(desc: "_OptionsList") -> Set[str]: return {_normalise_cmd_option_key(fancy_option[0]) for fancy_option in desc} diff --git a/setuptools/config/expand.py b/setuptools/config/expand.py index e23a762cf5..0d8d58add8 100644 --- a/setuptools/config/expand.py +++ b/setuptools/config/expand.py @@ -63,7 +63,7 @@ class StaticModule: """Proxy to a module object that avoids executing arbitrary code.""" def __init__(self, name: str, spec: ModuleSpec): - module = ast.parse(pathlib.Path(spec.origin).read_bytes()) + module = ast.parse(pathlib.Path(spec.origin).read_bytes()) # type: ignore[arg-type] # Let it raise an error on None vars(self).update(locals()) del self.self diff --git a/setuptools/config/pyprojecttoml.py b/setuptools/config/pyprojecttoml.py index 5eb9421f1f..ff97679895 100644 --- a/setuptools/config/pyprojecttoml.py +++ b/setuptools/config/pyprojecttoml.py @@ -24,6 +24,7 @@ if TYPE_CHECKING: from setuptools.dist import Distribution # noqa + from typing_extensions import Self _logger = logging.getLogger(__name__) @@ -271,7 +272,7 @@ def _ensure_previously_set(self, dist: "Distribution", field: str): def _expand_directive( self, specifier: str, directive, package_dir: Mapping[str, str] ): - from setuptools.extern.more_itertools import always_iterable # type: ignore + from setuptools.extern.more_itertools import always_iterable with _ignore_errors(self.ignore_option_errors): root_dir = self.root_dir @@ -401,7 +402,7 @@ def __init__( self._project_cfg = project_cfg self._setuptools_cfg = setuptools_cfg - def __enter__(self): + def __enter__(self) -> "Self": """When entering the context, the values of ``packages``, ``py_modules`` and ``package_dir`` that are missing in ``dist`` are copied from ``setuptools_cfg``. """ diff --git a/setuptools/config/setupcfg.py b/setuptools/config/setupcfg.py index cfa43a57b5..2912d3e143 100644 --- a/setuptools/config/setupcfg.py +++ b/setuptools/config/setupcfg.py @@ -108,7 +108,7 @@ def _apply( filenames = [*other_files, filepath] try: - _Distribution.parse_config_files(dist, filenames=filenames) + _Distribution.parse_config_files(dist, filenames=filenames) # type: ignore[arg-type] # TODO: fix in disutils stubs handlers = parse_configuration( dist, dist.command_options, ignore_option_errors=ignore_option_errors ) @@ -475,7 +475,7 @@ def parse_section(self, section_options): # Keep silent for a new option may appear anytime. self[name] = value - def parse(self): + def parse(self) -> None: """Parses configuration file items from one or more related sections. diff --git a/setuptools/dist.py b/setuptools/dist.py index d5787ed474..6350e38100 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -10,7 +10,7 @@ from contextlib import suppress from glob import iglob from pathlib import Path -from typing import List, Optional, Set +from typing import TYPE_CHECKING, Dict, List, MutableMapping, Optional, Set, Tuple import distutils.cmd import distutils.command @@ -202,7 +202,11 @@ def check_packages(dist, attr, value): ) -_Distribution = get_unpatched(distutils.core.Distribution) +if TYPE_CHECKING: + # 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 +else: + _Distribution = get_unpatched(distutils.core.Distribution) class Distribution(_Distribution): @@ -283,12 +287,12 @@ def patch_missing_pkg_info(self, attrs): dist._version = _normalization.safe_version(str(attrs['version'])) self._patched_dist = dist - def __init__(self, attrs=None): + def __init__(self, attrs: Optional[MutableMapping] = None) -> None: have_package_data = hasattr(self, "package_data") if not have_package_data: - self.package_data = {} + self.package_data: Dict[str, List[str]] = {} attrs = attrs or {} - self.dist_files = [] + self.dist_files: List[Tuple[str, str, str]] = [] # Filter-out setuptools' specific options. self.src_root = attrs.pop("src_root", None) self.patch_missing_pkg_info(attrs) @@ -381,7 +385,7 @@ def _normalize_requires(self): k: list(map(str, _reqs.parse(v or []))) for k, v in extras_require.items() } - def _finalize_license_files(self): + def _finalize_license_files(self) -> None: """Compute names of all license files which should be included.""" license_files: Optional[List[str]] = self.metadata.license_files patterns: List[str] = license_files if license_files else [] @@ -394,7 +398,7 @@ def _finalize_license_files(self): # Default patterns match the ones wheel uses # See https://wheel.readthedocs.io/en/stable/user_guide.html # -> 'Including license files in the generated wheel file' - patterns = ('LICEN[CS]E*', 'COPYING*', 'NOTICE*', 'AUTHORS*') + patterns = ['LICEN[CS]E*', 'COPYING*', 'NOTICE*', 'AUTHORS*'] self.metadata.license_files = list( unique_everseen(self._expand_patterns(patterns)) diff --git a/setuptools/extension.py b/setuptools/extension.py index 58c023f6b4..8caad78d4b 100644 --- a/setuptools/extension.py +++ b/setuptools/extension.py @@ -3,6 +3,7 @@ import distutils.core import distutils.errors import distutils.extension +from typing import TYPE_CHECKING from .monkey import get_unpatched @@ -23,8 +24,11 @@ def _have_cython(): # for compatibility have_pyrex = _have_cython - -_Extension = get_unpatched(distutils.core.Extension) +if TYPE_CHECKING: + # 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 +else: + _Extension = get_unpatched(distutils.core.Extension) class Extension(_Extension): diff --git a/setuptools/monkey.py b/setuptools/monkey.py index da0993506c..fd07d91dec 100644 --- a/setuptools/monkey.py +++ b/setuptools/monkey.py @@ -8,11 +8,14 @@ import sys import types from importlib import import_module +from typing import List, TypeVar import distutils.filelist -__all__ = [] +_T = TypeVar("_T") + +__all__: List[str] = [] """ Everything is private. Contact the project team if you think you need this functionality. @@ -33,7 +36,7 @@ def _get_mro(cls): return inspect.getmro(cls) -def get_unpatched(item): +def get_unpatched(item: _T) -> _T: lookup = ( get_unpatched_class if isinstance(item, type) diff --git a/setuptools/msvc.py b/setuptools/msvc.py index 53fe7b0de1..b2a0f2bebb 100644 --- a/setuptools/msvc.py +++ b/setuptools/msvc.py @@ -20,9 +20,11 @@ import itertools import subprocess import distutils.errors +from typing import Dict, TYPE_CHECKING from setuptools.extern.more_itertools import unique_everseen -if platform.system() == 'Windows': +# https://github.com/python/mypy/issues/8166 +if not TYPE_CHECKING and platform.system() == 'Windows': import winreg from os import environ else: @@ -34,7 +36,7 @@ class winreg: HKEY_LOCAL_MACHINE = None HKEY_CLASSES_ROOT = None - environ = dict() + environ: Dict[str, str] = dict() def _msvc14_find_vc2015(): diff --git a/setuptools/sandbox.py b/setuptools/sandbox.py index 7634b1320b..6c095e029e 100644 --- a/setuptools/sandbox.py +++ b/setuptools/sandbox.py @@ -9,6 +9,7 @@ import pickle import textwrap import builtins +from typing import Union, List import pkg_resources from distutils.errors import DistutilsError @@ -19,7 +20,7 @@ else: _os = sys.modules[os.name] try: - _file = file + _file = file # type: ignore[name-defined] # Check for global variable except NameError: _file = None _open = open @@ -298,7 +299,7 @@ def run(self, func): with self: return func() - def _mk_dual_path_wrapper(name): + def _mk_dual_path_wrapper(name: str): # type: ignore[misc] # https://github.com/pypa/setuptools/pull/4099 original = getattr(_os, name) def wrap(self, src, dst, *args, **kw): @@ -312,7 +313,7 @@ def wrap(self, src, dst, *args, **kw): if hasattr(_os, name): locals()[name] = _mk_dual_path_wrapper(name) - def _mk_single_path_wrapper(name, original=None): + def _mk_single_path_wrapper(name: str, original=None): # type: ignore[misc] # https://github.com/pypa/setuptools/pull/4099 original = original or getattr(_os, name) def wrap(self, path, *args, **kw): @@ -349,7 +350,7 @@ def wrap(self, path, *args, **kw): if hasattr(_os, name): locals()[name] = _mk_single_path_wrapper(name) - def _mk_single_with_return(name): + def _mk_single_with_return(name: str): # type: ignore[misc] # https://github.com/pypa/setuptools/pull/4099 original = getattr(_os, name) def wrap(self, path, *args, **kw): @@ -364,7 +365,7 @@ def wrap(self, path, *args, **kw): if hasattr(_os, name): locals()[name] = _mk_single_with_return(name) - def _mk_query(name): + def _mk_query(name: str): # type: ignore[misc] # https://github.com/pypa/setuptools/pull/4099 original = getattr(_os, name) def wrap(self, *args, **kw): @@ -424,7 +425,7 @@ class DirectorySandbox(AbstractSandbox): "tempnam", ]) - _exception_patterns = [] + _exception_patterns: List[Union[str, re.Pattern]] = [] "exempt writing to paths that match the pattern" def __init__(self, sandbox, exceptions=_EXCEPTIONS): diff --git a/setuptools/tests/config/test_apply_pyprojecttoml.py b/setuptools/tests/config/test_apply_pyprojecttoml.py index e63a89b0b4..555489b140 100644 --- a/setuptools/tests/config/test_apply_pyprojecttoml.py +++ b/setuptools/tests/config/test_apply_pyprojecttoml.py @@ -9,6 +9,7 @@ import tarfile from inspect import cleandoc from pathlib import Path +from typing import Tuple from unittest.mock import Mock from zipfile import ZipFile @@ -455,7 +456,7 @@ def core_metadata(dist) -> str: # Make sure core metadata is valid Metadata.from_email(pkg_file_txt, validate=True) # can raise exceptions - skip_prefixes = () + skip_prefixes: Tuple[str, ...] = () skip_lines = set() # ---- DIFF NORMALISATION ---- # PEP 621 is very particular about author/maintainer metadata conversion, so skip diff --git a/setuptools/tests/integration/test_pip_install_sdist.py b/setuptools/tests/integration/test_pip_install_sdist.py index 3467a5ec07..17bf2af9d2 100644 --- a/setuptools/tests/integration/test_pip_install_sdist.py +++ b/setuptools/tests/integration/test_pip_install_sdist.py @@ -1,3 +1,5 @@ +# https://github.com/python/mypy/issues/16936 +# mypy: disable-error-code="has-type" """Integration tests for setuptools that focus on building packages via pip. The idea behind these tests is not to exhaustively check all the possible @@ -25,10 +27,10 @@ from .helpers import Archive, run - pytestmark = pytest.mark.integration -(LATEST,) = Enum("v", "LATEST") + +(LATEST,) = Enum("v", "LATEST") # type: ignore[misc] # https://github.com/python/mypy/issues/16936 """Default version to be checked""" # There are positive and negative aspects of checking the latest version of the # packages. diff --git a/setuptools/tests/test_bdist_egg.py b/setuptools/tests/test_bdist_egg.py index 0e473d168b..12ed4d328c 100644 --- a/setuptools/tests/test_bdist_egg.py +++ b/setuptools/tests/test_bdist_egg.py @@ -47,7 +47,7 @@ def test_bdist_egg(self, setup_context, user_override): assert re.match(r'foo-0.0.0-py[23].\d+.egg$', content) @pytest.mark.xfail( - os.environ.get('PYTHONDONTWRITEBYTECODE'), + os.environ.get('PYTHONDONTWRITEBYTECODE', False), reason="Byte code disabled", ) def test_exclude_source_files(self, setup_context, user_override): diff --git a/setuptools/tests/test_editable_install.py b/setuptools/tests/test_editable_install.py index 862f8172cd..df85699586 100644 --- a/setuptools/tests/test_editable_install.py +++ b/setuptools/tests/test_editable_install.py @@ -120,7 +120,7 @@ def editable_opts(request): @pytest.mark.parametrize( "files", [ - {**EXAMPLE, "setup.py": SETUP_SCRIPT_STUB}, # type: ignore + {**EXAMPLE, "setup.py": SETUP_SCRIPT_STUB}, EXAMPLE, # No setup.py script ], ) diff --git a/setuptools/tests/test_egg_info.py b/setuptools/tests/test_egg_info.py index af7d2f8295..ba019dc79d 100644 --- a/setuptools/tests/test_egg_info.py +++ b/setuptools/tests/test_egg_info.py @@ -79,7 +79,8 @@ def run(): @staticmethod def _extract_mv_version(pkg_info_lines: List[str]) -> Tuple[int, int]: version_str = pkg_info_lines[0].split(' ')[1] - return tuple(map(int, version_str.split('.')[:2])) + major, minor = map(int, version_str.split('.')[:2]) + return major, minor def test_egg_info_save_version_info_setup_empty(self, tmpdir_cwd, env): """ diff --git a/setuptools/tests/test_manifest.py b/setuptools/tests/test_manifest.py index fbd21b1976..16fa2c2460 100644 --- a/setuptools/tests/test_manifest.py +++ b/setuptools/tests/test_manifest.py @@ -10,6 +10,7 @@ import logging from distutils import log from distutils.errors import DistutilsTemplateError +from typing import List, Tuple from setuptools.command.egg_info import FileList, egg_info, translate_pattern from setuptools.dist import Distribution @@ -75,7 +76,7 @@ def touch(filename): ) -translate_specs = [ +translate_specs: List[Tuple[str, List[str], List[str]]] = [ ('foo', ['foo'], ['bar', 'foobar']), ('foo/bar', ['foo/bar'], ['foo/bar/baz', './foo/bar', 'foo']), # Glob matching