Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 21 additions & 13 deletions distutils/archive_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down Expand Up @@ -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]
Expand Down
2 changes: 1 addition & 1 deletion distutils/cmd.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
2 changes: 1 addition & 1 deletion distutils/command/build_ext.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 []
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Runtime change: avoid assigning None to runtime_library_dirs (I didn't fully investigate whether that should even be possible at runtime or just an artefact of code that is too dynamic to be type-safe)

if 'def_file' in build_info:
log.warning("'def_file' element of build info dict no longer supported")

Expand Down
4 changes: 2 additions & 2 deletions distutils/command/install.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 2 additions & 1 deletion distutils/dist.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
Literal,
TypeVar,
Union,
cast,
overload,
)

Expand Down Expand Up @@ -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}')"
Expand Down
3 changes: 1 addition & 2 deletions distutils/extension.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
15 changes: 8 additions & 7 deletions distutils/fancy_getopt.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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))
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


# for opts
if created_object:
Expand Down
12 changes: 10 additions & 2 deletions distutils/sysconfig.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Comment on lines +327 to +336
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#366 (comment)

I'm not sure these changes are stable. I'm worried there are edge cases that would begin to fail with this change. Let's be confident that this change doesn't break anything (and consider using casts to retain the current behavior if appropriate).

#343 (comment)

I'm not sure these assertions will always pass. There have been some bugs around these being undefined in unusual environments. Maybe it makes sense to catch these conditions early. Do we have good reason to believe that non-string values returned here would never be viable (would be breaking anyway)?


cxxflags = cflags

Expand Down Expand Up @@ -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
Expand Down
10 changes: 8 additions & 2 deletions distutils/tests/unix_compat.py
Original file line number Diff line number Diff line change
@@ -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"
Expand Down
11 changes: 6 additions & 5 deletions mypy.ini
Original file line number Diff line number Diff line change
Expand Up @@ -16,20 +16,21 @@ 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.*]
Expand Down
Loading