Use a more intelligent default language version

This commit is contained in:
Anthony Sottile
2017-07-08 20:22:36 -07:00
parent 853cbecd4e
commit d876661345
15 changed files with 109 additions and 54 deletions

View File

@@ -10,16 +10,18 @@ from pre_commit.languages import script
from pre_commit.languages import swift
from pre_commit.languages import system
# A language implements the following constant and two functions in its module:
# A language implements the following constant and functions in its module:
#
# # Use None for no environment
# ENVIRONMENT_DIR = 'foo_env'
#
# def install_environment(
# repo_cmd_runner,
# version='default',
# additional_dependencies=(),
# ):
# def get_default_version():
# """Return a value to replace the 'default' value for language_version.
#
# return 'default' if there is no better option.
# """
#
# def install_environment(repo_cmd_runner, version, additional_dependencies):
# """Installs a repository in the given repository. Note that the current
# working directory will already be inside the repository.
#

View File

@@ -14,6 +14,7 @@ from pre_commit.xargs import xargs
ENVIRONMENT_DIR = 'docker'
PRE_COMMIT_LABEL = 'PRE_COMMIT'
get_default_version = helpers.basic_get_default_version
def md5(s): # pragma: windows no cover
@@ -55,9 +56,7 @@ def build_docker_image(repo_cmd_runner, **kwargs): # pragma: windows no cover
def install_environment(
repo_cmd_runner,
version='default',
additional_dependencies=(),
repo_cmd_runner, version, additional_dependencies,
): # pragma: windows no cover
assert repo_cmd_runner.exists('Dockerfile'), (
'No Dockerfile was found in the hook repository'

View File

@@ -14,6 +14,7 @@ from pre_commit.xargs import xargs
ENVIRONMENT_DIR = 'golangenv'
get_default_version = helpers.basic_get_default_version
def get_env_patch(venv):
@@ -44,11 +45,7 @@ def guess_go_dir(remote_url):
return 'unknown_src_dir'
def install_environment(
repo_cmd_runner,
version='default',
additional_dependencies=(),
):
def install_environment(repo_cmd_runner, version, additional_dependencies):
helpers.assert_version_default('golang', version)
directory = repo_cmd_runner.path(
helpers.environment_dir(ENVIRONMENT_DIR, 'default'),

View File

@@ -33,3 +33,7 @@ def assert_no_additional_deps(lang, additional_deps):
'For now, pre-commit does not support '
'additional_dependencies for {}'.format(lang),
)
def basic_get_default_version():
return 'default'

View File

@@ -12,6 +12,7 @@ from pre_commit.xargs import xargs
ENVIRONMENT_DIR = 'node_env'
get_default_version = helpers.basic_get_default_version
def get_env_patch(venv): # pragma: windows no cover
@@ -34,9 +35,7 @@ def in_env(repo_cmd_runner, language_version): # pragma: windows no cover
def install_environment(
repo_cmd_runner,
version='default',
additional_dependencies=(),
repo_cmd_runner, version, additional_dependencies,
): # pragma: windows no cover
additional_dependencies = tuple(additional_dependencies)
assert repo_cmd_runner.exists('package.json')

View File

@@ -2,18 +2,16 @@ from __future__ import unicode_literals
import sys
from pre_commit.languages import helpers
from pre_commit.xargs import xargs
ENVIRONMENT_DIR = None
GREP = 'ggrep' if sys.platform == 'darwin' else 'grep'
get_default_version = helpers.basic_get_default_version
def install_environment(
repo_cmd_runner,
version='default',
additional_dependencies=(),
):
def install_environment(repo_cmd_runner, version, additional_dependencies):
"""Installation for pcre type is a noop."""
raise AssertionError('Cannot install pcre repo.')

View File

@@ -1,7 +1,6 @@
from __future__ import unicode_literals
import contextlib
import distutils.spawn
import os
import sys
@@ -9,11 +8,13 @@ from pre_commit.envcontext import envcontext
from pre_commit.envcontext import UNSET
from pre_commit.envcontext import Var
from pre_commit.languages import helpers
from pre_commit.parse_shebang import find_executable
from pre_commit.util import clean_path_on_failure
from pre_commit.xargs import xargs
ENVIRONMENT_DIR = 'py_env'
get_default_version = helpers.basic_get_default_version
def bin_dir(venv):
@@ -39,10 +40,53 @@ def in_env(repo_cmd_runner, language_version):
yield
def _get_default_version(): # pragma: no cover (platform dependent)
def _norm(path):
_, exe = os.path.split(path.lower())
exe, _, _ = exe.partition('.exe')
if find_executable(exe) and exe not in {'python', 'pythonw'}:
return exe
# First attempt from `sys.executable` (or the realpath)
# On linux, I see these common sys.executables:
#
# system `python`: /usr/bin/python -> python2.7
# system `python2`: /usr/bin/python2 -> python2.7
# virtualenv v: v/bin/python (will not return from this loop)
# virtualenv v -ppython2: v/bin/python -> python2
# virtualenv v -ppython2.7: v/bin/python -> python2.7
# virtualenv v -ppypy: v/bin/python -> v/bin/pypy
for path in {sys.executable, os.path.realpath(sys.executable)}:
exe = _norm(path)
if exe:
return exe
# Next try the `pythonX.X` executable
exe = 'python{}.{}'.format(*sys.version_info)
if find_executable(exe):
return exe
# Give a best-effort try for windows
if os.path.exists(r'C:\{}\python.exe'.format(exe.replace('.', ''))):
return exe
# We tried!
return 'default'
def get_default_version():
# TODO: when dropping python2, use `functools.lru_cache(maxsize=1)`
try:
return get_default_version.cached_version
except AttributeError:
get_default_version.cached_version = _get_default_version()
return get_default_version()
def norm_version(version):
if os.name == 'nt': # pragma: no cover (windows)
# Try looking up by name
if distutils.spawn.find_executable(version):
if find_executable(version) and find_executable(version) != version:
return version
# If it is in the form pythonx.x search in the default
@@ -54,11 +98,7 @@ def norm_version(version):
return os.path.expanduser(version)
def install_environment(
repo_cmd_runner,
version='default',
additional_dependencies=(),
):
def install_environment(repo_cmd_runner, version, additional_dependencies):
additional_dependencies = tuple(additional_dependencies)
directory = helpers.environment_dir(ENVIRONMENT_DIR, version)

View File

@@ -16,6 +16,7 @@ from pre_commit.xargs import xargs
ENVIRONMENT_DIR = 'rbenv'
get_default_version = helpers.basic_get_default_version
def get_env_patch(venv, language_version): # pragma: windows no cover
@@ -97,9 +98,7 @@ def _install_ruby(runner, version): # pragma: windows no cover
def install_environment(
repo_cmd_runner,
version='default',
additional_dependencies=(),
repo_cmd_runner, version, additional_dependencies,
): # pragma: windows no cover
additional_dependencies = tuple(additional_dependencies)
directory = helpers.environment_dir(ENVIRONMENT_DIR, version)

View File

@@ -5,13 +5,10 @@ from pre_commit.xargs import xargs
ENVIRONMENT_DIR = None
get_default_version = helpers.basic_get_default_version
def install_environment(
repo_cmd_runner,
version='default',
additional_dependencies=(),
):
def install_environment(repo_cmd_runner, version, additional_dependencies):
"""Installation for script type is a noop."""
raise AssertionError('Cannot install script repo.')

View File

@@ -10,6 +10,7 @@ from pre_commit.util import clean_path_on_failure
from pre_commit.xargs import xargs
ENVIRONMENT_DIR = 'swift_env'
get_default_version = helpers.basic_get_default_version
BUILD_DIR = '.build'
BUILD_CONFIG = 'release'
@@ -29,9 +30,7 @@ def in_env(repo_cmd_runner): # pragma: windows no cover
def install_environment(
repo_cmd_runner,
version='default',
additional_dependencies=(),
repo_cmd_runner, version, additional_dependencies,
): # pragma: windows no cover
helpers.assert_version_default('swift', version)
helpers.assert_no_additional_deps('swift', additional_dependencies)

View File

@@ -5,13 +5,10 @@ from pre_commit.xargs import xargs
ENVIRONMENT_DIR = None
get_default_version = helpers.basic_get_default_version
def install_environment(
repo_cmd_runner,
version='default',
additional_dependencies=(),
):
def install_environment(repo_cmd_runner, version, additional_dependencies):
"""Installation for system type is a noop."""
raise AssertionError('Cannot install system repo.')

View File

@@ -7,6 +7,7 @@ from cached_property import cached_property
import pre_commit.constants as C
from pre_commit.clientlib import load_manifest
from pre_commit.languages.all import languages
logger = logging.getLogger('pre_commit')
@@ -38,4 +39,10 @@ class Manifest(object):
@cached_property
def hooks(self):
return {hook['id']: hook for hook in self.manifest_contents}
ret = {}
for hook in self.manifest_contents:
if hook['language_version'] == 'default':
language = languages[hook['language']]
hook['language_version'] = language.get_default_version()
ret[hook['id']] = hook
return ret

View File

@@ -12,9 +12,7 @@ from pre_commit.languages.all import languages
def test_install_environment_argspec(language):
expected_argspec = inspect.ArgSpec(
args=['repo_cmd_runner', 'version', 'additional_dependencies'],
varargs=None,
keywords=None,
defaults=('default', ()),
varargs=None, keywords=None, defaults=None,
)
argspec = inspect.getargspec(languages[language].install_environment)
assert argspec == expected_argspec
@@ -33,3 +31,12 @@ def test_run_hook_argpsec(language):
)
argspec = inspect.getargspec(languages[language].run_hook)
assert argspec == expected_argspec
@pytest.mark.parametrize('language', all_languages)
def test_get_default_version_argspec(language):
expected_argspec = inspect.ArgSpec(
args=[], varargs=None, keywords=None, defaults=None,
)
argspec = inspect.getargspec(languages[language].get_default_version)
assert argspec == expected_argspec

