Use virtualenv python from install-time for less virtualenv requirements at commit time.

This commit is contained in:
Anthony Sottile 2014-09-04 08:44:07 -07:00
parent bba24b6535
commit 598e54640b
4 changed files with 57 additions and 15 deletions

View file

@ -6,6 +6,7 @@ import logging
import os import os
import os.path import os.path
import stat import stat
import sys
from pre_commit.logging_handler import LoggingHandler from pre_commit.logging_handler import LoggingHandler
from pre_commit.util import resource_filename from pre_commit.util import resource_filename
@ -15,12 +16,13 @@ logger = logging.getLogger('pre_commit')
# This is used to identify the hook file we install # This is used to identify the hook file we install
PREVIOUS_IDENTIFYING_HASHES = [ PREVIOUS_IDENTIFYING_HASHES = (
'4d9958c90bc262f47553e2c073f14cfe',
'd8ee923c46731b42cd95cc869add4062', 'd8ee923c46731b42cd95cc869add4062',
] )
IDENTIFYING_HASH = '4d9958c90bc262f47553e2c073f14cfe' IDENTIFYING_HASH = '49fd668cb42069aa1b6048464be5d395'
def is_our_pre_commit(filename): def is_our_pre_commit(filename):
@ -63,8 +65,11 @@ def install(runner, overwrite=False, hooks=False):
) )
) )
with open(runner.pre_commit_path, 'w') as pre_commit_file_obj: with io.open(runner.pre_commit_path, 'w') as pre_commit_file_obj:
pre_commit_file_obj.write(open(pre_commit_file).read()) contents = io.open(pre_commit_file).read().format(
sys_executable=sys.executable,
)
pre_commit_file_obj.write(contents)
make_executable(runner.pre_commit_path) make_executable(runner.pre_commit_path)
print('pre-commit installed at {0}'.format(runner.pre_commit_path)) print('pre-commit installed at {0}'.format(runner.pre_commit_path))

View file

