From 4eef53aa6785cf0d93087178587006fbc6302d84 Mon Sep 17 00:00:00 2001 From: Avasam Date: Tue, 27 Aug 2024 11:27:38 -0400 Subject: [PATCH] Merge simple type annotations from typeshed --- mypy.ini | 2 +- pyproject.toml | 4 --- ruff.toml | 2 ++ setuptools/__init__.py | 8 ++--- setuptools/_path.py | 7 ++-- setuptools/build_meta.py | 35 +++++++++++--------- setuptools/command/_requirestxt.py | 2 +- setuptools/command/bdist_egg.py | 16 +++++++++- setuptools/command/bdist_wheel.py | 6 ++-- setuptools/command/build_ext.py | 8 ++--- setuptools/command/build_py.py | 18 ++++++----- setuptools/command/easy_install.py | 24 ++++++++------ setuptools/command/editable_wheel.py | 25 +++++++-------- setuptools/command/egg_info.py | 6 ++-- setuptools/command/install_lib.py | 9 +++--- setuptools/command/install_scripts.py | 2 +- setuptools/command/sdist.py | 3 +- setuptools/config/_apply_pyprojecttoml.py | 22 +++++++------ setuptools/config/expand.py | 16 +++++----- setuptools/config/pyprojecttoml.py | 6 ++-- setuptools/config/setupcfg.py | 19 +++++++---- setuptools/depends.py | 16 +++++++--- setuptools/discovery.py | 13 +++++--- setuptools/dist.py | 39 +++++++++++++---------- setuptools/extension.py | 24 ++++++++++---- setuptools/glob.py | 4 +-- setuptools/installer.py | 3 +- setuptools/logging.py | 2 +- setuptools/msvc.py | 14 ++++---- setuptools/package_index.py | 28 ++++++++-------- setuptools/sandbox.py | 12 +++---- setuptools/tests/test_build_ext.py | 2 +- setuptools/warnings.py | 7 ++-- 33 files changed, 233 insertions(+), 171 deletions(-) diff --git a/mypy.ini b/mypy.ini index fe6b23844a..70d4e6e481 100644 --- a/mypy.ini +++ b/mypy.ini @@ -51,6 +51,6 @@ 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.*,setuptools._distutils.*] +[mypy-setuptools.config._validate_pyproject.*,setuptools._vendor.*,setuptools._distutils.*] follow_imports = silent # silent => ignore errors when following imports diff --git a/pyproject.toml b/pyproject.toml index fbcd4b48ef..cec8c8b54b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -211,7 +211,3 @@ formats = "zip" [tool.setuptools_scm] - - -[tool.pytest-enabler.mypy] -# Disabled due to jaraco/skeleton#143 diff --git a/ruff.toml b/ruff.toml index b55b4e8067..ca778e51a9 100644 --- a/ruff.toml +++ b/ruff.toml @@ -53,6 +53,8 @@ ignore = [ # Only enforcing return type annotations for public modules "**/tests/**" = ["ANN2"] "tools/**" = ["ANN2"] +# Temporarily disabling enforced return annotations for the setuptool package to progressively type from Typeshed +"setuptools/**" = ["ANN2"] # Suppress nuisance warnings about module-import-not-at-top-of-file (E402) due to workaround for #4476 "setuptools/__init__.py" = ["E402"] "pkg_resources/__init__.py" = ["E402"] diff --git a/setuptools/__init__.py b/setuptools/__init__.py index ab373c51d6..625ecf7f5d 100644 --- a/setuptools/__init__.py +++ b/setuptools/__init__.py @@ -89,7 +89,7 @@ def finalize_options(self): _fetch_build_eggs(dist) -def _fetch_build_eggs(dist): +def _fetch_build_eggs(dist: Distribution): try: dist.fetch_build_eggs(dist.setup_requires) except Exception as ex: @@ -120,10 +120,8 @@ 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: TypeAlias = distutils.core.Command + from distutils.core import Command as _Command else: _Command = monkey.get_unpatched(distutils.core.Command) @@ -188,7 +186,7 @@ def _ensure_stringlike(self, option, what, default=None): ) return val - def ensure_string_list(self, option): + def ensure_string_list(self, option: str): # type: ignore[override] # Fixed in typeshed for mypy 1.12 r"""Ensure that 'option' is a list of strings. If 'option' is currently a string, we split it either on /,\s*/ or /\s+/, so "foo bar baz", "foo,bar,baz", and "foo, bar baz" all become diff --git a/setuptools/_path.py b/setuptools/_path.py index dd4a9db8cb..5a2bdbd0d4 100644 --- a/setuptools/_path.py +++ b/setuptools/_path.py @@ -5,15 +5,14 @@ import sys from typing import TYPE_CHECKING, Union +from more_itertools import unique_everseen + if TYPE_CHECKING: from typing_extensions import TypeAlias - -from more_itertools import unique_everseen - -if sys.version_info >= (3, 9): StrPath: TypeAlias = Union[str, os.PathLike[str]] # Same as _typeshed.StrPath else: + # Python 3.8 support StrPath: TypeAlias = Union[str, os.PathLike] diff --git a/setuptools/build_meta.py b/setuptools/build_meta.py index a6b85afc42..f6017a1d83 100644 --- a/setuptools/build_meta.py +++ b/setuptools/build_meta.py @@ -38,7 +38,7 @@ import tokenize import warnings from pathlib import Path -from typing import TYPE_CHECKING, Dict, Iterable, Iterator, List, Union +from typing import TYPE_CHECKING, Iterable, Iterator, List, Mapping, Union import setuptools @@ -53,7 +53,6 @@ if TYPE_CHECKING: from typing_extensions import TypeAlias - __all__ = [ 'get_requires_for_build_sdist', 'get_requires_for_build_wheel', @@ -147,7 +146,7 @@ def suppress_known_deprecation(): yield -_ConfigSettings: TypeAlias = Union[Dict[str, Union[str, List[str], None]], None] +_ConfigSettings: TypeAlias = Union[Mapping[str, Union[str, List[str], None]], None] """ Currently the user can run:: @@ -291,7 +290,9 @@ def _arbitrary_args(self, config_settings: _ConfigSettings) -> Iterator[str]: class _BuildMetaBackend(_ConfigSettingsTranslator): - def _get_build_requires(self, config_settings, requirements): + def _get_build_requires( + self, config_settings: _ConfigSettings, requirements: list[str] + ): sys.argv = [ *sys.argv[:1], *self._global_args(config_settings), @@ -305,7 +306,7 @@ def _get_build_requires(self, config_settings, requirements): return requirements - def run_setup(self, setup_script='setup.py'): + def run_setup(self, setup_script: str = 'setup.py'): # Note that we can reuse our build directory between calls # Correctness comes first, then optimization later __file__ = os.path.abspath(setup_script) @@ -328,13 +329,15 @@ def run_setup(self, setup_script='setup.py'): "setup-py-deprecated.html", ) - def get_requires_for_build_wheel(self, config_settings=None): + def get_requires_for_build_wheel(self, config_settings: _ConfigSettings = None): return self._get_build_requires(config_settings, requirements=[]) - def get_requires_for_build_sdist(self, config_settings=None): + def get_requires_for_build_sdist(self, config_settings: _ConfigSettings = None): return self._get_build_requires(config_settings, requirements=[]) - def _bubble_up_info_directory(self, metadata_directory: str, suffix: str) -> str: + def _bubble_up_info_directory( + self, metadata_directory: StrPath, suffix: str + ) -> str: """ PEP 517 requires that the .dist-info directory be placed in the metadata_directory. To comply, we MUST copy the directory to the root. @@ -347,7 +350,7 @@ def _bubble_up_info_directory(self, metadata_directory: str, suffix: str) -> str # PEP 517 allow other files and dirs to exist in metadata_directory return info_dir.name - def _find_info_directory(self, metadata_directory: str, suffix: str) -> Path: + def _find_info_directory(self, metadata_directory: StrPath, suffix: str) -> Path: for parent, dirs, _ in os.walk(metadata_directory): candidates = [f for f in dirs if f.endswith(suffix)] @@ -359,14 +362,14 @@ def _find_info_directory(self, metadata_directory: str, suffix: str) -> Path: raise errors.InternalError(msg) def prepare_metadata_for_build_wheel( - self, metadata_directory, config_settings=None + self, metadata_directory: StrPath, config_settings: _ConfigSettings = None ): sys.argv = [ *sys.argv[:1], *self._global_args(config_settings), "dist_info", "--output-dir", - metadata_directory, + str(metadata_directory), "--keep-egg-info", ] with no_install_setup_requires(): @@ -449,7 +452,7 @@ def build_editable( self, wheel_directory: StrPath, config_settings: _ConfigSettings = None, - metadata_directory: str | None = None, + metadata_directory: StrPath | None = None, ): # XXX can or should we hide our editable_wheel command normally? info_dir = self._get_dist_info_dir(metadata_directory) @@ -460,11 +463,13 @@ def build_editable( cmd, ".whl", wheel_directory, config_settings ) - def get_requires_for_build_editable(self, config_settings=None): + def get_requires_for_build_editable( + self, config_settings: _ConfigSettings = None + ): return self.get_requires_for_build_wheel(config_settings) def prepare_metadata_for_build_editable( - self, metadata_directory, config_settings=None + self, metadata_directory: StrPath, config_settings: _ConfigSettings = None ): return self.prepare_metadata_for_build_wheel( metadata_directory, config_settings @@ -483,7 +488,7 @@ class _BuildMetaLegacyBackend(_BuildMetaBackend): and will eventually be removed. """ - def run_setup(self, setup_script='setup.py'): + def run_setup(self, setup_script: str = 'setup.py'): # In order to maintain compatibility with scripts assuming that # the setup.py script is in a directory on the PYTHONPATH, inject # '' into sys.path. (pypa/setuptools#1642) diff --git a/setuptools/command/_requirestxt.py b/setuptools/command/_requirestxt.py index b87476d6f4..9ae9054ff3 100644 --- a/setuptools/command/_requirestxt.py +++ b/setuptools/command/_requirestxt.py @@ -18,12 +18,12 @@ from packaging.requirements import Requirement from .. import _reqs +from .._reqs import _StrOrIter # dict can work as an ordered set _T = TypeVar("_T") _Ordered = Dict[_T, None] _ordered = dict -_StrOrIter = _reqs._StrOrIter def _prepare( diff --git a/setuptools/command/bdist_egg.py b/setuptools/command/bdist_egg.py index f3b7150208..c9eee16a5d 100644 --- a/setuptools/command/bdist_egg.py +++ b/setuptools/command/bdist_egg.py @@ -2,6 +2,8 @@ Build .egg distributions""" +from __future__ import annotations + import marshal import os import re @@ -9,6 +11,7 @@ import textwrap from sysconfig import get_path, get_python_version from types import CodeType +from typing import TYPE_CHECKING, Literal from setuptools import Command from setuptools.extension import Library @@ -18,6 +21,12 @@ from distutils import log from distutils.dir_util import mkpath, remove_tree +if TYPE_CHECKING: + from typing_extensions import TypeAlias + +# Same as zipfile._ZipFileMode from typeshed +_ZipFileMode: TypeAlias = Literal["r", "w", "x", "a"] + def _get_purelib(): return get_path("purelib") @@ -431,7 +440,12 @@ def can_scan(): def make_zipfile( - zip_filename, base_dir, verbose=False, dry_run=False, compress=True, mode='w' + zip_filename, + base_dir, + verbose: bool = False, + dry_run: bool = False, + compress=True, + mode: _ZipFileMode = 'w', ): """Create a zip file from all the files under 'base_dir'. The output zip file will be named 'base_dir' + ".zip". Uses either the "zipfile" diff --git a/setuptools/command/bdist_wheel.py b/setuptools/command/bdist_wheel.py index 8f06786659..6343e31b7b 100644 --- a/setuptools/command/bdist_wheel.py +++ b/setuptools/command/bdist_wheel.py @@ -248,9 +248,9 @@ def initialize_options(self) -> None: self.relative = False self.owner = None self.group = None - self.universal: bool = False - self.compression: int | str = "deflated" - self.python_tag: str = python_tag() + self.universal = False + self.compression: str | int = "deflated" + self.python_tag = python_tag() self.build_number: str | None = None self.py_limited_api: str | Literal[False] = False self.plat_name_supplied = False diff --git a/setuptools/command/build_ext.py b/setuptools/command/build_ext.py index e2a88ce218..1b9c313ff5 100644 --- a/setuptools/command/build_ext.py +++ b/setuptools/command/build_ext.py @@ -89,8 +89,8 @@ def get_abi3_suffix(): class build_ext(_build_ext): distribution: Distribution # override distutils.dist.Distribution with setuptools.dist.Distribution - editable_mode: bool = False - inplace: bool = False + editable_mode = False + inplace = False def run(self): """Build extensions in build directory, then copy if --inplace""" @@ -410,7 +410,7 @@ def link_shared_object( library_dirs=None, runtime_library_dirs=None, export_symbols=None, - debug=False, + debug: bool = False, extra_preargs=None, extra_postargs=None, build_temp=None, @@ -445,7 +445,7 @@ def link_shared_object( library_dirs=None, runtime_library_dirs=None, export_symbols=None, - debug=False, + debug: bool = False, extra_preargs=None, extra_postargs=None, build_temp=None, diff --git a/setuptools/command/build_py.py b/setuptools/command/build_py.py index 584d2c15ac..628a20b40b 100644 --- a/setuptools/command/build_py.py +++ b/setuptools/command/build_py.py @@ -12,6 +12,8 @@ from more_itertools import unique_everseen +from setuptools._path import StrPath + from ..dist import Distribution from ..warnings import SetuptoolsDeprecationWarning @@ -48,14 +50,14 @@ def finalize_options(self): del self.__dict__['data_files'] self.__updated_files = [] - def copy_file( + def copy_file( # type: ignore[override] # No overload, str support only self, - infile, - outfile, - preserve_mode=True, - preserve_times=True, - link=None, - level=1, + infile: StrPath, + outfile: StrPath, + preserve_mode: bool = True, + preserve_times: bool = True, + link: str | None = None, + level: object = 1, ): # Overwrite base class to allow using links if link: @@ -141,7 +143,7 @@ def find_data_files(self, package, src_dir): ) return self.exclude_data_files(package, src_dir, files) - def get_outputs(self, include_bytecode=True) -> list[str]: + def get_outputs(self, include_bytecode: bool = True) -> list[str]: # type: ignore[override] # Using a real boolean instead of 0|1 """See :class:`setuptools.commands.build.SubCommand`""" if self.editable_mode: return list(self.get_output_mapping().keys()) diff --git a/setuptools/command/easy_install.py b/setuptools/command/easy_install.py index 46c0a231eb..4016ae499b 100644 --- a/setuptools/command/easy_install.py +++ b/setuptools/command/easy_install.py @@ -423,7 +423,7 @@ def expand_dirs(self): ] self._expand_attrs(dirs) - def run(self, show_deprecation=True): + def run(self, show_deprecation: bool = True): if show_deprecation: self.announce( "WARNING: The easy_install command is deprecated " @@ -671,7 +671,7 @@ def _tmpdir(self): finally: os.path.exists(tmpdir) and _rmtree(tmpdir) - def easy_install(self, spec, deps=False): + def easy_install(self, spec, deps: bool = False): with self._tmpdir() as tmpdir: if not isinstance(spec, Requirement): if URL_SCHEME(spec): @@ -708,7 +708,7 @@ def easy_install(self, spec, deps=False): else: return self.install_item(spec, dist.location, tmpdir, deps) - def install_item(self, spec, download, tmpdir, deps, install_needed=False): + def install_item(self, spec, download, tmpdir, deps, install_needed: bool = False): # Installation is also needed if file in tmpdir or is not an egg install_needed = install_needed or self.always_copy install_needed = install_needed or os.path.dirname(download) == tmpdir @@ -756,7 +756,7 @@ def process_distribution( # noqa: C901 self, requirement, dist, - deps=True, + deps: bool = True, *info, ): self.update_pth(dist) @@ -857,7 +857,7 @@ def _load_template(dev_path): raw_bytes = resource_string('setuptools', name) return raw_bytes.decode('utf-8') - def write_script(self, script_name, contents, mode="t", blockers=()): + def write_script(self, script_name, contents, mode: str = "t", blockers=()): """Write an executable file to the scripts directory""" self.delete_blockers( # clean up old .py/.pyw w/o a script [os.path.join(self.script_dir, x) for x in blockers] @@ -1140,7 +1140,7 @@ def install_wheel(self, wheel_path, tmpdir): """ ) - def installation_report(self, req, dist, what="Installed"): + def installation_report(self, req, dist, what: str = "Installed"): """Helpful installation message for display to package users""" msg = "\n%(what)s %(eggloc)s%(extras)s" if self.multi_version and not self.no_report: @@ -2078,7 +2078,7 @@ def from_environment(cls): return cls([cls._sys_executable()]) @classmethod - def from_string(cls, string): + def from_string(cls, string: str): """ Construct a command spec from a simple string representing a command line parseable by shlex.split. @@ -2086,7 +2086,7 @@ def from_string(cls, string): items = shlex.split(string, **cls.split_args) return cls(items) - def install_options(self, script_text): + def install_options(self, script_text: str): self.options = shlex.split(self._extract_options(script_text)) cmdline = subprocess.list2cmdline(self) if not isascii(cmdline): @@ -2216,7 +2216,11 @@ def _get_script_args(cls, type_, name, header, script_text): yield (name, header + script_text) @classmethod - def get_header(cls, script_text="", executable=None): + def get_header( + cls, + script_text: str = "", + executable: str | CommandSpec | Iterable[str] | None = None, + ): """Create a #! line, getting options (if any) from script_text""" cmd = cls.command_spec_class.best().from_param(executable) cmd.install_options(script_text) @@ -2338,7 +2342,7 @@ def load_launcher_manifest(name): return manifest.decode('utf-8') % vars() -def _rmtree(path, ignore_errors=False, onexc=auto_chmod): +def _rmtree(path, ignore_errors: bool = False, onexc=auto_chmod): return py311.shutil_rmtree(path, ignore_errors, onexc) diff --git a/setuptools/command/editable_wheel.py b/setuptools/command/editable_wheel.py index 4a8b3abb43..d5342ca023 100644 --- a/setuptools/command/editable_wheel.py +++ b/setuptools/command/editable_wheel.py @@ -23,7 +23,6 @@ from itertools import chain, starmap from pathlib import Path from tempfile import TemporaryDirectory -from types import TracebackType from typing import TYPE_CHECKING, Iterable, Iterator, Mapping, Protocol, TypeVar, cast from .. import Command, _normalization, _path, errors, namespaces @@ -138,7 +137,7 @@ def run(self): self._create_wheel_file(bdist_wheel) except Exception: traceback.print_exc() - # TODO: Fix false-positive [attr-defined] in typeshed + # TODO: Pending fix for false-positive [attr-defined] in typeshed https://github.com/python/typeshed/pull/12467 project = self.distribution.name or self.distribution.get_name() _DebuggingTips.emit(project=project) raise @@ -231,7 +230,6 @@ 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"): @@ -380,16 +378,15 @@ def _select_strategy( class EditableStrategy(Protocol): - def __call__(self, wheel: WheelFile, files: list[str], mapping: dict[str, str]): ... - + def __call__( + self, wheel: WheelFile, files: list[str], mapping: Mapping[str, str] + ): ... def __enter__(self) -> Self: ... - def __exit__( self, - exc_type: type[BaseException] | None, - exc_value: BaseException | None, - traceback: TracebackType | None, - /, + _exc_type: object, + _exc_value: object, + _traceback: object, ) -> object: ... @@ -399,7 +396,7 @@ def __init__(self, dist: Distribution, name: str, path_entries: list[Path]): self.name = name self.path_entries = path_entries - def __call__(self, wheel: WheelFile, files: list[str], mapping: dict[str, str]): + def __call__(self, wheel: WheelFile, files: list[str], mapping: Mapping[str, str]): entries = "\n".join(str(p.resolve()) for p in self.path_entries) contents = _encode_pth(f"{entries}\n") wheel.writestr(f"__editable__.{self.name}.pth", contents) @@ -444,7 +441,7 @@ def __init__( self._file = dist.get_command_obj("build_py").copy_file super().__init__(dist, name, [self.auxiliary_dir]) - def __call__(self, wheel: WheelFile, files: list[str], mapping: dict[str, str]): + def __call__(self, wheel: WheelFile, files: list[str], mapping: Mapping[str, str]): self._create_links(files, mapping) super().__call__(wheel, files, mapping) @@ -461,7 +458,7 @@ def _create_file(self, relative_output: str, src_file: str, link=None): dest.parent.mkdir(parents=True) self._file(src_file, dest, link=link) - def _create_links(self, outputs, output_mapping): + def _create_links(self, outputs, output_mapping: Mapping[str, str]): self.auxiliary_dir.mkdir(parents=True, exist_ok=True) link_type = "sym" if _can_symlink_files(self.auxiliary_dir) else "hard" normalised = ((self._normalize_output(k), v) for k, v in output_mapping.items()) @@ -538,7 +535,7 @@ def get_implementation(self) -> Iterator[tuple[str, bytes]]: content = _encode_pth(f"import {finder}; {finder}.install()") yield (f"__editable__.{self.name}.pth", content) - def __call__(self, wheel: WheelFile, files: list[str], mapping: dict[str, str]): + def __call__(self, wheel: WheelFile, files: list[str], mapping: Mapping[str, str]): for file, content in self.get_implementation(): wheel.writestr(file, content) diff --git a/setuptools/command/egg_info.py b/setuptools/command/egg_info.py index 09255a3240..3b7a216888 100644 --- a/setuptools/command/egg_info.py +++ b/setuptools/command/egg_info.py @@ -255,7 +255,7 @@ def _get_egg_basename(self, py_version=PY_MAJOR, platform=None): """Compute filename of the output egg. Private API.""" return _egg_basename(self.egg_name, self.egg_version, py_version, platform) - def write_or_delete_file(self, what, filename, data, force=False): + def write_or_delete_file(self, what, filename, data, force: bool = False): """Write `data` to `filename` or delete if empty If `data` is non-empty, this routine is the same as ``write_file()``. @@ -323,7 +323,7 @@ def find_sources(self): class FileList(_FileList): # Implementations of the various MANIFEST.in commands - def __init__(self, warn=None, debug_print=None, ignore_egg_info_dir=False): + def __init__(self, warn=None, debug_print=None, ignore_egg_info_dir: bool = False): super().__init__(warn, debug_print) self.ignore_egg_info_dir = ignore_egg_info_dir @@ -699,7 +699,7 @@ def overwrite_arg(cmd, basename, filename): write_arg(cmd, basename, filename, True) -def write_arg(cmd, basename, filename, force=False): +def write_arg(cmd, basename, filename, force: bool = False): argname = os.path.splitext(basename)[0] value = getattr(cmd.distribution, argname, None) if value is not None: diff --git a/setuptools/command/install_lib.py b/setuptools/command/install_lib.py index 292f07ab6e..53b68f6363 100644 --- a/setuptools/command/install_lib.py +++ b/setuptools/command/install_lib.py @@ -95,10 +95,11 @@ def copy_tree( self, infile: StrPath, outfile: str, - preserve_mode=True, - preserve_times=True, - preserve_symlinks=False, - level=1, + # override: Using actual booleans + preserve_mode: bool = True, # type: ignore[override] + preserve_times: bool = True, # type: ignore[override] + preserve_symlinks: bool = False, # type: ignore[override] + level: object = 1, ) -> list[str]: assert preserve_mode and preserve_times and not preserve_symlinks exclude = self.get_exclusions() diff --git a/setuptools/command/install_scripts.py b/setuptools/command/install_scripts.py index 7b90611d1c..f1ccc2bbf8 100644 --- a/setuptools/command/install_scripts.py +++ b/setuptools/command/install_scripts.py @@ -56,7 +56,7 @@ def _install_ep_scripts(self): for args in writer.get_args(dist, cmd.as_header()): self.write_script(*args) - def write_script(self, script_name, contents, mode="t", *ignored): + def write_script(self, script_name, contents, mode: str = "t", *ignored): """Write an executable file to the scripts directory""" from setuptools.command.easy_install import chmod, current_umask diff --git a/setuptools/command/sdist.py b/setuptools/command/sdist.py index 68afab89b4..4e4fb8a504 100644 --- a/setuptools/command/sdist.py +++ b/setuptools/command/sdist.py @@ -3,6 +3,7 @@ import contextlib import os from itertools import chain +from typing import ClassVar from .._importlib import metadata from ..dist import Distribution @@ -48,7 +49,7 @@ class sdist(orig.sdist): ] distribution: Distribution # override distutils.dist.Distribution with setuptools.dist.Distribution - negative_opt: dict[str, str] = {} + 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/_apply_pyprojecttoml.py b/setuptools/config/_apply_pyprojecttoml.py index 7b9c0b1a59..749bb5b102 100644 --- a/setuptools/config/_apply_pyprojecttoml.py +++ b/setuptools/config/_apply_pyprojecttoml.py @@ -29,12 +29,12 @@ from setuptools._importlib import metadata from setuptools.dist import Distribution - from distutils.dist import _OptionsList + from distutils.dist import _OptionsList # Comes from typeshed + EMPTY: Mapping = MappingProxyType({}) # Immutable dict-like _ProjectReadmeValue: TypeAlias = Union[str, Dict[str, str]] -_CorrespFn: TypeAlias = Callable[["Distribution", Any, StrPath], None] -_Correspondence: TypeAlias = Union[str, _CorrespFn] +_Correspondence: TypeAlias = Callable[["Distribution", Any, Union[StrPath, None]], None] _logger = logging.getLogger(__name__) @@ -146,7 +146,9 @@ def _guess_content_type(file: str) -> str | None: raise ValueError(f"Undefined content type for {file}, {msg}") -def _long_description(dist: Distribution, val: _ProjectReadmeValue, root_dir: StrPath): +def _long_description( + dist: Distribution, val: _ProjectReadmeValue, root_dir: StrPath | None +): from setuptools.config import expand file: str | tuple[()] @@ -168,7 +170,7 @@ def _long_description(dist: Distribution, val: _ProjectReadmeValue, root_dir: St dist._referenced_files.add(file) -def _license(dist: Distribution, val: dict, root_dir: StrPath): +def _license(dist: Distribution, val: dict, root_dir: StrPath | None): from setuptools.config import expand if "file" in val: @@ -178,7 +180,7 @@ def _license(dist: Distribution, val: dict, root_dir: StrPath): _set_config(dist, "license", val["text"]) -def _people(dist: Distribution, val: list[dict], _root_dir: StrPath, kind: str): +def _people(dist: Distribution, val: list[dict], _root_dir: StrPath | None, kind: str): field = [] email_field = [] for person in val: @@ -196,24 +198,24 @@ def _people(dist: Distribution, val: list[dict], _root_dir: StrPath, kind: str): _set_config(dist, f"{kind}_email", ", ".join(email_field)) -def _project_urls(dist: Distribution, val: dict, _root_dir): +def _project_urls(dist: Distribution, val: dict, _root_dir: StrPath | None): _set_config(dist, "project_urls", val) -def _python_requires(dist: Distribution, val: str, _root_dir): +def _python_requires(dist: Distribution, val: str, _root_dir: StrPath | None): from packaging.specifiers import SpecifierSet _set_config(dist, "python_requires", SpecifierSet(val)) -def _dependencies(dist: Distribution, val: list, _root_dir): +def _dependencies(dist: Distribution, val: list, _root_dir: StrPath | None): if getattr(dist, "install_requires", []): msg = "`install_requires` overwritten in `pyproject.toml` (dependencies)" SetuptoolsWarning.emit(msg) dist.install_requires = val -def _optional_dependencies(dist: Distribution, val: dict, _root_dir): +def _optional_dependencies(dist: Distribution, val: dict, _root_dir: StrPath | None): existing = getattr(dist, "extras_require", None) or {} dist.extras_require = {**existing, **val} diff --git a/setuptools/config/expand.py b/setuptools/config/expand.py index e11bcf9b42..7396fbc634 100644 --- a/setuptools/config/expand.py +++ b/setuptools/config/expand.py @@ -45,7 +45,7 @@ from setuptools.dist import Distribution _K = TypeVar("_K") -_V = TypeVar("_V", covariant=True) +_V_co = TypeVar("_V_co", covariant=True) class StaticModule: @@ -352,7 +352,7 @@ def canonic_data_files( ] -def entry_points(text: str, text_source="entry-points") -> dict[str, dict]: +def entry_points(text: str, text_source: str = "entry-points") -> dict[str, dict]: """Given the contents of entry-points file, process it into a 2-level dictionary (``dict[str, dict[str, str]]``). The first level keys are entry-point groups, the second level keys are @@ -395,7 +395,7 @@ def __exit__( exc_type: type[BaseException] | None, exc_value: BaseException | None, traceback: TracebackType | None, - ) -> None: + ): if self._called: self._dist.set_defaults.analyse_name() # Now we can set a default name @@ -410,7 +410,7 @@ def package_dir(self) -> Mapping[str, str]: return LazyMappingProxy(self._get_package_dir) -class LazyMappingProxy(Mapping[_K, _V]): +class LazyMappingProxy(Mapping[_K, _V_co]): """Mapping proxy that delays resolving the target object, until really needed. >>> def obtain_mapping(): @@ -424,16 +424,16 @@ class LazyMappingProxy(Mapping[_K, _V]): 'other value' """ - def __init__(self, obtain_mapping_value: Callable[[], Mapping[_K, _V]]): + def __init__(self, obtain_mapping_value: Callable[[], Mapping[_K, _V_co]]): self._obtain = obtain_mapping_value - self._value: Mapping[_K, _V] | None = None + self._value: Mapping[_K, _V_co] | None = None - def _target(self) -> Mapping[_K, _V]: + def _target(self) -> Mapping[_K, _V_co]: if self._value is None: self._value = self._obtain() return self._value - def __getitem__(self, key: _K) -> _V: + def __getitem__(self, key: _K) -> _V_co: return self._target()[key] def __len__(self) -> int: diff --git a/setuptools/config/pyprojecttoml.py b/setuptools/config/pyprojecttoml.py index 3449a4bfb7..007124ea88 100644 --- a/setuptools/config/pyprojecttoml.py +++ b/setuptools/config/pyprojecttoml.py @@ -63,7 +63,7 @@ def validate(config: dict, filepath: StrPath) -> bool: def apply_configuration( dist: Distribution, filepath: StrPath, - ignore_option_errors=False, + ignore_option_errors: bool = False, ) -> Distribution: """Apply the configuration from a ``pyproject.toml`` file into an existing distribution object. @@ -74,8 +74,8 @@ def apply_configuration( def read_configuration( filepath: StrPath, - expand=True, - ignore_option_errors=False, + expand: bool = True, + ignore_option_errors: bool = False, dist: Distribution | None = None, ) -> dict[str, Any]: """Read given configuration file and returns options from it as a dict. diff --git a/setuptools/config/setupcfg.py b/setuptools/config/setupcfg.py index 54469f74a3..bc74b89688 100644 --- a/setuptools/config/setupcfg.py +++ b/setuptools/config/setupcfg.py @@ -42,22 +42,25 @@ from . import expand if TYPE_CHECKING: + from typing_extensions import TypeAlias + from setuptools.dist import Distribution from distutils.dist import DistributionMetadata -SingleCommandOptions = Dict["str", Tuple["str", Any]] +SingleCommandOptions: TypeAlias = Dict[str, Tuple[str, Any]] """Dict that associate the name of the options of a particular command to a tuple. The first element of the tuple indicates the origin of the option value (e.g. the name of the configuration file where it was read from), while the second element of the tuple is the option value itself """ -AllCommandOptions = Dict["str", SingleCommandOptions] # cmd name => its options +AllCommandOptions: TypeAlias = Dict[str, SingleCommandOptions] +"""cmd name => its options""" Target = TypeVar("Target", bound=Union["Distribution", "DistributionMetadata"]) def read_configuration( - filepath: StrPath, find_others=False, ignore_option_errors=False + filepath: StrPath, find_others: bool = False, ignore_option_errors: bool = False ) -> dict: """Read given configuration file and returns options from it as a dict. @@ -134,7 +137,9 @@ def _get_option(target_obj: Target, key: str): return getter() -def configuration_to_dict(handlers: tuple[ConfigHandler, ...]) -> dict: +def configuration_to_dict( + handlers: tuple[ConfigHandler[Distribution | DistributionMetadata], ...], +) -> dict: """Returns configuration data gathered by given handlers as a dict. :param list[ConfigHandler] handlers: Handlers list, @@ -155,7 +160,7 @@ def configuration_to_dict(handlers: tuple[ConfigHandler, ...]) -> dict: def parse_configuration( distribution: Distribution, command_options: AllCommandOptions, - ignore_option_errors=False, + ignore_option_errors: bool = False, ) -> tuple[ConfigMetadataHandler, ConfigOptionsHandler]: """Performs additional parsing of configuration options for a distribution. @@ -376,7 +381,7 @@ def parser(value): return parser - def _parse_file(self, value, root_dir: StrPath): + def _parse_file(self, value, root_dir: StrPath | None): """Represents value as a string, allowing including text from nearest files using `file:` directive. @@ -544,7 +549,7 @@ def __init__( ignore_option_errors: bool, ensure_discovered: expand.EnsurePackagesDiscovered, package_dir: dict | None = None, - root_dir: StrPath = os.curdir, + root_dir: StrPath | None = os.curdir, ): super().__init__(target_obj, options, ignore_option_errors, ensure_discovered) self.package_dir = package_dir diff --git a/setuptools/depends.py b/setuptools/depends.py index 9398b95331..e73f06808e 100644 --- a/setuptools/depends.py +++ b/setuptools/depends.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import contextlib import dis import marshal @@ -15,7 +17,13 @@ class Require: """A prerequisite to building or installing a distribution""" def __init__( - self, name, requested_version, module, homepage='', attribute=None, format=None + self, + name, + requested_version, + module, + homepage: str = '', + attribute=None, + format=None, ): if format is None and requested_version is not None: format = Version @@ -43,7 +51,7 @@ def version_ok(self, version): and self.format(version) >= self.requested_version ) - def get_version(self, paths=None, default="unknown"): + def get_version(self, paths=None, default: str = "unknown"): """Get version number of installed module, 'None', or 'default' Search 'paths' for module. If not found, return 'None'. If found, @@ -98,7 +106,7 @@ def empty(): # XXX it'd be better to test assertions about bytecode instead. if not sys.platform.startswith('java') and sys.platform != 'cli': - def get_module_constant(module, symbol, default=-1, paths=None): + def get_module_constant(module, symbol, default: str | int = -1, paths=None): """Find 'module' by searching 'paths', and extract 'symbol' Return 'None' if 'module' does not exist on 'paths', or it does not define @@ -126,7 +134,7 @@ def get_module_constant(module, symbol, default=-1, paths=None): return extract_constant(code, symbol, default) - def extract_constant(code, symbol, default=-1): + def extract_constant(code, symbol, default: str | int = -1): """Extract the constant value of 'symbol' from 'code' If the name 'symbol' is bound to a constant value by the Python code diff --git a/setuptools/discovery.py b/setuptools/discovery.py index 577be2f16b..ca1438d30d 100644 --- a/setuptools/discovery.py +++ b/setuptools/discovery.py @@ -53,13 +53,14 @@ from distutils import log from distutils.util import convert_path -StrIter = Iterator[str] - -chain_iter = itertools.chain.from_iterable - if TYPE_CHECKING: + from typing_extensions import TypeAlias + from setuptools import Distribution +StrIter: TypeAlias = Iterator[str] +chain_iter = itertools.chain.from_iterable + def _valid_name(path: StrPath) -> bool: # Ignore invalid names that cannot be imported directly @@ -328,7 +329,9 @@ def _package_dir(self) -> dict[str, str]: return {} return self.dist.package_dir - def __call__(self, force=False, name=True, ignore_ext_modules=False): + def __call__( + self, force: bool = False, name: bool = True, ignore_ext_modules: bool = False + ): """Automatically discover missing configuration fields and modifies the given ``distribution`` object in-place. diff --git a/setuptools/dist.py b/setuptools/dist.py index 68f877decd..9f18d7d337 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -6,21 +6,25 @@ import os import re import sys +from collections.abc import Iterable from glob import iglob from pathlib import Path -from typing import TYPE_CHECKING, MutableMapping +from typing import TYPE_CHECKING, Any, MutableMapping from more_itertools import partition, unique_everseen from packaging.markers import InvalidMarker, Marker from packaging.specifiers import InvalidSpecifier, SpecifierSet from packaging.version import Version +from setuptools._path import StrPath + from . import ( _entry_points, _reqs, command as _, # noqa: F401 # imported for side-effects ) from ._importlib import metadata +from ._reqs import _StrOrIter from .config import pyprojecttoml, setupcfg from .discovery import ConfigDiscovery from .monkey import get_unpatched @@ -195,10 +199,8 @@ 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: TypeAlias = distutils.core.Distribution + from distutils.core import Distribution as _Distribution else: _Distribution = get_unpatched(distutils.core.Distribution) @@ -262,7 +264,8 @@ class Distribution(_Distribution): # Used by build_py, editable_wheel and install_lib commands for legacy namespaces namespace_packages: list[str] #: :meta private: DEPRECATED - def __init__(self, attrs: MutableMapping | None = None) -> None: + # Any: Dynamic assignment results in Incompatible types in assignment + def __init__(self, attrs: MutableMapping[str, Any] | None = None) -> None: have_package_data = hasattr(self, "package_data") if not have_package_data: self.package_data: dict[str, list[str]] = {} @@ -271,9 +274,9 @@ def __init__(self, attrs: MutableMapping | None = None) -> None: self.include_package_data: bool | None = None self.exclude_package_data: dict[str, list[str]] | None = None # Filter-out setuptools' specific options. - self.src_root = attrs.pop("src_root", None) - self.dependency_links = attrs.pop('dependency_links', []) - self.setup_requires = attrs.pop('setup_requires', []) + self.src_root: str | None = attrs.pop("src_root", None) + self.dependency_links: list[str] = attrs.pop('dependency_links', []) + self.setup_requires: list[str] = attrs.pop('setup_requires', []) for ep in metadata.entry_points(group='distutils.setup_keywords'): vars(self).setdefault(ep.name, None) @@ -475,7 +478,7 @@ def _parse_config_files(self, filenames=None): # noqa: C901 except ValueError as e: raise DistutilsOptionError(e) from e - def warn_dash_deprecation(self, opt, section): + def warn_dash_deprecation(self, opt: str, section: str): if section in ( 'options.extras_require', 'options.data_files', @@ -517,7 +520,7 @@ def _setuptools_commands(self): # during bootstrapping, distribution doesn't exist return [] - def make_option_lowercase(self, opt, section): + def make_option_lowercase(self, opt: str, section: str): if section != 'metadata' or opt.islower(): return opt @@ -581,7 +584,7 @@ def _set_command_options(self, command_obj, option_dict=None): # noqa: C901 except ValueError as e: raise DistutilsOptionError(e) from e - def _get_project_config_files(self, filenames): + def _get_project_config_files(self, filenames: Iterable[StrPath] | None): """Add default file and split between INI and TOML""" tomlfiles = [] standard_project_metadata = Path(self.src_root or os.curdir, "pyproject.toml") @@ -593,7 +596,11 @@ def _get_project_config_files(self, filenames): tomlfiles = [standard_project_metadata] return filenames, tomlfiles - def parse_config_files(self, filenames=None, ignore_option_errors=False): + def parse_config_files( + self, + filenames: Iterable[StrPath] | None = None, + ignore_option_errors: bool = False, + ): """Parses configuration files from various levels and loads configuration. """ @@ -610,7 +617,7 @@ def parse_config_files(self, filenames=None, ignore_option_errors=False): self._finalize_requires() self._finalize_license_files() - def fetch_build_eggs(self, requires): + def fetch_build_eggs(self, requires: _StrOrIter): """Resolve pre-setup requirements""" from .installer import _fetch_build_eggs @@ -681,7 +688,7 @@ def fetch_build_egg(self, req): return fetch_build_egg(self, req) - def get_command_class(self, command): + def get_command_class(self, command: str): """Pluggable version of get_command_class()""" if command in self.cmdclass: return self.cmdclass[command] @@ -735,7 +742,7 @@ def include(self, **attrs): else: self._include_misc(k, v) - def exclude_package(self, package): + def exclude_package(self, package: str): """Remove packages, modules, and extensions in named package""" pfx = package + '.' @@ -756,7 +763,7 @@ def exclude_package(self, package): if p.name != package and not p.name.startswith(pfx) ] - def has_contents_for(self, package): + def has_contents_for(self, package: str): """Return true if 'exclude_package(package)' would do something""" pfx = package + '.' diff --git a/setuptools/extension.py b/setuptools/extension.py index dcc7709982..a381461765 100644 --- a/setuptools/extension.py +++ b/setuptools/extension.py @@ -4,6 +4,8 @@ import re from typing import TYPE_CHECKING +from setuptools._path import StrPath + from .monkey import get_unpatched import distutils.core @@ -27,10 +29,8 @@ 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: TypeAlias = distutils.core.Extension + from distutils.core import Extension as _Extension else: _Extension = get_unpatched(distutils.core.Extension) @@ -52,7 +52,7 @@ class Extension(_Extension): the full name of the extension, including any packages -- ie. *not* a filename or pathname, but Python dotted name - :arg list[str] sources: + :arg list[str|os.PathLike[str]] sources: list of source filenames, relative to the distribution root (where the setup script lives), in Unix form (slash-separated) for portability. Source files may be C, C++, SWIG (.i), @@ -140,11 +140,23 @@ class Extension(_Extension): _needs_stub: bool #: Private API, internal use only. _file_name: str #: Private API, internal use only. - def __init__(self, name: str, sources, *args, py_limited_api: bool = False, **kw): + def __init__( + self, + name: str, + sources: list[StrPath], + *args, + py_limited_api: bool = False, + **kw, + ): # The *args is needed for compatibility as calls may use positional # arguments. py_limited_api may be set only via keyword. self.py_limited_api = py_limited_api - super().__init__(name, sources, *args, **kw) + super().__init__( + name, + sources, # type: ignore[arg-type] # Vendored version of setuptools supports PathLike + *args, + **kw, + ) def _convert_pyx_sources_to_lang(self): """ diff --git a/setuptools/glob.py b/setuptools/glob.py index ffe0ae92cb..97aca44314 100644 --- a/setuptools/glob.py +++ b/setuptools/glob.py @@ -13,7 +13,7 @@ __all__ = ["glob", "iglob", "escape"] -def glob(pathname, recursive=False): +def glob(pathname, recursive: bool = False): """Return a list of paths matching a pathname pattern. The pattern may contain simple shell-style wildcards a la @@ -27,7 +27,7 @@ def glob(pathname, recursive=False): return list(iglob(pathname, recursive=recursive)) -def iglob(pathname, recursive=False): +def iglob(pathname, recursive: bool = False): """Return an iterator which yields the paths matching a pathname pattern. The pattern may contain simple shell-style wildcards a la diff --git a/setuptools/installer.py b/setuptools/installer.py index ce3559cd93..ba2d808d49 100644 --- a/setuptools/installer.py +++ b/setuptools/installer.py @@ -6,6 +6,7 @@ from functools import partial from . import _reqs +from ._reqs import _StrOrIter from .warnings import SetuptoolsDeprecationWarning from .wheel import Wheel @@ -30,7 +31,7 @@ def fetch_build_egg(dist, req): return _fetch_build_egg_no_warn(dist, req) -def _fetch_build_eggs(dist, requires): +def _fetch_build_eggs(dist, requires: _StrOrIter): import pkg_resources # Delay import to avoid unnecessary side-effects _DeprecatedInstaller.emit(stacklevel=3) diff --git a/setuptools/logging.py b/setuptools/logging.py index e9674c5a81..c6d25a6b1e 100644 --- a/setuptools/logging.py +++ b/setuptools/logging.py @@ -35,6 +35,6 @@ def configure(): distutils.dist.log = distutils.log -def set_threshold(level): +def set_threshold(level: int): logging.root.setLevel(level * 10) return set_threshold.unpatched(level) diff --git a/setuptools/msvc.py b/setuptools/msvc.py index 57f09417ca..0df89540cd 100644 --- a/setuptools/msvc.py +++ b/setuptools/msvc.py @@ -213,7 +213,7 @@ def _msvc14_get_vc_env(plat_spec): return env -def msvc14_get_vc_env(plat_spec): +def msvc14_get_vc_env(plat_spec: str): """ Patched "distutils._msvccompiler._get_vc_env" for support extra Microsoft Visual C++ 14.X compilers. @@ -327,7 +327,7 @@ def current_is_x86(self): """ return self.current_cpu == 'x86' - def current_dir(self, hidex86=False, x64=False): + def current_dir(self, hidex86: bool = False, x64: bool = False): """ Current platform specific subfolder. @@ -351,7 +351,7 @@ def current_dir(self, hidex86=False, x64=False): else r'\%s' % self.current_cpu ) - def target_dir(self, hidex86=False, x64=False): + def target_dir(self, hidex86: bool = False, x64: bool = False): r""" Target platform specific subfolder. @@ -375,7 +375,7 @@ def target_dir(self, hidex86=False, x64=False): else r'\%s' % self.target_cpu ) - def cross_dir(self, forcex86=False): + def cross_dir(self, forcex86: bool = False): r""" Cross platform specific subfolder. @@ -527,7 +527,7 @@ def windows_kits_roots(self): """ return r'Windows Kits\Installed Roots' - def microsoft(self, key, x86=False): + def microsoft(self, key, x86: bool = False): """ Return key in Microsoft software registry. @@ -1142,7 +1142,7 @@ class EnvironmentInfo: # Variables and properties in this class use originals CamelCase variables # names from Microsoft source files for more easy comparison. - def __init__(self, arch, vc_ver=None, vc_min_ver=0): + def __init__(self, arch, vc_ver=None, vc_min_ver: float = 0): self.pi = PlatformInfo(arch) self.ri = RegistryInfo(self.pi) self.si = SystemInfo(self.ri, vc_ver) @@ -1655,7 +1655,7 @@ def VCRuntimeRedist(self): return path return None - def return_env(self, exists=True): + def return_env(self, exists: bool = True): """ Return environment dict. diff --git a/setuptools/package_index.py b/setuptools/package_index.py index 9e01d5e082..80170ed6c0 100644 --- a/setuptools/package_index.py +++ b/setuptools/package_index.py @@ -303,20 +303,20 @@ class PackageIndex(Environment): def __init__( self, - index_url="https://pypi.org/simple/", + index_url: str = "https://pypi.org/simple/", hosts=('*',), ca_bundle=None, - verify_ssl=True, + verify_ssl: bool = True, *args, **kw, ): super().__init__(*args, **kw) self.index_url = index_url + "/"[: not index_url.endswith('/')] - self.scanned_urls = {} - self.fetched_urls = {} - self.package_pages = {} + self.scanned_urls: dict = {} + self.fetched_urls: dict = {} + self.package_pages: dict = {} self.allows = re.compile('|'.join(map(translate, hosts))).match - self.to_scan = [] + self.to_scan: list = [] self.opener = urllib.request.urlopen def add(self, dist): @@ -328,7 +328,7 @@ def add(self, dist): return super().add(dist) # FIXME: 'PackageIndex.process_url' is too complex (14) - def process_url(self, url, retrieve=False): # noqa: C901 + def process_url(self, url, retrieve: bool = False): # noqa: C901 """Evaluate a URL as a possible download, and maybe retrieve it""" if url in self.scanned_urls and not retrieve: return @@ -381,7 +381,7 @@ def process_url(self, url, retrieve=False): # noqa: C901 if url.startswith(self.index_url) and getattr(f, 'code', None) != 404: page = self.process_index(url, page) - def process_filename(self, fn, nested=False): + def process_filename(self, fn, nested: bool = False): # process filenames or directories if not os.path.exists(fn): self.warn("Not found: %s", fn) @@ -397,7 +397,7 @@ def process_filename(self, fn, nested=False): self.debug("Found: %s", fn) list(map(self.add, dists)) - def url_ok(self, url, fatal=False): + def url_ok(self, url, fatal: bool = False): s = URL_SCHEME(url) is_file = s and s.group(1).lower() == 'file' if is_file or self.allows(urllib.parse.urlparse(url)[1]): @@ -606,9 +606,9 @@ def fetch_distribution( # noqa: C901 # is too complex (14) # FIXME self, requirement, tmpdir, - force_scan=False, - source=False, - develop_ok=False, + force_scan: bool = False, + source: bool = False, + develop_ok: bool = False, local_index=None, ): """Obtain a distribution suitable for fulfilling `requirement` @@ -684,7 +684,9 @@ def find(req, env=None): self.info("Best match: %s", dist) return dist.clone(location=dist.download_location) - def fetch(self, requirement, tmpdir, force_scan=False, source=False): + def fetch( + self, requirement, tmpdir, force_scan: bool = False, source: bool = False + ): """Obtain a file suitable for fulfilling `requirement` DEPRECATED; use the ``fetch_distribution()`` method now instead. For diff --git a/setuptools/sandbox.py b/setuptools/sandbox.py index 9c2c78a32c..ec5e79da05 100644 --- a/setuptools/sandbox.py +++ b/setuptools/sandbox.py @@ -295,10 +295,10 @@ def __enter__(self) -> None: def __exit__( self, - exc_type: object, - exc_value: object, - traceback: object, - ) -> None: + exc_type: type[BaseException] | None, + exc_value: BaseException | None, + traceback: TracebackType | None, + ): self._active = False builtins.open = _open self._copy(_os) @@ -416,7 +416,7 @@ def _remap_pair(self, operation, src, dst, *args, **kw): class DirectorySandbox(AbstractSandbox): """Restrict operations to a single subdirectory - pseudo-chroot""" - write_ops = dict.fromkeys([ + write_ops: dict[str, None] = dict.fromkeys([ "open", "chmod", "chown", @@ -491,7 +491,7 @@ def _remap_pair(self, operation, src, dst, *args, **kw): self._violation(operation, src, dst, *args, **kw) return (src, dst) - def open(self, file, flags, mode=0o777, *args, **kw): + def open(self, file, flags, mode: int = 0o777, *args, **kw): """Called for low-level os.open()""" if flags & WRITE_FLAGS and not self._ok(file): self._violation("os.open", file, flags, mode, *args, **kw) diff --git a/setuptools/tests/test_build_ext.py b/setuptools/tests/test_build_ext.py index dab8b41cc9..f3e4ccd364 100644 --- a/setuptools/tests/test_build_ext.py +++ b/setuptools/tests/test_build_ext.py @@ -183,7 +183,7 @@ def get_build_ext_cmd(self, optional: bool, **opts): "eggs.c": "#include missingheader.h\n", ".build": {"lib": {}, "tmp": {}}, } - path.build(files) # type: ignore[arg-type] # jaraco/path#232 + path.build(files) # jaraco/path#232 extension = Extension('spam.eggs', ['eggs.c'], optional=optional) dist = Distribution(dict(ext_modules=[extension])) dist.script_name = 'setup.py' diff --git a/setuptools/warnings.py b/setuptools/warnings.py index 8c94bc96e6..f0ef616725 100644 --- a/setuptools/warnings.py +++ b/setuptools/warnings.py @@ -12,9 +12,12 @@ from datetime import date from inspect import cleandoc from textwrap import indent -from typing import Tuple +from typing import TYPE_CHECKING, Tuple -_DueDate = Tuple[int, int, int] # time tuple +if TYPE_CHECKING: + from typing_extensions import TypeAlias + +_DueDate: TypeAlias = Tuple[int, int, int] # time tuple _INDENT = 8 * " " _TEMPLATE = f"""{80 * '*'}\n{{details}}\n{80 * '*'}"""