Skip to content

Commit b089c74

Browse files
committed
Merge pull request pre-commit#166 from pre-commit/venv_python
Use virtualenv python from install-time for less virtualenv requirements at commit time.
2 parents a6112f4 + 598e546 commit b089c74

4 files changed

Lines changed: 57 additions & 15 deletions

File tree

pre_commit/commands/install_uninstall.py

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import os
77
import os.path
88
import stat
9+
import sys
910

1011
from pre_commit.logging_handler import LoggingHandler
1112
from pre_commit.util import resource_filename
@@ -15,12 +16,13 @@
1516

1617

1718
# This is used to identify the hook file we install
18-
PREVIOUS_IDENTIFYING_HASHES = [
19+
PREVIOUS_IDENTIFYING_HASHES = (
20+
'4d9958c90bc262f47553e2c073f14cfe',
1921
'd8ee923c46731b42cd95cc869add4062',
20-
]
22+
)
2123

2224

23-
IDENTIFYING_HASH = '4d9958c90bc262f47553e2c073f14cfe'
25+
IDENTIFYING_HASH = '49fd668cb42069aa1b6048464be5d395'
2426

2527

2628
def is_our_pre_commit(filename):
@@ -63,8 +65,11 @@ def install(runner, overwrite=False, hooks=False):
6365
)
6466
)
6567

66-
with open(runner.pre_commit_path, 'w') as pre_commit_file_obj:
67-
pre_commit_file_obj.write(open(pre_commit_file).read())
68+
with io.open(runner.pre_commit_path, 'w') as pre_commit_file_obj:
69+
contents = io.open(pre_commit_file).read().format(
70+
sys_executable=sys.executable,
71+
)
72+
pre_commit_file_obj.write(contents)
6873
make_executable(runner.pre_commit_path)
6974

7075
print('pre-commit installed at {0}'.format(runner.pre_commit_path))

pre_commit/output.py

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

3+
import os
34
import subprocess
45
import sys
56