@ -1,5 +1,6 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import os
import subprocess import subprocess
import sys import sys
@ -10,7 +11,7 @@ from pre_commit import five
# TODO: smell: import side-effects # TODO: smell: import side-effects
COLS = int( COLS = int(
subprocess.Popen( subprocess.Popen(
['tput', 'cols'], stdout=subprocess.PIPE ['tput', 'cols'], stdout=subprocess.PIPE, stderr=open(os.devnull, 'w'),
).communicate()[0] or ).communicate()[0] or
# Default in the case of no terminal # Default in the case of no terminal
80 80

View file

@ -1,6 +1,6 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# This is a randomish md5 to identify this script # This is a randomish md5 to identify this script
# 4d9958c90bc262f47553e2c073f14cfe # 49fd668cb42069aa1b6048464be5d395
pushd `dirname $0` > /dev/null pushd `dirname $0` > /dev/null
HERE=`pwd` HERE=`pwd`
@ -8,12 +8,21 @@ popd > /dev/null
retv=0 retv=0
ENV_PYTHON='{sys_executable}'
which pre-commit >& /dev/null which pre-commit >& /dev/null
WHICH_RETV=$? WHICH_RETV=$?
"$ENV_PYTHON" -c 'import pre_commit.main' >& /dev/null
ENV_PYTHON_RETV=$?
python -c 'import pre_commit.main' >& /dev/null python -c 'import pre_commit.main' >& /dev/null
PYTHON_RETV=$? PYTHON_RETV=$?
if [ $WHICH_RETV -ne 0 ] && [ $PYTHON_RETV -ne 0 ]; then
if ((
(WHICH_RETV != 0) &&
(ENV_PYTHON_RETV != 0) &&
(PYTHON_RETV != 0)
)); then
echo '`pre-commit` not found. Did you forget to activate your virtualenv?' echo '`pre-commit` not found. Did you forget to activate your virtualenv?'
exit 1 exit 1
fi fi
@ -29,15 +38,18 @@ fi
# Run pre-commit # Run pre-commit
if [ $WHICH_RETV -eq 0 ]; then if ((WHICH_RETV == 0)); then
pre-commit pre-commit
PRE_COMMIT_RETV=$? PRE_COMMIT_RETV=$?
elif ((ENV_PYTHON_RETV == 0)); then
"$ENV_PYTHON" -m pre_commit.main
PRE_COMMIT_RETV=$?
else else
python -m pre_commit.main python -m pre_commit.main
PRE_COMMIT_RETV=$? PRE_COMMIT_RETV=$?
fi fi
if [ $PRE_COMMIT_RETV -ne 0 ]; then if ((PRE_COMMIT_RETV != 0)); then
retv=1 retv=1
fi fi

View file

@ -2,11 +2,13 @@ from __future__ import absolute_import
from __future__ import unicode_literals from __future__ import unicode_literals
import io import io
import mock
import os import os
import os.path import os.path
import re import re
import subprocess import subprocess
import stat import stat
import sys
from plumbum import local from plumbum import local
from pre_commit.commands.install_uninstall import IDENTIFYING_HASH from pre_commit.commands.install_uninstall import IDENTIFYING_HASH
@ -53,7 +55,9 @@ def test_install_pre_commit(tmpdir_factory):
assert os.path.exists(runner.pre_commit_path) assert os.path.exists(runner.pre_commit_path)
pre_commit_contents = io.open(runner.pre_commit_path).read() pre_commit_contents = io.open(runner.pre_commit_path).read()
pre_commit_script = resource_filename('pre-commit-hook') pre_commit_script = resource_filename('pre-commit-hook')
expected_contents = io.open(pre_commit_script).read() expected_contents = io.open(pre_commit_script).read().format(
sys_executable=sys.executable,
)
assert pre_commit_contents == expected_contents assert pre_commit_contents == expected_contents
stat_result = os.stat(runner.pre_commit_path) stat_result = os.stat(runner.pre_commit_path)
assert stat_result.st_mode & (stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH) assert stat_result.st_mode & (stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH)
@ -76,12 +80,17 @@ def test_uninstall(tmpdir_factory):
assert not os.path.exists(runner.pre_commit_path) assert not os.path.exists(runner.pre_commit_path)
def _get_commit_output(tmpdir_factory, touch_file='foo', home=None): def _get_commit_output(
tmpdir_factory,
touch_file='foo',
home=None,
env_base=os.environ,
):
local['touch'](touch_file) local['touch'](touch_file)
local['git']('add', touch_file) local['git']('add', touch_file)
# Don't want to write to home directory # Don't want to write to home directory
home = home or tmpdir_factory.get() home = home or tmpdir_factory.get()
env = dict(os.environ, **{'PRE_COMMIT_HOME': home}) env = dict(env_base, **{'PRE_COMMIT_HOME': home})
return local['git'].run( return local['git'].run(
['commit', '-m', 'Commit!', '--allow-empty'], ['commit', '-m', 'Commit!', '--allow-empty'],
# git commit puts pre-commit to stderr # git commit puts pre-commit to stderr
@ -136,11 +145,12 @@ def test_install_idempotent(tmpdir_factory):
def test_environment_not_sourced(tmpdir_factory): def test_environment_not_sourced(tmpdir_factory):
path = make_consuming_repo(tmpdir_factory, 'script_hooks_repo') path = make_consuming_repo(tmpdir_factory, 'script_hooks_repo')
with local.cwd(path): with local.cwd(path):
assert install(Runner(path)) == 0 # Patch the executable to simulate rming virtualenv
with mock.patch.object(sys, 'executable', '/bin/false'):
assert install(Runner(path)) == 0
ret, stdout, stderr = local['git'].run( ret, stdout, stderr = local['git'].run(
['commit', '--allow-empty', '-m', 'foo'], ['commit', '--allow-empty', '-m', 'foo'],
# XXX: 'HOME' makes this test pass on OSX
env={'HOME': os.environ['HOME']}, env={'HOME': os.environ['HOME']},
retcode=None, retcode=None,
) )
@ -362,3 +372,17 @@ def test_installs_hooks_with_hooks_True(
assert ret == 0 assert ret == 0
assert PRE_INSTALLED.match(output) assert PRE_INSTALLED.match(output)
def test_installed_from_venv(tmpdir_factory):
path = make_consuming_repo(tmpdir_factory, 'script_hooks_repo')
with local.cwd(path):
install(Runner(path))
# No environment so pre-commit is not on the path when running!
# Should still pick up the python from when we installed
ret, output = _get_commit_output(
tmpdir_factory,
env_base={'HOME': os.environ['HOME']},
)
assert ret == 0
assert NORMAL_PRE_COMMIT_RUN.match(output)