From 235308cc7c868c041d1310af38a47e6e7de93076 Mon Sep 17 00:00:00 2001 From: Avasam Date: Sun, 4 May 2025 11:48:10 -0400 Subject: [PATCH 1/2] Re-organize mypy errors --- mypy.ini | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/mypy.ini b/mypy.ini index 146222a5..5244a0e9 100644 --- a/mypy.ini +++ b/mypy.ini @@ -16,20 +16,22 @@ disable_error_code = # local + # Code that is too dynamic using variable command names; + # and code that uses platform checks mypy doesn't understand + attr-defined, + # These reveal issues in distutils/_modified.py that should be fixed + return-value, + type-var, # TODO: Resolve and re-enable these gradually operator, - attr-defined, arg-type, assignment, call-overload, - return-value, index, - type-var, func-returns-value, union-attr, str-bytes-safe, misc, - has-type, # stdlib's test module is not typed on typeshed [mypy-test.*] From d4fdab6377f46a85b6266ca9842663715dceec75 Mon Sep 17 00:00:00 2001 From: Avasam Date: Sun, 4 May 2025 12:49:36 -0400 Subject: [PATCH 2/2] mypy: fix all assignment --- distutils/archive_util.py | 34 +++++++++++++++++++++------------- distutils/cmd.py | 2 +- distutils/command/build_ext.py | 2 +- distutils/command/install.py | 4 ++-- distutils/dist.py | 3 ++- distutils/extension.py | 3 +-- distutils/fancy_getopt.py | 15 ++++++++------- distutils/sysconfig.py | 12 ++++++++++-- distutils/tests/unix_compat.py | 10 ++++++++-- mypy.ini | 1 - 10 files changed, 54 insertions(+), 32 deletions(-) diff --git a/distutils/archive_util.py b/distutils/archive_util.py index d860f552..2a3c9e04 100644 --- a/distutils/archive_util.py +++ b/distutils/archive_util.py @@ -6,28 +6,36 @@ from __future__ import annotations import os -from typing import Literal, overload - -try: - import zipfile -except ImportError: - zipfile = None - +import sys +from types import ModuleType +from typing import TYPE_CHECKING, Literal, overload from ._log import log from .dir_util import mkpath from .errors import DistutilsExecError from .spawn import spawn +zipfile: ModuleType | None = None try: - from pwd import getpwnam + import zipfile except ImportError: - getpwnam = None + pass -try: +# At runtime we have to be more flexible than simply checking for `sys.platform` +# https://github.com/python/mypy/issues/1393 +if TYPE_CHECKING and sys.platform != "win32": from grp import getgrnam -except ImportError: - getgrnam = None + from pwd import getpwnam +else: + try: + from pwd import getpwnam + except ImportError: + getpwnam = None + + try: + from grp import getgrnam + except ImportError: + getgrnam = None def _get_gid(name): @@ -270,7 +278,7 @@ def make_archive( if base_dir is None: base_dir = os.curdir - kwargs = {'dry_run': dry_run} + kwargs: dict[str, str | bool | None] = {'dry_run': dry_run} try: format_info = ARCHIVE_FORMATS[format] diff --git a/distutils/cmd.py b/distutils/cmd.py index 241621bd..8593d6fd 100644 --- a/distutils/cmd.py +++ b/distutils/cmd.py @@ -107,7 +107,7 @@ def __init__(self, dist: Distribution) -> None: # timestamps, but methods defined *here* assume that # 'self.force' exists for all commands. So define it here # just to be safe. - self.force = None + self.force: bool | None = None # The 'help' flag is just used for command-line parsing, so # none of that complicated bureaucracy is needed. diff --git a/distutils/command/build_ext.py b/distutils/command/build_ext.py index ec45b440..1d03d7cf 100644 --- a/distutils/command/build_ext.py +++ b/distutils/command/build_ext.py @@ -433,7 +433,7 @@ def check_extensions_list(self, extensions) -> None: # noqa: C901 setattr(ext, key, val) # Medium-easy stuff: same syntax/semantics, different names. - ext.runtime_library_dirs = build_info.get('rpath') + ext.runtime_library_dirs = build_info.get('rpath') or [] if 'def_file' in build_info: log.warning("'def_file' element of build info dict no longer supported") diff --git a/distutils/command/install.py b/distutils/command/install.py index 8421d54e..7b4c553c 100644 --- a/distutils/command/install.py +++ b/distutils/command/install.py @@ -256,8 +256,8 @@ def initialize_options(self) -> None: # These select only the installation base; it's up to the user to # specify the installation scheme (currently, that means supplying # the --install-{platlib,purelib,scripts,data} options). - self.install_base = None - self.install_platbase = None + self.install_base: str | None = None + self.install_platbase: str | None = None self.root: str | None = None # These options are the actual installation directories; if not diff --git a/distutils/dist.py b/distutils/dist.py index b9552a8b..777c9f2d 100644 --- a/distutils/dist.py +++ b/distutils/dist.py @@ -23,6 +23,7 @@ Literal, TypeVar, Union, + cast, overload, ) @@ -849,7 +850,7 @@ def get_command_class(self, command: str) -> type[Command]: continue try: - klass = getattr(module, klass_name) + klass = cast("type[Command]", getattr(module, klass_name)) except AttributeError: raise DistutilsModuleError( f"invalid command '{command}' (no class '{klass_name}' in module '{module_name}')" diff --git a/distutils/extension.py b/distutils/extension.py index f5141126..d61ce77e 100644 --- a/distutils/extension.py +++ b/distutils/extension.py @@ -144,8 +144,7 @@ def __init__( # If there are unknown keyword options, warn about them if len(kw) > 0: - options = [repr(option) for option in kw] - options = ', '.join(sorted(options)) + options = ', '.join(sorted([repr(option) for option in kw])) msg = f"Unknown Extension options: {options}" warnings.warn(msg) diff --git a/distutils/fancy_getopt.py b/distutils/fancy_getopt.py index 7d079118..e9407fee 100644 --- a/distutils/fancy_getopt.py +++ b/distutils/fancy_getopt.py @@ -249,6 +249,7 @@ def getopt(self, args: Sequence[str] | None = None, object=None): # noqa: C901 raise DistutilsArgError(msg) for opt, val in opts: + value: int | str = val if len(opt) == 2 and opt[0] == '-': # it's a short option opt = self.short2long[opt[1]] else: @@ -260,21 +261,21 @@ def getopt(self, args: Sequence[str] | None = None, object=None): # noqa: C901 opt = alias if not self.takes_arg[opt]: # boolean option? - assert val == '', "boolean option can't have value" + assert value == '', "boolean option can't have value" alias = self.negative_alias.get(opt) if alias: opt = alias - val = 0 + value = 0 else: - val = 1 + value = 1 attr = self.attr_name[opt] # The only repeating option at the moment is 'verbose'. # It has a negative option -q quiet, which should set verbose = False. - if val and self.repeat.get(attr) is not None: - val = getattr(object, attr, 0) + 1 - setattr(object, attr, val) - self.option_order.append((opt, val)) + if value and self.repeat.get(attr) is not None: + value = getattr(object, attr, 0) + 1 + setattr(object, attr, value) + self.option_order.append((opt, value)) # for opts if created_object: diff --git a/distutils/sysconfig.py b/distutils/sysconfig.py index 7ddc869a..fe2d3270 100644 --- a/distutils/sysconfig.py +++ b/distutils/sysconfig.py @@ -324,6 +324,16 @@ def customize_compiler(compiler: CCompiler) -> None: 'AR', 'ARFLAGS', ) + assert isinstance(cc, str) + assert isinstance(cxx, str) + assert isinstance(cflags, str) + assert isinstance(ccshared, str) + assert isinstance(ldshared, str) + assert isinstance(ldcxxshared, str) + assert isinstance(shlib_suffix, str) + assert isinstance(ar_flags, str) + ar = os.environ.get('AR', ar) + assert isinstance(ar, str) cxxflags = cflags @@ -354,8 +364,6 @@ def customize_compiler(compiler: CCompiler) -> None: ldshared = _add_flags(ldshared, 'CPP') ldcxxshared = _add_flags(ldcxxshared, 'CPP') - ar = os.environ.get('AR', ar) - archiver = ar + ' ' + os.environ.get('ARFLAGS', ar_flags) cc_cmd = cc + ' ' + cflags cxx_cmd = cxx + ' ' + cxxflags diff --git a/distutils/tests/unix_compat.py b/distutils/tests/unix_compat.py index a5d9ee45..af020ee6 100644 --- a/distutils/tests/unix_compat.py +++ b/distutils/tests/unix_compat.py @@ -1,12 +1,18 @@ +from __future__ import annotations + import sys +from types import ModuleType + +import pytest +grp: ModuleType | None = None +pwd: ModuleType | None = None try: import grp import pwd except ImportError: - grp = pwd = None + pass -import pytest UNIX_ID_SUPPORT = grp and pwd UID_0_SUPPORT = UNIX_ID_SUPPORT and sys.platform != "cygwin" diff --git a/mypy.ini b/mypy.ini index 5244a0e9..f0fe54e4 100644 --- a/mypy.ini +++ b/mypy.ini @@ -25,7 +25,6 @@ disable_error_code = # TODO: Resolve and re-enable these gradually operator, arg-type, - assignment, call-overload, index, func-returns-value,