From d3b5a41830acc7254ae6c9bacc7895da5252e6a1 Mon Sep 17 00:00:00 2001 From: Anthony Sottile Date: Sat, 5 Jan 2019 13:01:42 -0800 Subject: [PATCH] Implement default_language_version --- .pre-commit-config.yaml | 2 +- pre_commit/clientlib.py | 41 ++++++++-------- pre_commit/commands/autoupdate.py | 7 ++- pre_commit/commands/gc.py | 8 ++-- pre_commit/commands/install_uninstall.py | 3 +- pre_commit/constants.py | 2 + pre_commit/languages/all.py | 3 +- pre_commit/languages/docker.py | 3 +- pre_commit/languages/golang.py | 5 +- pre_commit/languages/helpers.py | 6 +-- pre_commit/languages/node.py | 3 +- pre_commit/languages/python.py | 5 +- pre_commit/languages/ruby.py | 11 +++-- pre_commit/languages/rust.py | 5 +- pre_commit/languages/swift.py | 5 +- pre_commit/repository.py | 47 ++++++++++--------- pre_commit/xargs.py | 2 +- tests/clientlib_test.py | 22 ++++++--- tests/commands/gc_test.py | 4 +- tests/commands/install_uninstall_test.py | 4 +- tests/languages/helpers_test.py | 3 +- tests/repository_test.py | 60 +++++++++++++++++------- tests/xargs_test.py | 2 +- 23 files changed, 150 insertions(+), 103 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 4fe852f7..9ffdbe94 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -27,7 +27,7 @@ repos: rev: v1.3.0 hooks: - id: reorder-python-imports - language_version: python2.7 + language_version: python3 - repo: https://github.com/asottile/add-trailing-comma rev: v0.7.1 hooks: diff --git a/pre_commit/clientlib.py b/pre_commit/clientlib.py index c5b99477..d458daef 100644 --- a/pre_commit/clientlib.py +++ b/pre_commit/clientlib.py @@ -55,7 +55,7 @@ MANIFEST_HOOK_DICT = cfgv.Map( cfgv.Optional('always_run', cfgv.check_bool, False), cfgv.Optional('pass_filenames', cfgv.check_bool, True), cfgv.Optional('description', cfgv.check_string, ''), - cfgv.Optional('language_version', cfgv.check_string, 'default'), + cfgv.Optional('language_version', cfgv.check_string, C.DEFAULT), cfgv.Optional('log_file', cfgv.check_string, ''), cfgv.Optional('minimum_pre_commit_version', cfgv.check_string, '0'), cfgv.Optional('require_serial', cfgv.check_bool, False), @@ -90,8 +90,8 @@ def validate_manifest_main(argv=None): return ret -_LOCAL = 'local' -_META = 'meta' +LOCAL = 'local' +META = 'meta' class MigrateShaToRev(object): @@ -100,12 +100,12 @@ class MigrateShaToRev(object): return cfgv.Conditional( key, cfgv.check_string, condition_key='repo', - condition_value=cfgv.NotIn(_LOCAL, _META), + condition_value=cfgv.NotIn(LOCAL, META), ensure_absent=True, ) def check(self, dct): - if dct.get('repo') in {_LOCAL, _META}: + if dct.get('repo') in {LOCAL, META}: self._cond('rev').check(dct) self._cond('sha').check(dct) elif 'sha' in dct and 'rev' in dct: @@ -159,12 +159,11 @@ _meta = ( META_HOOK_DICT = cfgv.Map( 'Hook', 'id', + cfgv.Required('id', cfgv.check_string), + cfgv.Required('id', cfgv.check_one_of(tuple(k for k, _ in _meta))), + # language must be system + cfgv.Optional('language', cfgv.check_one_of({'system'}), 'system'), *([ - cfgv.Required('id', cfgv.check_string), - cfgv.Required('id', cfgv.check_one_of(tuple(k for k, _ in _meta))), - # language must be system - cfgv.Optional('language', cfgv.check_one_of({'system'}), 'system'), - ] + [ # default to the hook definition for the meta hooks cfgv.ConditionalOptional(key, cfgv.check_any, value, 'id', hook_id) for hook_id, values in _meta @@ -200,36 +199,36 @@ CONFIG_REPO_DICT = cfgv.Map( cfgv.ConditionalRecurse( 'hooks', cfgv.Array(CONFIG_HOOK_DICT), - 'repo', cfgv.NotIn(_LOCAL, _META), + 'repo', cfgv.NotIn(LOCAL, META), ), cfgv.ConditionalRecurse( 'hooks', cfgv.Array(MANIFEST_HOOK_DICT), - 'repo', _LOCAL, + 'repo', LOCAL, ), cfgv.ConditionalRecurse( 'hooks', cfgv.Array(META_HOOK_DICT), - 'repo', _META, + 'repo', META, ), MigrateShaToRev(), ) +DEFAULT_LANGUAGE_VERSION = cfgv.Map( + 'DefaultLanguageVersion', None, + cfgv.NoAdditionalKeys(all_languages), + *[cfgv.Optional(x, cfgv.check_string, C.DEFAULT) for x in all_languages] +) CONFIG_SCHEMA = cfgv.Map( 'Config', None, cfgv.RequiredRecurse('repos', cfgv.Array(CONFIG_REPO_DICT)), + cfgv.OptionalRecurse( + 'default_language_version', DEFAULT_LANGUAGE_VERSION, {}, + ), cfgv.Optional('exclude', cfgv.check_regex, '^$'), cfgv.Optional('fail_fast', cfgv.check_bool, False), ) -def is_local_repo(repo_entry): - return repo_entry['repo'] == _LOCAL - - -def is_meta_repo(repo_entry): - return repo_entry['repo'] == _META - - class InvalidConfigError(FatalError): pass diff --git a/pre_commit/commands/autoupdate.py b/pre_commit/commands/autoupdate.py index f75a1924..99e96050 100644 --- a/pre_commit/commands/autoupdate.py +++ b/pre_commit/commands/autoupdate.py @@ -13,10 +13,10 @@ import pre_commit.constants as C from pre_commit import output from pre_commit.clientlib import CONFIG_SCHEMA from pre_commit.clientlib import InvalidManifestError -from pre_commit.clientlib import is_local_repo -from pre_commit.clientlib import is_meta_repo from pre_commit.clientlib import load_config from pre_commit.clientlib import load_manifest +from pre_commit.clientlib import LOCAL +from pre_commit.clientlib import META from pre_commit.commands.migrate_config import migrate_config from pre_commit.util import CalledProcessError from pre_commit.util import cmd_output @@ -123,8 +123,7 @@ def autoupdate(config_file, store, tags_only, repos=()): for repo_config in input_config['repos']: if ( - is_local_repo(repo_config) or - is_meta_repo(repo_config) or + repo_config['repo'] in {LOCAL, META} or # Skip updating any repo_configs that aren't for the specified repo repos and repo_config['repo'] not in repos ): diff --git a/pre_commit/commands/gc.py b/pre_commit/commands/gc.py index 9722643d..65818e50 100644 --- a/pre_commit/commands/gc.py +++ b/pre_commit/commands/gc.py @@ -7,16 +7,16 @@ import pre_commit.constants as C from pre_commit import output from pre_commit.clientlib import InvalidConfigError from pre_commit.clientlib import InvalidManifestError -from pre_commit.clientlib import is_local_repo -from pre_commit.clientlib import is_meta_repo from pre_commit.clientlib import load_config from pre_commit.clientlib import load_manifest +from pre_commit.clientlib import LOCAL +from pre_commit.clientlib import META def _mark_used_repos(store, all_repos, unused_repos, repo): - if is_meta_repo(repo): + if repo['repo'] == META: return - elif is_local_repo(repo): + elif repo['repo'] == LOCAL: for hook in repo['hooks']: deps = hook.get('additional_dependencies') unused_repos.discard(( diff --git a/pre_commit/commands/install_uninstall.py b/pre_commit/commands/install_uninstall.py index a5df9312..4ff2a413 100644 --- a/pre_commit/commands/install_uninstall.py +++ b/pre_commit/commands/install_uninstall.py @@ -6,6 +6,7 @@ import logging import os.path import sys +import pre_commit.constants as C from pre_commit import git from pre_commit import output from pre_commit.clientlib import load_config @@ -51,7 +52,7 @@ def shebang(): py = 'python' else: py = python.get_default_version() - if py == 'default': + if py == C.DEFAULT: py = 'python' return '#!/usr/bin/env {}'.format(py) diff --git a/pre_commit/constants.py b/pre_commit/constants.py index a8cdc2e5..996480a9 100644 --- a/pre_commit/constants.py +++ b/pre_commit/constants.py @@ -22,3 +22,5 @@ VERSION = importlib_metadata.version('pre_commit') # `manual` is not invoked by any installed git hook. See #719 STAGES = ('commit', 'commit-msg', 'manual', 'push') + +DEFAULT = 'default' diff --git a/pre_commit/languages/all.py b/pre_commit/languages/all.py index fecce471..6d85ddf1 100644 --- a/pre_commit/languages/all.py +++ b/pre_commit/languages/all.py @@ -35,8 +35,7 @@ from pre_commit.languages import system # # Args: # prefix - `Prefix` bound to the repository. -# version - A version specified in the hook configuration or -# 'default'. +# version - A version specified in the hook configuration or 'default'. # """ # # def run_hook(hook, file_args): diff --git a/pre_commit/languages/docker.py b/pre_commit/languages/docker.py index e5f3a36b..59a53b4f 100644 --- a/pre_commit/languages/docker.py +++ b/pre_commit/languages/docker.py @@ -4,6 +4,7 @@ from __future__ import unicode_literals import hashlib import os +import pre_commit.constants as C from pre_commit import five from pre_commit.languages import helpers from pre_commit.util import CalledProcessError @@ -62,7 +63,7 @@ def install_environment( assert_docker_available() directory = prefix.path( - helpers.environment_dir(ENVIRONMENT_DIR, 'default'), + helpers.environment_dir(ENVIRONMENT_DIR, C.DEFAULT), ) # Docker doesn't really have relevant disk environment, but pre-commit diff --git a/pre_commit/languages/golang.py b/pre_commit/languages/golang.py index 92d5d36c..e19df88a 100644 --- a/pre_commit/languages/golang.py +++ b/pre_commit/languages/golang.py @@ -4,6 +4,7 @@ import contextlib import os.path import sys +import pre_commit.constants as C from pre_commit import git from pre_commit.envcontext import envcontext from pre_commit.envcontext import Var @@ -27,7 +28,7 @@ def get_env_patch(venv): @contextlib.contextmanager def in_env(prefix): envdir = prefix.path( - helpers.environment_dir(ENVIRONMENT_DIR, 'default'), + helpers.environment_dir(ENVIRONMENT_DIR, C.DEFAULT), ) with envcontext(get_env_patch(envdir)): yield @@ -52,7 +53,7 @@ def guess_go_dir(remote_url): def install_environment(prefix, version, additional_dependencies): helpers.assert_version_default('golang', version) directory = prefix.path( - helpers.environment_dir(ENVIRONMENT_DIR, 'default'), + helpers.environment_dir(ENVIRONMENT_DIR, C.DEFAULT), ) with clean_path_on_failure(directory): diff --git a/pre_commit/languages/helpers.py b/pre_commit/languages/helpers.py index faff1437..0915f410 100644 --- a/pre_commit/languages/helpers.py +++ b/pre_commit/languages/helpers.py @@ -7,10 +7,10 @@ import shlex import six +import pre_commit.constants as C from pre_commit.util import cmd_output from pre_commit.xargs import xargs - FIXED_RANDOM_SEED = 1542676186 @@ -30,7 +30,7 @@ def to_cmd(hook): def assert_version_default(binary, version): - if version != 'default': + if version != C.DEFAULT: raise AssertionError( 'For now, pre-commit requires system-installed {}'.format(binary), ) @@ -45,7 +45,7 @@ def assert_no_additional_deps(lang, additional_deps): def basic_get_default_version(): - return 'default' + return C.DEFAULT def basic_healthy(prefix, language_version): diff --git a/pre_commit/languages/node.py b/pre_commit/languages/node.py index 07f785ea..b313bf5b 100644 --- a/pre_commit/languages/node.py +++ b/pre_commit/languages/node.py @@ -4,6 +4,7 @@ import contextlib import os import sys +import pre_commit.constants as C from pre_commit.envcontext import envcontext from pre_commit.envcontext import Var from pre_commit.languages import helpers @@ -57,7 +58,7 @@ def install_environment(prefix, version, additional_dependencies): cmd = [ sys.executable, '-mnodeenv', '--prebuilt', '--clean-src', envdir, ] - if version != 'default': + if version != C.DEFAULT: cmd.extend(['-n', version]) cmd_output(*cmd) diff --git a/pre_commit/languages/python.py b/pre_commit/languages/python.py index fab5450a..46aa0595 100644 --- a/pre_commit/languages/python.py +++ b/pre_commit/languages/python.py @@ -4,6 +4,7 @@ import contextlib import os import sys +import pre_commit.constants as C from pre_commit.envcontext import envcontext from pre_commit.envcontext import UNSET from pre_commit.envcontext import Var @@ -76,7 +77,7 @@ def _get_default_version(): # pragma: no cover (platform dependent) return exe # We tried! - return 'default' + return C.DEFAULT def get_default_version(): @@ -134,7 +135,7 @@ def py_interface(_dir, _make_venv): env_dir = prefix.path(directory) with clean_path_on_failure(env_dir): - if version != 'default': + if version != C.DEFAULT: python = norm_version(version) else: python = os.path.realpath(sys.executable) diff --git a/pre_commit/languages/ruby.py b/pre_commit/languages/ruby.py index 04a74155..c721b3ce 100644 --- a/pre_commit/languages/ruby.py +++ b/pre_commit/languages/ruby.py @@ -6,6 +6,7 @@ import os.path import shutil import tarfile +import pre_commit.constants as C from pre_commit.envcontext import envcontext from pre_commit.envcontext import Var from pre_commit.languages import helpers @@ -32,7 +33,7 @@ def get_env_patch(venv, language_version): # pragma: windows no cover ), ), ) - if language_version != 'default': + if language_version != C.DEFAULT: patches += (('RBENV_VERSION', language_version),) return patches @@ -52,14 +53,14 @@ def _extract_resource(filename, dest): tf.extractall(dest) -def _install_rbenv(prefix, version='default'): # pragma: windows no cover +def _install_rbenv(prefix, version=C.DEFAULT): # pragma: windows no cover directory = helpers.environment_dir(ENVIRONMENT_DIR, version) _extract_resource('rbenv.tar.gz', prefix.path('.')) shutil.move(prefix.path('rbenv'), prefix.path(directory)) # Only install ruby-build if the version is specified - if version != 'default': + if version != C.DEFAULT: plugins_dir = prefix.path(directory, 'plugins') _extract_resource('ruby-download.tar.gz', plugins_dir) _extract_resource('ruby-build.tar.gz', plugins_dir) @@ -84,7 +85,7 @@ def _install_rbenv(prefix, version='default'): # pragma: windows no cover ) # If we aren't using the system ruby, add a version here - if version != 'default': + if version != C.DEFAULT: activate_file.write('export RBENV_VERSION="{}"\n'.format(version)) @@ -109,7 +110,7 @@ def install_environment( # Need to call this before installing so rbenv's directories are # set up helpers.run_setup_cmd(prefix, ('rbenv', 'init', '-')) - if version != 'default': + if version != C.DEFAULT: _install_ruby(prefix, version) # Need to call this after installing to set up the shims helpers.run_setup_cmd(prefix, ('rbenv', 'rehash')) diff --git a/pre_commit/languages/rust.py b/pre_commit/languages/rust.py index e81fbad2..e09d0078 100644 --- a/pre_commit/languages/rust.py +++ b/pre_commit/languages/rust.py @@ -5,6 +5,7 @@ import os.path import toml +import pre_commit.constants as C from pre_commit.envcontext import envcontext from pre_commit.envcontext import Var from pre_commit.languages import helpers @@ -29,7 +30,7 @@ def get_env_patch(target_dir): @contextlib.contextmanager def in_env(prefix): target_dir = prefix.path( - helpers.environment_dir(ENVIRONMENT_DIR, 'default'), + helpers.environment_dir(ENVIRONMENT_DIR, C.DEFAULT), ) with envcontext(get_env_patch(target_dir)): yield @@ -50,7 +51,7 @@ def _add_dependencies(cargo_toml_path, additional_dependencies): def install_environment(prefix, version, additional_dependencies): helpers.assert_version_default('rust', version) directory = prefix.path( - helpers.environment_dir(ENVIRONMENT_DIR, 'default'), + helpers.environment_dir(ENVIRONMENT_DIR, C.DEFAULT), ) # There are two cases where we might want to specify more dependencies: diff --git a/pre_commit/languages/swift.py b/pre_commit/languages/swift.py index 5841f25e..3f5a92f1 100644 --- a/pre_commit/languages/swift.py +++ b/pre_commit/languages/swift.py @@ -3,6 +3,7 @@ from __future__ import unicode_literals import contextlib import os +import pre_commit.constants as C from pre_commit.envcontext import envcontext from pre_commit.envcontext import Var from pre_commit.languages import helpers @@ -24,7 +25,7 @@ def get_env_patch(venv): # pragma: windows no cover @contextlib.contextmanager def in_env(prefix): # pragma: windows no cover envdir = prefix.path( - helpers.environment_dir(ENVIRONMENT_DIR, 'default'), + helpers.environment_dir(ENVIRONMENT_DIR, C.DEFAULT), ) with envcontext(get_env_patch(envdir)): yield @@ -36,7 +37,7 @@ def install_environment( helpers.assert_version_default('swift', version) helpers.assert_no_additional_deps('swift', additional_dependencies) directory = prefix.path( - helpers.environment_dir(ENVIRONMENT_DIR, 'default'), + helpers.environment_dir(ENVIRONMENT_DIR, C.DEFAULT), ) # Build the swift package diff --git a/pre_commit/repository.py b/pre_commit/repository.py index a654d082..76001fa1 100644 --- a/pre_commit/repository.py +++ b/pre_commit/repository.py @@ -8,10 +8,10 @@ import os import pre_commit.constants as C from pre_commit import five -from pre_commit.clientlib import is_local_repo -from pre_commit.clientlib import is_meta_repo from pre_commit.clientlib import load_manifest +from pre_commit.clientlib import LOCAL from pre_commit.clientlib import MANIFEST_HOOK_DICT +from pre_commit.clientlib import META from pre_commit.languages.all import languages from pre_commit.languages.helpers import environment_dir from pre_commit.prefix import Prefix @@ -111,7 +111,9 @@ class Hook(collections.namedtuple('Hook', ('src', 'prefix') + _KEYS)): return cls(src=src, prefix=prefix, **{k: dct[k] for k in _KEYS}) -def _hook(*hook_dicts): +def _hook(*hook_dicts, **kwargs): + root_config = kwargs.pop('root_config') + assert not kwargs, kwargs ret, rest = dict(hook_dicts[0]), hook_dicts[1:] for dct in rest: ret.update(dct) @@ -127,14 +129,16 @@ def _hook(*hook_dicts): ) exit(1) - if ret['language_version'] == 'default': - language = languages[ret['language']] - ret['language_version'] = language.get_default_version() + lang = ret['language'] + if ret['language_version'] == C.DEFAULT: + ret['language_version'] = root_config['default_language_version'][lang] + if ret['language_version'] == C.DEFAULT: + ret['language_version'] = languages[lang].get_default_version() return ret -def _non_cloned_repository_hooks(repo_config, store): +def _non_cloned_repository_hooks(repo_config, store, root_config): def _prefix(language_name, deps): language = languages[language_name] # pcre / pygrep / script / system / docker_image do not have @@ -148,13 +152,13 @@ def _non_cloned_repository_hooks(repo_config, store): Hook.create( repo_config['repo'], _prefix(hook['language'], hook['additional_dependencies']), - _hook(hook), + _hook(hook, root_config=root_config), ) for hook in repo_config['hooks'] ) -def _cloned_repository_hooks(repo_config, store): +def _cloned_repository_hooks(repo_config, store, root_config): repo, rev = repo_config['repo'], repo_config['rev'] manifest_path = os.path.join(store.clone(repo, rev), C.MANIFEST_FILE) by_id = {hook['id']: hook for hook in load_manifest(manifest_path)} @@ -169,7 +173,10 @@ def _cloned_repository_hooks(repo_config, store): ) exit(1) - hook_dcts = [_hook(by_id[h['id']], h) for h in repo_config['hooks']] + hook_dcts = [ + _hook(by_id[hook['id']], hook, root_config=root_config) + for hook in repo_config['hooks'] + ] return tuple( Hook.create( repo_config['repo'], @@ -180,11 +187,11 @@ def _cloned_repository_hooks(repo_config, store): ) -def repository_hooks(repo_config, store): - if is_local_repo(repo_config) or is_meta_repo(repo_config): - return _non_cloned_repository_hooks(repo_config, store) +def _repository_hooks(repo_config, store, root_config): + if repo_config['repo'] in {LOCAL, META}: + return _non_cloned_repository_hooks(repo_config, store, root_config) else: - return _cloned_repository_hooks(repo_config, store) + return _cloned_repository_hooks(repo_config, store, root_config) def install_hook_envs(hooks, store): @@ -201,17 +208,13 @@ def install_hook_envs(hooks, store): return with store.exclusive_lock(): # Another process may have already completed this work - need_installed = _need_installed() - if not need_installed: # pragma: no cover (race) - return - - for hook in need_installed: + for hook in _need_installed(): hook.install() -def all_hooks(config, store): +def all_hooks(root_config, store): return tuple( hook - for repo in config['repos'] - for hook in repository_hooks(repo, store) + for repo in root_config['repos'] + for hook in _repository_hooks(repo, store, root_config) ) diff --git a/pre_commit/xargs.py b/pre_commit/xargs.py index 3b4a25f9..e2686f0f 100644 --- a/pre_commit/xargs.py +++ b/pre_commit/xargs.py @@ -2,11 +2,11 @@ from __future__ import absolute_import from __future__ import division from __future__ import unicode_literals +import concurrent.futures import contextlib import math import sys -import concurrent.futures import six from pre_commit import parse_shebang diff --git a/tests/clientlib_test.py b/tests/clientlib_test.py index 1f691c2b..fd7f051a 100644 --- a/tests/clientlib_test.py +++ b/tests/clientlib_test.py @@ -7,7 +7,7 @@ from pre_commit.clientlib import check_type_tag from pre_commit.clientlib import CONFIG_HOOK_DICT from pre_commit.clientlib import CONFIG_REPO_DICT from pre_commit.clientlib import CONFIG_SCHEMA -from pre_commit.clientlib import is_local_repo +from pre_commit.clientlib import DEFAULT_LANGUAGE_VERSION from pre_commit.clientlib import MANIFEST_SCHEMA from pre_commit.clientlib import MigrateShaToRev from pre_commit.clientlib import validate_config_main @@ -30,10 +30,6 @@ def test_check_type_tag_failures(value): check_type_tag(value) -def test_is_local_repo(): - assert is_local_repo({'repo': 'local'}) - - @pytest.mark.parametrize( ('args', 'expected_output'), ( @@ -250,6 +246,20 @@ def test_migrate_to_sha_ok(): {'repo': 'meta', 'hooks': [{'id': 'identity', 'name': False}]}, ), ) -def test_meta_hook_invalid_id(config_repo): +def test_meta_hook_invalid(config_repo): with pytest.raises(cfgv.ValidationError): cfgv.validate(config_repo, CONFIG_REPO_DICT) + + +@pytest.mark.parametrize( + 'mapping', + ( + # invalid language key + {'pony': '1.0'}, + # not a string for version + {'python': 3}, + ), +) +def test_default_language_version_invalid(mapping): + with pytest.raises(cfgv.ValidationError): + cfgv.validate(mapping, DEFAULT_LANGUAGE_VERSION) diff --git a/tests/commands/gc_test.py b/tests/commands/gc_test.py index 2a018509..d2528507 100644 --- a/tests/commands/gc_test.py +++ b/tests/commands/gc_test.py @@ -110,10 +110,10 @@ def test_gc_config_with_missing_hook( path = make_repo(tempdir_factory, 'script_hooks_repo') write_config('.', make_config_from_repo(path)) store.mark_config_used(C.CONFIG_FILE) + # to trigger a clone + all_hooks(load_config(C.CONFIG_FILE), store) with modify_config() as config: - # just to trigger a clone - all_hooks(config, store) # add a hook which does not exist, make sure we don't crash config['repos'][0]['hooks'].append({'id': 'does-not-exist'}) diff --git a/tests/commands/install_uninstall_test.py b/tests/commands/install_uninstall_test.py index 2faa1917..608fe385 100644 --- a/tests/commands/install_uninstall_test.py +++ b/tests/commands/install_uninstall_test.py @@ -53,13 +53,13 @@ def test_shebang_windows(): def test_shebang_otherwise(): with mock.patch.object(sys, 'platform', 'posix'): - assert 'default' not in shebang() + assert C.DEFAULT not in shebang() def test_shebang_returns_default(): with mock.patch.object(sys, 'platform', 'posix'): with mock.patch.object( - python, 'get_default_version', return_value='default', + python, 'get_default_version', return_value=C.DEFAULT, ): assert shebang() == '#!/usr/bin/env python' diff --git a/tests/languages/helpers_test.py b/tests/languages/helpers_test.py index 831e0d59..629322c3 100644 --- a/tests/languages/helpers_test.py +++ b/tests/languages/helpers_test.py @@ -8,6 +8,7 @@ import sys import mock import pytest +import pre_commit.constants as C from pre_commit.languages import helpers from pre_commit.prefix import Prefix from pre_commit.util import CalledProcessError @@ -15,7 +16,7 @@ from testing.auto_namedtuple import auto_namedtuple def test_basic_get_default_version(): - assert helpers.basic_get_default_version() == 'default' + assert helpers.basic_get_default_version() == C.DEFAULT def test_basic_healthy(): diff --git a/tests/repository_test.py b/tests/repository_test.py index 25fe2447..d237da2b 100644 --- a/tests/repository_test.py +++ b/tests/repository_test.py @@ -12,7 +12,7 @@ import pytest import pre_commit.constants as C from pre_commit import five from pre_commit import parse_shebang -from pre_commit.clientlib import CONFIG_REPO_DICT +from pre_commit.clientlib import CONFIG_SCHEMA from pre_commit.clientlib import load_manifest from pre_commit.languages import golang from pre_commit.languages import helpers @@ -22,9 +22,9 @@ from pre_commit.languages import python from pre_commit.languages import ruby from pre_commit.languages import rust from pre_commit.prefix import Prefix +from pre_commit.repository import all_hooks from pre_commit.repository import Hook from pre_commit.repository import install_hook_envs -from pre_commit.repository import repository_hooks from pre_commit.util import cmd_output from testing.fixtures import make_config_from_repo from testing.fixtures import make_repo @@ -43,15 +43,21 @@ def _norm_out(b): return b.replace(b'\r\n', b'\n') -def _get_hook(config, store, hook_id): - config = cfgv.validate(config, CONFIG_REPO_DICT) - config = cfgv.apply_defaults(config, CONFIG_REPO_DICT) - hooks = repository_hooks(config, store) - install_hook_envs(hooks, store) +def _get_hook_no_install(repo_config, store, hook_id): + config = {'repos': [repo_config]} + config = cfgv.validate(config, CONFIG_SCHEMA) + config = cfgv.apply_defaults(config, CONFIG_SCHEMA) + hooks = all_hooks(config, store) hook, = [hook for hook in hooks if hook.id == hook_id] return hook +def _get_hook(repo_config, store, hook_id): + hook = _get_hook_no_install(repo_config, store, hook_id) + install_hook_envs([hook], store) + return hook + + def _test_hook_repo( tempdir_factory, store, @@ -81,7 +87,7 @@ def test_python_hook_default_version(tempdir_factory, store): # make sure that this continues to work for platforms where default # language detection does not work with mock.patch.object( - python, 'get_default_version', return_value='default', + python, 'get_default_version', return_value=C.DEFAULT, ): test_python_hook(tempdir_factory, store) @@ -278,7 +284,7 @@ def test_additional_rust_cli_dependencies_installed( config['hooks'][0]['additional_dependencies'] = [dep] hook = _get_hook(config, store, 'rust-hook') binaries = os.listdir(hook.prefix.path( - helpers.environment_dir(rust.ENVIRONMENT_DIR, 'default'), 'bin', + helpers.environment_dir(rust.ENVIRONMENT_DIR, C.DEFAULT), 'bin', )) # normalize for windows binaries = [os.path.splitext(binary)[0] for binary in binaries] @@ -295,7 +301,7 @@ def test_additional_rust_lib_dependencies_installed( config['hooks'][0]['additional_dependencies'] = deps hook = _get_hook(config, store, 'rust-hook') binaries = os.listdir(hook.prefix.path( - helpers.environment_dir(rust.ENVIRONMENT_DIR, 'default'), 'bin', + helpers.environment_dir(rust.ENVIRONMENT_DIR, C.DEFAULT), 'bin', )) # normalize for windows binaries = [os.path.splitext(binary)[0] for binary in binaries] @@ -494,7 +500,7 @@ def test_additional_golang_dependencies_installed( config['hooks'][0]['additional_dependencies'] = deps hook = _get_hook(config, store, 'golang-hook') binaries = os.listdir(hook.prefix.path( - helpers.environment_dir(golang.ENVIRONMENT_DIR, 'default'), 'bin', + helpers.environment_dir(golang.ENVIRONMENT_DIR, C.DEFAULT), 'bin', )) # normalize for windows binaries = [os.path.splitext(binary)[0] for binary in binaries] @@ -588,7 +594,7 @@ def test_control_c_control_c_on_install(tempdir_factory, store): """Regression test for #186.""" path = make_repo(tempdir_factory, 'python_hooks_repo') config = make_config_from_repo(path) - hooks = repository_hooks(config, store) + hooks = [_get_hook_no_install(config, store, 'foo')] class MyKeyboardInterrupt(KeyboardInterrupt): pass @@ -686,22 +692,42 @@ def test_tags_on_repositories(in_tmpdir, tempdir_factory, store): assert ret2[1] == b'bar\nHello World\n' -def test_local_python_repo(store): +@pytest.fixture +def local_python_config(): # Make a "local" hooks repo that just installs our other hooks repo repo_path = get_resource_path('python_hooks_repo') manifest = load_manifest(os.path.join(repo_path, C.MANIFEST_FILE)) hooks = [ dict(hook, additional_dependencies=[repo_path]) for hook in manifest ] - config = {'repo': 'local', 'hooks': hooks} - hook = _get_hook(config, store, 'foo') + return {'repo': 'local', 'hooks': hooks} + + +def test_local_python_repo(store, local_python_config): + hook = _get_hook(local_python_config, store, 'foo') # language_version should have been adjusted to the interpreter version - assert hook.language_version != 'default' + assert hook.language_version != C.DEFAULT ret = hook.run(('filename',)) assert ret[0] == 0 assert _norm_out(ret[1]) == b"['filename']\nHello World\n" +def test_default_language_version(store, local_python_config): + config = { + 'default_language_version': {'python': 'fake'}, + 'repos': [local_python_config], + } + + # `language_version` was not set, should default + hook, = all_hooks(config, store) + assert hook.language_version == 'fake' + + # `language_version` is set, should not default + config['repos'][0]['hooks'][0]['language_version'] = 'fake2' + hook, = all_hooks(config, store) + assert hook.language_version == 'fake2' + + def test_hook_id_not_present(tempdir_factory, store, fake_log_handler): path = make_repo(tempdir_factory, 'script_hooks_repo') config = make_config_from_repo(path) @@ -760,7 +786,7 @@ def test_manifest_hooks(tempdir_factory, store): files='', id='bash_hook', language='script', - language_version='default', + language_version=C.DEFAULT, log_file='', minimum_pre_commit_version='0', name='Bash hook', diff --git a/tests/xargs_test.py b/tests/xargs_test.py index ed65ed46..0e91f9be 100644 --- a/tests/xargs_test.py +++ b/tests/xargs_test.py @@ -2,10 +2,10 @@ from __future__ import absolute_import from __future__ import unicode_literals +import concurrent.futures import sys import time -import concurrent.futures import mock import pytest import six