Skip to content

Commit 226e1a2

Browse files
Avasamabravalheri
andauthored
Fix mypy issues (#3979)
* Fix all mypy issues * Ran black * Exclude tox from mypy check * Fix all mypy issues again * Address PR comments * Fix accidental line ending changes * Update .gitignore * No unused type: ignore * TypeError: 'ABCMeta' object is not subscriptable * Fix RuffError * Fix post-merge mypy issues * RUff format * Ignore more generated files * Disable more mypy errors * Globally ignore attr-defined for now * Update more comments * Address PR comments and fix new exposed typing issues * Comments updates and don't touch vendored * Accidentally removed noqa * Update setuptools/tests/integration/test_pip_install_sdist.py Co-authored-by: Anderson Bravalheri <andersonbravalheri+github@gmail.com> * Post merge comments Update setuptools/tests/integration/test_pip_install_sdist.py Co-authored-by: Anderson Bravalheri <andersonbravalheri+github@gmail.com> * Document that usage of _config_vars is very purposeful Closes #4228 + try to resolve doc issue * sort nitpick_ignore * Make only comment on newline like others * Forgot to re-ignore --------- Co-authored-by: Anderson Bravalheri <andersonbravalheri+github@gmail.com>
1 parent dbc6471 commit 226e1a2

28 files changed

+164
-69
lines changed

docs/conf.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -161,22 +161,23 @@
161161
# Ref: https://stackoverflow.com/a/30624034/595220
162162
nitpick_ignore = [
163163
('c:func', 'SHGetSpecialFolderPath'), # ref to MS docs
164+
('envvar', 'DIST_EXTRA_CONFIG'), # undocumented
164165
('envvar', 'DISTUTILS_DEBUG'), # undocumented
165166
('envvar', 'HOME'), # undocumented
166167
('envvar', 'PLAT'), # undocumented
167-
('envvar', 'DIST_EXTRA_CONFIG'), # undocumented
168168
('py:attr', 'CCompiler.language_map'), # undocumented
169169
('py:attr', 'CCompiler.language_order'), # undocumented
170-
('py:class', 'distutils.dist.Distribution'), # undocumented
171-
('py:class', 'distutils.extension.Extension'), # undocumented
172170
('py:class', 'BorlandCCompiler'), # undocumented
173171
('py:class', 'CCompiler'), # undocumented
174172
('py:class', 'CygwinCCompiler'), # undocumented
173+
('py:class', 'distutils.dist.Distribution'), # undocumented
175174
('py:class', 'distutils.dist.DistributionMetadata'), # undocumented
175+
('py:class', 'distutils.extension.Extension'), # undocumented
176176
('py:class', 'FileList'), # undocumented
177177
('py:class', 'IShellLink'), # ref to MS docs
178178
('py:class', 'MSVCCompiler'), # undocumented
179179
('py:class', 'OptionDummy'), # undocumented
180+
('py:class', 'setuptools.dist.Distribution'), # undocumented
180181
('py:class', 'UnixCCompiler'), # undocumented
181182
('py:exc', 'CompileError'), # undocumented
182183
('py:exc', 'DistutilsExecError'), # undocumented
@@ -186,8 +187,7 @@
186187
('py:exc', 'PreprocessError'), # undocumented
187188
('py:exc', 'setuptools.errors.PlatformError'), # sphinx cannot find it
188189
('py:func', 'distutils.CCompiler.new_compiler'), # undocumented
189-
# undocumented:
190-
('py:func', 'distutils.dist.DistributionMetadata.read_pkg_file'),
190+
('py:func', 'distutils.dist.DistributionMetadata.read_pkg_file'), # undocumented
191191
('py:func', 'distutils.file_util._copy_file_contents'), # undocumented
192192
('py:func', 'distutils.log.debug'), # undocumented
193193
('py:func', 'distutils.spawn.find_executable'), # undocumented

mypy.ini

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,42 @@
11
[mypy]
2-
ignore_missing_imports = True
3-
# required to support namespace packages
4-
# https://github.com/python/mypy/issues/14057
2+
# CI should test for all versions, local development gets hints for oldest supported
3+
python_version = 3.8
4+
strict = False
5+
warn_unused_ignores = True
6+
# required to support namespace packages: https://github.com/python/mypy/issues/14057
57
explicit_package_bases = True
8+
exclude = (?x)(
9+
^build/
10+
| ^.tox/
11+
| ^pkg_resources/tests/data/my-test-package-source/setup.py$ # Duplicate module name
12+
| ^.+?/(_vendor|extern)/ # Vendored
13+
| ^setuptools/_distutils/ # Vendored
14+
| ^setuptools/config/_validate_pyproject/ # Auto-generated
15+
)
16+
disable_error_code =
17+
# TODO: Test environment is not yet properly configured to install all imported packages
18+
# import-not-found, # This can be left commented out for local runs until we enforce running mypy in the CI
19+
# TODO: Not all dependencies are typed. Namely: distutils._modified, wheel.wheelfile, and jaraco.*
20+
import-untyped,
21+
# Ignoring attr-defined because setuptools wraps a lot of distutils classes, adding new attributes,
22+
# w/o updating all the attributes and return types from the base classes for type-checkers to understand
23+
# Especially with setuptools.dist.command vs distutils.dist.command vs setuptools._distutils.dist.command
24+
# *.extern modules that actually live in *._vendor will also cause attr-defined issues on import
25+
attr-defined,
26+
27+
# Avoid raising issues when importing from "extern" modules, as those are added to path dynamically.
28+
# https://github.com/pypa/setuptools/pull/3979#discussion_r1367968993
29+
[mypy-pkg_resources.extern.*,setuptools.extern.*]
30+
ignore_missing_imports = True
31+
32+
[mypy-pkg_resources.tests.*,setuptools.tests.*]
33+
disable_error_code =
34+
# Tests include creating dynamic modules that won't exists statically before the test is run.
35+
# Let's ignore all "import-not-found", as if an import really wasn't found, then the test would fail.
36+
import-not-found,
37+
# mmany untyped "jaraco" modules
38+
import-untyped,
39+
40+
# Mypy issue, this vendored module is already excluded!
41+
[mypy-setuptools._vendor.packaging._manylinux]
42+
disable_error_code = import-not-found

pkg_resources/tests/test_pkg_resources.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import stat
1010
import distutils.dist
1111
import distutils.command.install_egg_info
12+
from typing import List
1213

1314
from unittest import mock
1415

@@ -32,7 +33,7 @@ def __call__(self):
3233

3334

3435
class TestZipProvider:
35-
finalizers = []
36+
finalizers: List[EggRemover] = []
3637

3738
ref_time = datetime.datetime(2013, 5, 12, 13, 25, 0)
3839
"A reference time for a file modification"

setup.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,5 +88,6 @@ def _restore_install_lib(self):
8888

8989
if __name__ == '__main__':
9090
# allow setup.py to run from another directory
91-
here and os.chdir(here)
91+
# TODO: Use a proper conditonal statement here
92+
here and os.chdir(here) # type: ignore[func-returns-value]
9293
dist = setuptools.setup(**setup_params)

setuptools/__init__.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import functools
44
import os
55
import re
6+
from typing import TYPE_CHECKING
67

78
import _distutils_hack.override # noqa: F401
89
import distutils.core
@@ -105,8 +106,11 @@ def setup(**attrs):
105106

106107
setup.__doc__ = distutils.core.setup.__doc__
107108

108-
109-
_Command = monkey.get_unpatched(distutils.core.Command)
109+
if TYPE_CHECKING:
110+
# Work around a mypy issue where type[T] can't be used as a base: https://github.com/python/mypy/issues/10962
111+
_Command = distutils.core.Command
112+
else:
113+
_Command = monkey.get_unpatched(distutils.core.Command)
110114

111115

112116
class Command(_Command):
@@ -165,8 +169,9 @@ class Command(_Command):
165169
"""
166170

167171
command_consumes_arguments = False
172+
distribution: Distribution # override distutils.dist.Distribution with setuptools.dist.Distribution
168173

169-
def __init__(self, dist, **kw):
174+
def __init__(self, dist: Distribution, **kw):
170175
"""
171176
Construct the command for dist, updating
172177
vars(self) with any keyword parameters.

setuptools/command/_requirestxt.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ def _prepare(
3535

3636

3737
def _convert_extras_requirements(
38-
extras_require: _StrOrIter,
38+
extras_require: Mapping[str, _StrOrIter],
3939
) -> Mapping[str, _Ordered[Requirement]]:
4040
"""
4141
Convert requirements in `extras_require` of the form

setuptools/command/build_ext.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,9 @@
2626

2727
# make sure _config_vars is initialized
2828
get_config_var("LDSHARED")
29-
from distutils.sysconfig import _config_vars as _CONFIG_VARS # noqa
29+
# Not publicly exposed in typeshed distutils stubs, but this is done on purpose
30+
# See https://github.com/pypa/setuptools/pull/4228#issuecomment-1959856400
31+
from distutils.sysconfig import _config_vars as _CONFIG_VARS # type: ignore # noqa
3032

3133

3234
def _customize_compiler_for_shlib(compiler):
@@ -58,7 +60,7 @@ def _customize_compiler_for_shlib(compiler):
5860
use_stubs = True
5961
elif os.name != 'nt':
6062
try:
61-
import dl
63+
import dl # type: ignore[import-not-found] # https://github.com/python/mypy/issues/13002
6264

6365
use_stubs = have_rtld = hasattr(dl, 'RTLD_NOW')
6466
except ImportError:
@@ -378,7 +380,10 @@ def _compile_and_remove_stub(self, stub_file: str):
378380
optimize = self.get_finalized_command('install_lib').optimize
379381
if optimize > 0:
380382
byte_compile(
381-
[stub_file], optimize=optimize, force=True, dry_run=self.dry_run
383+
[stub_file],
384+
optimize=optimize,
385+
force=True,
386+
dry_run=self.dry_run,
382387
)
383388
if os.path.exists(stub_file) and not self.dry_run:
384389
os.unlink(stub_file)

setuptools/command/dist_info.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,10 @@
99
from distutils import log
1010
from distutils.core import Command
1111
from pathlib import Path
12+
from typing import cast
1213

1314
from .. import _normalization
15+
from .egg_info import egg_info as egg_info_cls
1416

1517

1618
class dist_info(Command):
@@ -50,7 +52,7 @@ def finalize_options(self):
5052
project_dir = dist.src_root or os.curdir
5153
self.output_dir = Path(self.output_dir or project_dir)
5254

53-
egg_info = self.reinitialize_command("egg_info")
55+
egg_info = cast(egg_info_cls, self.reinitialize_command("egg_info"))
5456
egg_info.egg_base = str(self.output_dir)
5557

5658
if self.tag_date:

setuptools/command/easy_install.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
from distutils.command import install
2626
import sys
2727
import os
28+
from typing import Dict, List
2829
import zipimport
2930
import shutil
3031
import tempfile
@@ -43,7 +44,6 @@
4344
import configparser
4445
import sysconfig
4546

46-
4747
from sysconfig import get_path
4848

4949
from setuptools import Command
@@ -1765,7 +1765,7 @@ def _wrap_lines(cls, lines):
17651765

17661766

17671767
if os.environ.get('SETUPTOOLS_SYS_PATH_TECHNIQUE', 'raw') == 'rewrite':
1768-
PthDistributions = RewritePthDistributions
1768+
PthDistributions = RewritePthDistributions # type: ignore[misc] # Overwriting type
17691769

17701770

17711771
def _first_line_re():
@@ -2015,7 +2015,7 @@ def is_python_script(script_text, filename):
20152015
from os import chmod as _chmod
20162016
except ImportError:
20172017
# Jython compatibility
2018-
def _chmod(*args):
2018+
def _chmod(*args: object, **kwargs: object) -> None: # type: ignore[misc] # Mypy re-uses the imported definition anyway
20192019
pass
20202020

20212021

@@ -2033,8 +2033,8 @@ class CommandSpec(list):
20332033
those passed to Popen.
20342034
"""
20352035

2036-
options = []
2037-
split_args = dict()
2036+
options: List[str] = []
2037+
split_args: Dict[str, bool] = dict()
20382038

20392039
@classmethod
20402040
def best(cls):

setuptools/command/editable_wheel.py

Lines changed: 30 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
Protocol,
3434
Tuple,
3535
TypeVar,
36+
cast,
3637
)
3738

3839
from .. import (
@@ -50,7 +51,12 @@
5051
SetuptoolsDeprecationWarning,
5152
SetuptoolsWarning,
5253
)
54+
from .build import build as build_cls
5355
from .build_py import build_py as build_py_cls
56+
from .dist_info import dist_info as dist_info_cls
57+
from .egg_info import egg_info as egg_info_cls
58+
from .install import install as install_cls
59+
from .install_scripts import install_scripts as install_scripts_cls
5460

5561
if TYPE_CHECKING:
5662
from wheel.wheelfile import WheelFile # noqa
@@ -155,7 +161,7 @@ def run(self):
155161

156162
def _ensure_dist_info(self):
157163
if self.dist_info_dir is None:
158-
dist_info = self.reinitialize_command("dist_info")
164+
dist_info = cast(dist_info_cls, self.reinitialize_command("dist_info"))
159165
dist_info.output_dir = self.dist_dir
160166
dist_info.ensure_finalized()
161167
dist_info.run()
@@ -202,25 +208,33 @@ def _configure_build(
202208
scripts = str(Path(unpacked_wheel, f"{name}.data", "scripts"))
203209

204210
# egg-info may be generated again to create a manifest (used for package data)
205-
egg_info = dist.reinitialize_command("egg_info", reinit_subcommands=True)
211+
egg_info = cast(
212+
egg_info_cls, dist.reinitialize_command("egg_info", reinit_subcommands=True)
213+
)
206214
egg_info.egg_base = str(tmp_dir)
207215
egg_info.ignore_egg_info_in_manifest = True
208216

209-
build = dist.reinitialize_command("build", reinit_subcommands=True)
210-
install = dist.reinitialize_command("install", reinit_subcommands=True)
217+
build = cast(
218+
build_cls, dist.reinitialize_command("build", reinit_subcommands=True)
219+
)
220+
install = cast(
221+
install_cls, dist.reinitialize_command("install", reinit_subcommands=True)
222+
)
211223

212224
build.build_platlib = build.build_purelib = build.build_lib = build_lib
213225
install.install_purelib = install.install_platlib = install.install_lib = wheel
214226
install.install_scripts = build.build_scripts = scripts
215227
install.install_headers = headers
216228
install.install_data = data
217229

218-
install_scripts = dist.get_command_obj("install_scripts")
230+
install_scripts = cast(
231+
install_scripts_cls, dist.get_command_obj("install_scripts")
232+
)
219233
install_scripts.no_ep = True
220234

221235
build.build_temp = str(tmp_dir)
222236

223-
build_py = dist.get_command_obj("build_py")
237+
build_py = cast(build_py_cls, dist.get_command_obj("build_py"))
224238
build_py.compile = False
225239
build_py.existing_egg_info_dir = self._find_egg_info_dir()
226240

@@ -233,6 +247,7 @@ def _set_editable_mode(self):
233247
"""Set the ``editable_mode`` flag in the build sub-commands"""
234248
dist = self.distribution
235249
build = dist.get_command_obj("build")
250+
# TODO: Update typeshed distutils stubs to overload non-None return type by default
236251
for cmd_name in build.get_sub_commands():
237252
cmd = dist.get_command_obj(cmd_name)
238253
if hasattr(cmd, "editable_mode"):
@@ -269,7 +284,7 @@ def _run_build_commands(
269284
self._run_install("data")
270285
return files, mapping
271286

272-
def _run_build_subcommands(self):
287+
def _run_build_subcommands(self) -> None:
273288
"""
274289
Issue #3501 indicates that some plugins/customizations might rely on:
275290
@@ -283,7 +298,7 @@ def _run_build_subcommands(self):
283298
# TODO: Once plugins/customisations had the chance to catch up, replace
284299
# `self._run_build_subcommands()` with `self.run_command("build")`.
285300
# Also remove _safely_run, TestCustomBuildPy. Suggested date: Aug/2023.
286-
build: Command = self.get_finalized_command("build")
301+
build = self.get_finalized_command("build")
287302
for name in build.get_sub_commands():
288303
cmd = self.get_finalized_command(name)
289304
if name == "build_py" and type(cmd) != build_py_cls:
@@ -432,7 +447,8 @@ def __init__(
432447
):
433448
self.auxiliary_dir = Path(auxiliary_dir)
434449
self.build_lib = Path(build_lib).resolve()
435-
self._file = dist.get_command_obj("build_py").copy_file
450+
# TODO: Update typeshed distutils stubs to overload non-None return type by default
451+
self._file = dist.get_command_obj("build_py").copy_file # type: ignore[union-attr]
436452
super().__init__(dist, name, [self.auxiliary_dir])
437453

438454
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):
450466
dest = self.auxiliary_dir / relative_output
451467
if not dest.parent.is_dir():
452468
dest.parent.mkdir(parents=True)
453-
self._file(src_file, dest, link=link)
469+
# TODO: Update typeshed distutils stubs so distutils.cmd.Command.copy_file, accepts PathLike
470+
# same with methods used by copy_file
471+
self._file(src_file, dest, link=link) # type: ignore[arg-type]
454472

455473
def _create_links(self, outputs, output_mapping):
456474
self.auxiliary_dir.mkdir(parents=True, exist_ok=True)
@@ -603,7 +621,8 @@ def _simple_layout(
603621
layout = {pkg: find_package_path(pkg, package_dir, project_dir) for pkg in packages}
604622
if not layout:
605623
return set(package_dir) in ({}, {""})
606-
parent = os.path.commonpath(starmap(_parent_path, layout.items()))
624+
# TODO: has been fixed upstream, waiting for new mypy release https://github.com/python/typeshed/pull/11310
625+
parent = os.path.commonpath(starmap(_parent_path, layout.items())) # type: ignore[call-overload]
607626
return all(
608627
_path.same_path(Path(parent, *key.split('.')), value)
609628
for key, value in layout.items()

setuptools/command/install.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,11 @@
33
import glob
44
import platform
55
import distutils.command.install as orig
6+
from typing import cast
67

78
import setuptools
89
from ..warnings import SetuptoolsDeprecationWarning, SetuptoolsWarning
10+
from .bdist_egg import bdist_egg as bdist_egg_cls
911

1012
# Prior to numpy 1.9, NumPy relies on the '_install' name, so provide it for
1113
# now. See https://github.com/pypa/setuptools/issues/199/
@@ -135,7 +137,8 @@ def do_egg_install(self):
135137
cmd.package_index.scan(glob.glob('*.egg'))
136138

137139
self.run_command('bdist_egg')
138-
args = [self.distribution.get_command_obj('bdist_egg').egg_output]
140+
bdist_egg = cast(bdist_egg_cls, self.distribution.get_command_obj('bdist_egg'))
141+
args = [bdist_egg.egg_output]
139142

140143
if setuptools.bootstrap_install_from:
141144
# Bootstrap self-installation of setuptools

0 commit comments

Comments
 (0)