@@ -10,7 +11,7 @@
1011
# TODO: smell: import side-effects
1112
COLS = int(
1213
subprocess.Popen(
13-
['tput', 'cols'], stdout=subprocess.PIPE
14+
['tput', 'cols'], stdout=subprocess.PIPE, stderr=open(os.devnull, 'w'),
1415
).communicate()[0] or
1516
# Default in the case of no terminal
1617
80

pre_commit/resources/pre-commit-hook

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,28 @@
11
#!/usr/bin/env bash
22
# This is a randomish md5 to identify this script
3-
# 4d9958c90bc262f47553e2c073f14cfe
3+
# 49fd668cb42069aa1b6048464be5d395
44

55
pushd `dirname $0` > /dev/null
66
HERE=`pwd`
77
popd > /dev/null
88

99
retv=0
1010

11+
ENV_PYTHON='{sys_executable}'
12+
1113
which pre-commit >& /dev/null
1214
WHICH_RETV=$?
15+
"$ENV_PYTHON" -c 'import pre_commit.main' >& /dev/null
16+
ENV_PYTHON_RETV=$?
1317
python -c 'import pre_commit.main' >& /dev/null
1418
PYTHON_RETV=$?
1519

16-
if [ $WHICH_RETV -ne 0 ] && [ $PYTHON_RETV -ne 0 ]; then
20+
21+
if ((
22+
(WHICH_RETV != 0) &&
23+
(ENV_PYTHON_RETV != 0) &&
24+
(PYTHON_RETV != 0)
25+
)); then
1726
echo '`pre-commit` not found. Did you forget to activate your virtualenv?'
1827
exit 1
1928
fi
@@ -29,15 +38,18 @@ fi
2938

3039

3140
# Run pre-commit
32-
if [ $WHICH_RETV -eq 0 ]; then
41+
if ((WHICH_RETV == 0)); then
3342
pre-commit
3443
PRE_COMMIT_RETV=$?
44+
elif ((ENV_PYTHON_RETV == 0)); then
45+
"$ENV_PYTHON" -m pre_commit.main
46+
PRE_COMMIT_RETV=$?
3547
else
3648
python -m pre_commit.main
3749
PRE_COMMIT_RETV=$?
3850
fi
3951

40-
if [ $PRE_COMMIT_RETV -ne 0 ]; then
52+
if ((PRE_COMMIT_RETV != 0)); then
4153
retv=1
4254
fi
4355

tests/commands/install_uninstall_test.py

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,13 @@
22
from __future__ import unicode_literals
33

44
import io
5+
import mock
56
import os
67
import os.path
78
import re
89
import subprocess
910
import stat
11+
import sys
1012
from plumbum import local
1113

1214
from pre_commit.commands.install_uninstall import IDENTIFYING_HASH
@@ -53,7 +55,9 @@ def test_install_pre_commit(tmpdir_factory):
5355
assert os.path.exists(runner.pre_commit_path)
5456
pre_commit_contents = io.open(runner.pre_commit_path).read()
5557
pre_commit_script = resource_filename('pre-commit-hook')
56-
expected_contents = io.open(pre_commit_script).read()
58+
expected_contents = io.open(pre_commit_script).read().format(
59+
sys_executable=sys.executable,
60+
)
5761
assert pre_commit_contents == expected_contents
5862
stat_result = os.stat(runner.pre_commit_path)
5963
assert stat_result.st_mode & (stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH)
@@ -76,12 +80,17 @@ def test_uninstall(tmpdir_factory):
7680
assert not os.path.exists(runner.pre_commit_path)
7781

7882

79-
def _get_commit_output(tmpdir_factory, touch_file='foo', home=None):
83+
def _get_commit_output(
84+
tmpdir_factory,
85+
touch_file='foo',
86+
home=None,
87+
env_base=os.environ,
88+
):
8089
local['touch'](touch_file)
8190
local['git']('add', touch_file)
8291
# Don't want to write to home directory
8392
home = home or tmpdir_factory.get()
84-
env = dict(os.environ, **{'PRE_COMMIT_HOME': home})
93+
env = dict(env_base, **{'PRE_COMMIT_HOME': home})
8594
return local['git'].run(
8695
['commit', '-m', 'Commit!', '--allow-empty'],
8796
# git commit puts pre-commit to stderr
@@ -136,11 +145,12 @@ def test_install_idempotent(tmpdir_factory):
136145
def test_environment_not_sourced(tmpdir_factory):
137146
path = make_consuming_repo(tmpdir_factory, 'script_hooks_repo')
138147
with local.cwd(path):
139-
assert install(Runner(path)) == 0
148+
# Patch the executable to simulate rming virtualenv
149+
with mock.patch.object(sys, 'executable', '/bin/false'):
150+
assert install(Runner(path)) == 0
140151

141152
ret, stdout, stderr = local['git'].run(
142153
['commit', '--allow-empty', '-m', 'foo'],
143-
# XXX: 'HOME' makes this test pass on OSX
144154
env={'HOME': os.environ['HOME']},
145155
retcode=None,
146156
)
@@ -362,3 +372,17 @@ def test_installs_hooks_with_hooks_True(
362372

363373
assert ret == 0
364374
assert PRE_INSTALLED.match(output)
375+
376+
377+
def test_installed_from_venv(tmpdir_factory):
378+
path = make_consuming_repo(tmpdir_factory, 'script_hooks_repo')
379+
with local.cwd(path):
380+
install(Runner(path))
381+
# No environment so pre-commit is not on the path when running!
382+
# Should still pick up the python from when we installed
383+
ret, output = _get_commit_output(
384+
tmpdir_factory,
385+
env_base={'HOME': os.environ['HOME']},
386+
)
387+
assert ret == 0
388+
assert NORMAL_PRE_COMMIT_RUN.match(output)

0 commit comments

Comments
 (0)