Skip to content

Commit f3f9fbb

Browse files
committed
don't use system for ruby/node if it is a shim exe
1 parent 0c339e0 commit f3f9fbb

File tree

4 files changed

+68
-5
lines changed

4 files changed

+68
-5
lines changed

pre_commit/languages/helpers.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import multiprocessing
22
import os
33
import random
4+
import re
45
from typing import Any
56
from typing import List
67
from typing import Optional
@@ -10,6 +11,7 @@
1011
from typing import TYPE_CHECKING
1112

1213
import pre_commit.constants as C
14+
from pre_commit import parse_shebang
1315
from pre_commit.hook import Hook
1416
from pre_commit.prefix import Prefix
1517
from pre_commit.util import cmd_output_b
@@ -20,6 +22,25 @@
2022

2123
FIXED_RANDOM_SEED = 1542676187
2224

25+
SHIMS_RE = re.compile(r'[/\\]shims[/\\]')
26+
27+
28+
def exe_exists(exe: str) -> bool:
29+
found = parse_shebang.find_executable(exe)
30+
if found is None: # exe exists
31+
return False
32+
33+
homedir = os.path.expanduser('~')
34+
try:
35+
common: Optional[str] = os.path.commonpath((found, homedir))
36+
except ValueError: # on windows, different drives raises ValueError
37+
common = None
38+
39+
return (
40+
not SHIMS_RE.search(found) and # it is not in a /shims/ directory
41+
common != homedir # it is not in the home directory
42+
)
43+
2344

2445
def run_setup_cmd(prefix: Prefix, cmd: Tuple[str, ...]) -> None:
2546
cmd_output_b(*cmd, cwd=prefix.prefix_dir)

pre_commit/languages/node.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
from typing import Tuple
88

99
import pre_commit.constants as C
10-
from pre_commit import parse_shebang
1110
from pre_commit.envcontext import envcontext
1211
from pre_commit.envcontext import PatchesT
1312
from pre_commit.envcontext import UNSET
@@ -31,7 +30,7 @@ def get_default_version() -> str:
3130
return C.DEFAULT
3231
# if node is already installed, we can save a bunch of setup time by
3332
# using the installed version
34-
elif all(parse_shebang.find_executable(exe) for exe in ('node', 'npm')):
33+
elif all(helpers.exe_exists(exe) for exe in ('node', 'npm')):
3534
return 'system'
3635
else:
3736
return C.DEFAULT

pre_commit/languages/ruby.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
from typing import Tuple
99

1010
import pre_commit.constants as C
11-
from pre_commit import parse_shebang
1211
from pre_commit.envcontext import envcontext
1312
from pre_commit.envcontext import PatchesT
1413
from pre_commit.envcontext import UNSET
@@ -26,7 +25,7 @@
2625

2726
@functools.lru_cache(maxsize=1)
2827
def get_default_version() -> str:
29-
if all(parse_shebang.find_executable(exe) for exe in ('ruby', 'gem')):
28+
if all(helpers.exe_exists(exe) for exe in ('ruby', 'gem')):
3029
return 'system'
3130
else:
3231
return C.DEFAULT

tests/languages/helpers_test.py

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,61 @@
11
import multiprocessing
2-
import os
2+
import os.path
33
import sys
44
from unittest import mock
55

66
import pytest
77

88
import pre_commit.constants as C
9+
from pre_commit import parse_shebang
910
from pre_commit.languages import helpers
1011
from pre_commit.prefix import Prefix
1112
from pre_commit.util import CalledProcessError
1213
from testing.auto_namedtuple import auto_namedtuple
1314

1415

16+
@pytest.fixture
17+
def find_exe_mck():
18+
with mock.patch.object(parse_shebang, 'find_executable') as mck:
19+
yield mck
20+
21+
22+
@pytest.fixture
23+
def homedir_mck():
24+
def fake_expanduser(pth):
25+
assert pth == '~'
26+
return '/home/me'
27+
28+
with mock.patch.object(os.path, 'expanduser', fake_expanduser):
29+
yield
30+
31+
32+
def test_exe_exists_does_not_exist(find_exe_mck, homedir_mck):
33+
find_exe_mck.return_value = None
34+
assert helpers.exe_exists('ruby') is False
35+
36+
37+
def test_exe_exists_exists(find_exe_mck, homedir_mck):
38+
find_exe_mck.return_value = '/usr/bin/ruby'
39+
assert helpers.exe_exists('ruby') is True
40+
41+
42+
def test_exe_exists_false_if_shim(find_exe_mck, homedir_mck):
43+
find_exe_mck.return_value = '/foo/shims/ruby'
44+
assert helpers.exe_exists('ruby') is False
45+
46+
47+
def test_exe_exists_false_if_homedir(find_exe_mck, homedir_mck):
48+
find_exe_mck.return_value = '/home/me/somedir/ruby'
49+
with mock.patch.object(os.path, 'expanduser', return_value='/home/me'):
50+
assert helpers.exe_exists('ruby') is False
51+
52+
53+
def test_exe_exists_commonpath_raises_ValueError(find_exe_mck, homedir_mck):
54+
find_exe_mck.return_value = '/usr/bin/ruby'
55+
with mock.patch.object(os.path, 'commonpath', side_effect=ValueError):
56+
assert helpers.exe_exists('ruby') is True
57+
58+
1559
def test_basic_get_default_version():
1660
assert helpers.basic_get_default_version() == C.DEFAULT
1761

0 commit comments

Comments
 (0)