Skip to content

Commit 22f61d9

Browse files
committed
spawn: don't force param cmd to be mutable
1 parent 603b94e commit 22f61d9

File tree

5 files changed

+92
-17
lines changed

5 files changed

+92
-17
lines changed

distutils/cmd.py

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@
1111
import re
1212
import sys
1313
from abc import abstractmethod
14-
from collections.abc import Callable, MutableSequence
15-
from typing import TYPE_CHECKING, Any, ClassVar, TypeVar, overload
14+
from collections.abc import Callable, MutableSequence, Sequence
15+
from typing import TYPE_CHECKING, Any, ClassVar, Literal, TypeVar, overload
1616

1717
from . import _modified, archive_util, dir_util, file_util, util
1818
from ._log import log
@@ -467,6 +467,20 @@ def move_file(
467467
"""Move a file respecting dry-run flag."""
468468
return file_util.move_file(src, dst, dry_run=self.dry_run)
469469

470+
@overload
471+
def spawn(
472+
self,
473+
cmd: Sequence[bytes | os.PathLike[bytes] | str | os.PathLike[str]],
474+
search_path: Literal[False],
475+
level: int = 1,
476+
) -> None: ...
477+
@overload
478+
def spawn(
479+
self,
480+
cmd: Sequence[bytes | os.PathLike[bytes] | str | os.PathLike[str]],
481+
search_path: Literal[True] = True,
482+
level: int = 1,
483+
) -> None: ...
470484
def spawn(
471485
self, cmd: MutableSequence[str], search_path: bool = True, level: int = 1
472486
) -> None:

distutils/compilers/C/base.py

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
import re
1111
import sys
1212
import warnings
13-
from collections.abc import Callable, Iterable, MutableSequence, Sequence
13+
from collections.abc import Callable, Iterable, Sequence
1414
from typing import (
1515
TYPE_CHECKING,
1616
ClassVar,
@@ -39,6 +39,8 @@
3939
)
4040

4141
if TYPE_CHECKING:
42+
from subprocess import _ENV
43+
4244
from typing_extensions import TypeAlias, TypeVarTuple, Unpack
4345

4446
_Ts = TypeVarTuple("_Ts")
@@ -70,7 +72,7 @@ class Compiler:
7072
# dictionary (see below -- used by the 'new_compiler()' factory
7173
# function) -- authors of new compiler interface classes are
7274
# responsible for updating 'compiler_class'!
73-
compiler_type: ClassVar[str] = None # type: ignore[assignment]
75+
compiler_type: ClassVar[str] = None
7476

7577
# XXX things not handled by this compiler abstraction model:
7678
# * client can't provide additional options for a compiler,
@@ -1152,8 +1154,28 @@ def execute(
11521154
) -> None:
11531155
execute(func, args, msg, self.dry_run)
11541156

1157+
@overload
1158+
def spawn(
1159+
self,
1160+
cmd: Sequence[bytes | os.PathLike[bytes] | str | os.PathLike[str]],
1161+
*,
1162+
search_path: Literal[False],
1163+
verbose: bool = False,
1164+
env: _ENV | None = None,
1165+
) -> None: ...
1166+
@overload
1167+
def spawn(
1168+
self,
1169+
cmd: Sequence[bytes | str | os.PathLike[str]],
1170+
*,
1171+
search_path: Literal[True] = True,
1172+
verbose: bool = False,
1173+
env: _ENV | None = None,
1174+
) -> None: ...
11551175
def spawn(
1156-
self, cmd: MutableSequence[bytes | str | os.PathLike[str]], **kwargs
1176+
self,
1177+
cmd: Sequence[bytes | os.PathLike[bytes] | str | os.PathLike[str]],
1178+
**kwargs,
11571179
) -> None:
11581180
spawn(cmd, dry_run=self.dry_run, **kwargs)
11591181

distutils/compilers/C/msvc.py

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@
1818
import subprocess
1919
import unittest.mock as mock
2020
import warnings
21-
from collections.abc import Iterable
21+
from collections.abc import Iterable, Sequence
22+
from typing import Literal, overload
2223

2324
with contextlib.suppress(ImportError):
2425
import winreg
@@ -95,7 +96,8 @@ def _find_vc2017():
9596
subprocess.CalledProcessError, OSError, UnicodeDecodeError
9697
):
9798
path = (
98-
subprocess.check_output([
99+
subprocess
100+
.check_output([
99101
os.path.join(
100102
root, "Microsoft Visual Studio", "Installer", "vswhere.exe"
101103
),
@@ -557,14 +559,34 @@ def link(
557559
else:
558560
log.debug("skipping %s (up-to-date)", output_filename)
559561

560-
def spawn(self, cmd):
562+
@overload # type: ignore[override] # env param not available
563+
def spawn(
564+
self,
565+
cmd: Sequence[bytes | os.PathLike[bytes] | str | os.PathLike[str]],
566+
*,
567+
search_path: Literal[False],
568+
verbose: bool = False,
569+
) -> None: ...
570+
@overload
571+
def spawn(
572+
self,
573+
cmd: Sequence[bytes | str | os.PathLike[str]],
574+
*,
575+
search_path: Literal[True] = True,
576+
verbose: bool = False,
577+
) -> None: ...
578+
def spawn(
579+
self,
580+
cmd: Sequence[bytes | os.PathLike[bytes] | str | os.PathLike[str]],
581+
**kwargs,
582+
):
561583
env = dict(os.environ, PATH=self._paths)
562-
with self._fallback_spawn(cmd, env) as fallback:
563-
return super().spawn(cmd, env=env)
584+
with self._fallback_spawn(cmd, env, **kwargs) as fallback:
585+
return super().spawn(cmd, env=env, **kwargs)
564586
return fallback.value
565587

566588
@contextlib.contextmanager
567-
def _fallback_spawn(self, cmd, env):
589+
def _fallback_spawn(self, cmd, env, **kwargs):
568590
"""
569591
Discovered in pypa/distutils#15, some tools monkeypatch the compiler,
570592
so the 'env' kwarg causes a TypeError. Detect this condition and
@@ -580,7 +602,7 @@ def _fallback_spawn(self, cmd, env):
580602
return
581603
warnings.warn("Fallback spawn triggered. Please update distutils monkeypatch.")
582604
with mock.patch.dict('os.environ', env):
583-
bag.value = super().spawn(cmd)
605+
bag.value = super().spawn(cmd, **kwargs)
584606

585607
# -- Miscellaneous methods -----------------------------------------
586608
# These are all used by the 'gen_lib_options() function, in

distutils/compilers/C/tests/test_base.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ def c_file(tmp_path):
1818
all_headers = gen_headers + plat_headers
1919
headers = '\n'.join(f'#include <{header}>\n' for header in all_headers)
2020
payload = (
21-
textwrap.dedent(
21+
textwrap
22+
.dedent(
2223
"""
2324
#headers
2425
void PyInit_foo(void) {}

distutils/spawn.py

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@
1212
import subprocess
1313
import sys
1414
import warnings
15-
from collections.abc import Mapping, MutableSequence
16-
from typing import TYPE_CHECKING, TypeVar, overload
15+
from collections.abc import Mapping, Sequence
16+
from typing import TYPE_CHECKING, Literal, TypeVar, overload
1717

1818
from ._log import log
1919
from .debug import DEBUG
@@ -52,8 +52,24 @@ def _resolve(env: _MappingT | None) -> _MappingT | os._Environ[str]:
5252
return os.environ if env is None else env
5353

5454

55+
@overload
56+
def spawn(
57+
cmd: Sequence[bytes | os.PathLike[bytes] | str | os.PathLike[str]],
58+
search_path: Literal[False],
59+
verbose: bool = False,
60+
dry_run: bool = False,
61+
env: _ENV | None = None,
62+
) -> None: ...
63+
@overload
64+
def spawn(
65+
cmd: Sequence[bytes | str | os.PathLike[str]],
66+
search_path: Literal[True] = True,
67+
verbose: bool = False,
68+
dry_run: bool = False,
69+
env: _ENV | None = None,
70+
) -> None: ...
5571
def spawn(
56-
cmd: MutableSequence[bytes | str | os.PathLike[str]],
72+
cmd: Sequence[bytes | os.PathLike[bytes] | str | os.PathLike[str]],
5773
search_path: bool = True,
5874
verbose: bool = False,
5975
dry_run: bool = False,
@@ -81,7 +97,7 @@ def spawn(
8197
if search_path:
8298
executable = shutil.which(cmd[0])
8399
if executable is not None:
84-
cmd[0] = executable
100+
cmd = [executable, *cmd[1:]]
85101

86102
try:
87103
subprocess.check_call(cmd, env=_inject_macos_ver(env))

0 commit comments

Comments
 (0)