Skip to content

Commit bf1a1fa

Browse files
taoufik07asottile
authored andcommitted
Fix command normalization when a custom env is passed
1 parent 0dbc154 commit bf1a1fa

File tree

5 files changed

+35
-27
lines changed

5 files changed

+35
-27
lines changed

pre_commit/parse_shebang.py

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,13 @@ def parse_filename(filename: str) -> tuple[str, ...]:
2020

2121

2222
def find_executable(
23-
exe: str, _environ: Mapping[str, str] | None = None,
23+
exe: str, *, env: Mapping[str, str] | None = None,
2424
) -> str | None:
2525
exe = os.path.normpath(exe)
2626
if os.sep in exe:
2727
return exe
2828

29-
environ = _environ if _environ is not None else os.environ
29+
environ = env if env is not None else os.environ
3030

3131
if 'PATHEXT' in environ:
3232
exts = environ['PATHEXT'].split(os.pathsep)
@@ -43,12 +43,12 @@ def find_executable(
4343
return None
4444

4545

46-
def normexe(orig: str) -> str:
46+
def normexe(orig: str, *, env: Mapping[str, str] | None = None) -> str:
4747
def _error(msg: str) -> NoReturn:
4848
raise ExecutableNotFoundError(f'Executable `{orig}` {msg}')
4949

5050
if os.sep not in orig and (not os.altsep or os.altsep not in orig):
51-
exe = find_executable(orig)
51+
exe = find_executable(orig, env=env)
5252
if exe is None:
5353
_error('not found')
5454
return exe
@@ -62,20 +62,24 @@ def _error(msg: str) -> NoReturn:
6262
return orig
6363

6464

65-
def normalize_cmd(cmd: tuple[str, ...]) -> tuple[str, ...]:
65+
def normalize_cmd(
66+
cmd: tuple[str, ...],
67+
*,
68+
env: Mapping[str, str] | None = None,
69+
) -> tuple[str, ...]:
6670
"""Fixes for the following issues on windows
6771
- https://bugs.python.org/issue8557
6872
- windows does not parse shebangs
6973
7074
This function also makes deep-path shebangs work just fine
7175
"""
7276
# Use PATH to determine the executable
73-
exe = normexe(cmd[0])
77+
exe = normexe(cmd[0], env=env)
7478

7579
# Figure out the shebang from the resulting command
7680
cmd = parse_filename(exe) + (exe,) + cmd[1:]
7781

7882
# This could have given us back another bare executable
79-
exe = normexe(cmd[0])
83+
exe = normexe(cmd[0], env=env)
8084

8185
return (exe,) + cmd[1:]

pre_commit/util.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ def cmd_output_b(
9999
_setdefault_kwargs(kwargs)
100100

101101
try:
102-
cmd = parse_shebang.normalize_cmd(cmd)
102+
cmd = parse_shebang.normalize_cmd(cmd, env=kwargs.get('env'))
103103
except parse_shebang.ExecutableNotFoundError as e:
104104
returncode, stdout_b, stderr_b = e.to_output()
105105
else:

tests/commands/install_uninstall_test.py

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -248,7 +248,7 @@ def test_install_idempotent(tempdir_factory, store):
248248
def _path_without_us():
249249
# Choose a path which *probably* doesn't include us
250250
env = dict(os.environ)
251-
exe = find_executable('pre-commit', _environ=env)
251+
exe = find_executable('pre-commit', env=env)
252252
while exe:
253253
parts = env['PATH'].split(os.pathsep)
254254
after = [
@@ -258,7 +258,7 @@ def _path_without_us():
258258
if parts == after:
259259
raise AssertionError(exe, parts)
260260
env['PATH'] = os.pathsep.join(after)
261-
exe = find_executable('pre-commit', _environ=env)
261+
exe = find_executable('pre-commit', env=env)
262262
return env['PATH']
263263

264264

@@ -276,18 +276,19 @@ def test_environment_not_sourced(tempdir_factory, store):
276276

277277
# Use a specific homedir to ignore --user installs
278278
homedir = tempdir_factory.get()
279-
ret, out = git_commit(
280-
env={
281-
'HOME': homedir,
282-
'PATH': _path_without_us(),
283-
# Git needs this to make a commit
284-
'GIT_AUTHOR_NAME': os.environ['GIT_AUTHOR_NAME'],
285-
'GIT_COMMITTER_NAME': os.environ['GIT_COMMITTER_NAME'],
286-
'GIT_AUTHOR_EMAIL': os.environ['GIT_AUTHOR_EMAIL'],
287-
'GIT_COMMITTER_EMAIL': os.environ['GIT_COMMITTER_EMAIL'],
288-
},
289-
check=False,
290-
)
279+
env = {
280+
'HOME': homedir,
281+
'PATH': _path_without_us(),
282+
# Git needs this to make a commit
283+
'GIT_AUTHOR_NAME': os.environ['GIT_AUTHOR_NAME'],
284+
'GIT_COMMITTER_NAME': os.environ['GIT_COMMITTER_NAME'],
285+
'GIT_AUTHOR_EMAIL': os.environ['GIT_AUTHOR_EMAIL'],
286+
'GIT_COMMITTER_EMAIL': os.environ['GIT_COMMITTER_EMAIL'],
287+
}
288+
if os.name == 'nt' and 'PATHEXT' in os.environ: # pragma: no cover
289+
env['PATHEXT'] = os.environ['PATHEXT']
290+
291+
ret, out = git_commit(env=env, check=False)
291292
assert ret == 1
292293
assert out == (
293294
'`pre-commit` not found. '

tests/languages/rust_test.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from __future__ import annotations
22

3+
from typing import Mapping
34
from unittest import mock
45

56
import pytest
@@ -48,7 +49,9 @@ def test_installs_with_bootstrapped_rustup(tmpdir, language_version):
4849

4950
original_find_executable = parse_shebang.find_executable
5051

51-
def mocked_find_executable(exe: str) -> str | None:
52+
def mocked_find_executable(
53+
exe: str, *, env: Mapping[str, str] | None = None,
54+
) -> str | None:
5255
"""
5356
Return `None` the first time `find_executable` is called to ensure
5457
that the bootstrapping code is executed, then just let the function
@@ -59,7 +62,7 @@ def mocked_find_executable(exe: str) -> str | None:
5962
find_executable_exes.append(exe)
6063
if len(find_executable_exes) == 1:
6164
return None
62-
return original_find_executable(exe)
65+
return original_find_executable(exe, env=env)
6366

6467
with mock.patch.object(parse_shebang, 'find_executable') as find_exe_mck:
6568
find_exe_mck.side_effect = mocked_find_executable

tests/parse_shebang_test.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -75,10 +75,10 @@ def test_find_executable_path_ext(in_tmpdir):
7575
env_path = {'PATH': os.path.dirname(exe_path)}
7676
env_path_ext = dict(env_path, PATHEXT=os.pathsep.join(('.exe', '.myext')))
7777
assert parse_shebang.find_executable('run') is None
78-
assert parse_shebang.find_executable('run', _environ=env_path) is None
79-
ret = parse_shebang.find_executable('run.myext', _environ=env_path)
78+
assert parse_shebang.find_executable('run', env=env_path) is None
79+
ret = parse_shebang.find_executable('run.myext', env=env_path)
8080
assert ret == exe_path
81-
ret = parse_shebang.find_executable('run', _environ=env_path_ext)
81+
ret = parse_shebang.find_executable('run', env=env_path_ext)
8282
assert ret == exe_path
8383

8484

0 commit comments

Comments
 (0)