View File

@@ -11,8 +11,7 @@ from testing.util import get_head_sha
@pytest.yield_fixture
def manifest(store, tempdir_factory):
path = make_repo(tempdir_factory, 'script_hooks_repo')
head_sha = get_head_sha(path)
repo_path = store.clone(path, head_sha)
repo_path = store.clone(path, get_head_sha(path))
yield Manifest(repo_path, path)
@@ -76,3 +75,13 @@ def test_legacy_manifest_warn(store, tempdir_factory, log_warning_mock):
'If `pre-commit autoupdate` does not silence this warning consider '
'making an issue / pull request.'.format(path)
)
def test_default_python_language_version(store, tempdir_factory):
path = make_repo(tempdir_factory, 'python_hooks_repo')
repo_path = store.clone(path, get_head_sha(path))
manifest = Manifest(repo_path, path)
# This assertion is difficult as it is version dependent, just assert
# that it is *something*
assert manifest.hooks['foo']['language_version'] != 'default'

View File

@@ -442,7 +442,7 @@ def test_venvs(tempdir_factory, store):
config = make_config_from_repo(path)
repo = Repository.create(config, store)
venv, = repo._venvs
assert venv == (mock.ANY, 'python', 'default', [])
assert venv == (mock.ANY, 'python', python.get_default_version(), [])
@pytest.mark.integration
@@ -452,7 +452,7 @@ def test_additional_dependencies(tempdir_factory, store):
config['hooks'][0]['additional_dependencies'] = ['pep8']
repo = Repository.create(config, store)
venv, = repo._venvs
assert venv == (mock.ANY, 'python', 'default', ['pep8'])
assert venv == (mock.ANY, 'python', python.get_default_version(), ['pep8'])
@pytest.mark.integration
@@ -591,7 +591,8 @@ def test_control_c_control_c_on_install(tempdir_factory, store):
repo.run_hook(hook, [])
# Should have made an environment, however this environment is broken!
assert os.path.exists(repo._cmd_runner.path('py_env-default'))
envdir = 'py_env-{}'.format(python.get_default_version())
assert repo._cmd_runner.exists(envdir)
# However, it should be perfectly runnable (reinstall after botched
# install)