move Hook data type to a separate file

This commit is contained in:
Anthony Sottile
2020-01-14 16:07:50 -08:00
parent 5a62501307
commit 755b8000f6
19 changed files with 139 additions and 163 deletions

View File

@@ -20,8 +20,9 @@ from pre_commit import color
from pre_commit import git
from pre_commit import output
from pre_commit.clientlib import load_config
from pre_commit.hook import Hook
from pre_commit.languages.all import languages
from pre_commit.repository import all_hooks
from pre_commit.repository import Hook
from pre_commit.repository import install_hook_envs
from pre_commit.staged_files_only import staged_files_only
from pre_commit.store import Store
@@ -160,7 +161,8 @@ def _run_single_hook(
if not hook.pass_filenames:
filenames = ()
time_before = time.time()
retcode, out = hook.run(filenames, use_color)
language = languages[hook.language]
retcode, out = language.run_hook(hook, filenames, use_color)
duration = round(time.time() - time_before, 2) or 0
diff_after = cmd_output_b(*diff_cmd, retcode=None)

63
pre_commit/hook.py Normal file
View File

@@ -0,0 +1,63 @@
import logging
import shlex
from typing import Any
from typing import Dict
from typing import NamedTuple
from typing import Sequence
from typing import Tuple
from pre_commit.prefix import Prefix
logger = logging.getLogger('pre_commit')
class Hook(NamedTuple):
src: str
prefix: Prefix
id: str
name: str
entry: str
language: str
alias: str
files: str
exclude: str
types: Sequence[str]
exclude_types: Sequence[str]
additional_dependencies: Sequence[str]
args: Sequence[str]
always_run: bool
pass_filenames: bool
description: str
language_version: str
log_file: str
minimum_pre_commit_version: str
require_serial: bool
stages: Sequence[str]
verbose: bool
@property
def cmd(self) -> Tuple[str, ...]:
return (*shlex.split(self.entry), *self.args)
@property
def install_key(self) -> Tuple[Prefix, str, str, Tuple[str, ...]]:
return (
self.prefix,
self.language,
self.language_version,
tuple(self.additional_dependencies),
)
@classmethod
def create(cls, src: str, prefix: Prefix, dct: Dict[str, Any]) -> 'Hook':
# TODO: have cfgv do this (?)
extra_keys = set(dct) - _KEYS
if extra_keys:
logger.warning(
f'Unexpected key(s) present on {src} => {dct["id"]}: '
f'{", ".join(sorted(extra_keys))}',
)
return cls(src=src, prefix=prefix, **{k: dct[k] for k in _KEYS})
_KEYS = frozenset(set(Hook._fields) - {'src', 'prefix'})

View File

@@ -3,8 +3,8 @@ from typing import NamedTuple
from typing import Optional
from typing import Sequence
from typing import Tuple
from typing import TYPE_CHECKING
from pre_commit.hook import Hook
from pre_commit.languages import conda
from pre_commit.languages import docker
from pre_commit.languages import docker_image
@@ -21,9 +21,6 @@ from pre_commit.languages import swift
from pre_commit.languages import system
from pre_commit.prefix import Prefix
if TYPE_CHECKING:
from pre_commit.repository import Hook
class Language(NamedTuple):
name: str

View File

@@ -3,21 +3,18 @@ import os
from typing import Generator
from typing import Sequence
from typing import Tuple
from typing import TYPE_CHECKING
from pre_commit.envcontext import envcontext
from pre_commit.envcontext import PatchesT
from pre_commit.envcontext import SubstitutionT
from pre_commit.envcontext import UNSET
from pre_commit.envcontext import Var
from pre_commit.hook import Hook
from pre_commit.languages import helpers
from pre_commit.prefix import Prefix
from pre_commit.util import clean_path_on_failure
from pre_commit.util import cmd_output_b
if TYPE_CHECKING:
from pre_commit.repository import Hook
ENVIRONMENT_DIR = 'conda'
get_default_version = helpers.basic_get_default_version
healthy = helpers.basic_healthy

View File

@@ -2,18 +2,15 @@ import hashlib
import os
from typing import Sequence
from typing import Tuple
from typing import TYPE_CHECKING
import pre_commit.constants as C
from pre_commit.hook import Hook
from pre_commit.languages import helpers
from pre_commit.prefix import Prefix
from pre_commit.util import CalledProcessError
from pre_commit.util import clean_path_on_failure
from pre_commit.util import cmd_output_b
if TYPE_CHECKING:
from pre_commit.repository import Hook
ENVIRONMENT_DIR = 'docker'
PRE_COMMIT_LABEL = 'PRE_COMMIT'
get_default_version = helpers.basic_get_default_version

View File

@@ -1,14 +1,11 @@
from typing import Sequence
from typing import Tuple
from typing import TYPE_CHECKING
from pre_commit.hook import Hook
from pre_commit.languages import helpers
from pre_commit.languages.docker import assert_docker_available
from pre_commit.languages.docker import docker_cmd
if TYPE_CHECKING:
from pre_commit.repository import Hook
ENVIRONMENT_DIR = None
get_default_version = helpers.basic_get_default_version
healthy = helpers.basic_healthy

View File

@@ -1,12 +1,9 @@
from typing import Sequence
from typing import Tuple
from typing import TYPE_CHECKING
from pre_commit.hook import Hook
from pre_commit.languages import helpers
if TYPE_CHECKING:
from pre_commit.repository import Hook
ENVIRONMENT_DIR = None
get_default_version = helpers.basic_get_default_version
healthy = helpers.basic_healthy

View File

@@ -4,13 +4,13 @@ import sys
from typing import Generator
from typing import Sequence
from typing import Tuple
from typing import TYPE_CHECKING
import pre_commit.constants as C
from pre_commit import git
from pre_commit.envcontext import envcontext
from pre_commit.envcontext import PatchesT
from pre_commit.envcontext import Var
from pre_commit.hook import Hook
from pre_commit.languages import helpers
from pre_commit.prefix import Prefix
from pre_commit.util import clean_path_on_failure
@@ -18,9 +18,6 @@ from pre_commit.util import cmd_output
from pre_commit.util import cmd_output_b
from pre_commit.util import rmtree
if TYPE_CHECKING:
from pre_commit.repository import Hook
ENVIRONMENT_DIR = 'golangenv'
get_default_version = helpers.basic_get_default_version
healthy = helpers.basic_healthy

View File

@@ -8,16 +8,13 @@ from typing import Optional
from typing import overload
from typing import Sequence
from typing import Tuple
from typing import TYPE_CHECKING
import pre_commit.constants as C
from pre_commit.hook import Hook
from pre_commit.prefix import Prefix
from pre_commit.util import cmd_output_b
from pre_commit.xargs import xargs
if TYPE_CHECKING:
from pre_commit.repository import Hook
FIXED_RANDOM_SEED = 1542676186

View File

@@ -4,12 +4,12 @@ import sys
from typing import Generator
from typing import Sequence
from typing import Tuple
from typing import TYPE_CHECKING
import pre_commit.constants as C
from pre_commit.envcontext import envcontext
from pre_commit.envcontext import PatchesT
from pre_commit.envcontext import Var
from pre_commit.hook import Hook
from pre_commit.languages import helpers
from pre_commit.languages.python import bin_dir
from pre_commit.prefix import Prefix
@@ -17,9 +17,6 @@ from pre_commit.util import clean_path_on_failure
from pre_commit.util import cmd_output
from pre_commit.util import cmd_output_b
if TYPE_CHECKING:
from pre_commit.repository import Hook
ENVIRONMENT_DIR = 'node_env'
get_default_version = helpers.basic_get_default_version
healthy = helpers.basic_healthy

View File

@@ -5,15 +5,12 @@ from typing import Optional
from typing import Pattern
from typing import Sequence
from typing import Tuple
from typing import TYPE_CHECKING
from pre_commit import output
from pre_commit.hook import Hook
from pre_commit.languages import helpers
from pre_commit.xargs import xargs
if TYPE_CHECKING:
from pre_commit.repository import Hook
ENVIRONMENT_DIR = None
get_default_version = helpers.basic_get_default_version
healthy = helpers.basic_healthy

View File

@@ -8,13 +8,13 @@ from typing import Generator
from typing import Optional
from typing import Sequence
from typing import Tuple
from typing import TYPE_CHECKING
import pre_commit.constants as C
from pre_commit.envcontext import envcontext
from pre_commit.envcontext import PatchesT
from pre_commit.envcontext import UNSET
from pre_commit.envcontext import Var
from pre_commit.hook import Hook
from pre_commit.languages import helpers
from pre_commit.parse_shebang import find_executable
from pre_commit.prefix import Prefix
@@ -23,9 +23,6 @@ from pre_commit.util import clean_path_on_failure
from pre_commit.util import cmd_output
from pre_commit.util import cmd_output_b
if TYPE_CHECKING:
from pre_commit.repository import Hook
ENVIRONMENT_DIR = 'py_env'

View File

@@ -5,21 +5,18 @@ import tarfile
from typing import Generator
from typing import Sequence
from typing import Tuple
from typing import TYPE_CHECKING
import pre_commit.constants as C
from pre_commit.envcontext import envcontext
from pre_commit.envcontext import PatchesT
from pre_commit.envcontext import Var
from pre_commit.hook import Hook
from pre_commit.languages import helpers
from pre_commit.prefix import Prefix
from pre_commit.util import CalledProcessError
from pre_commit.util import clean_path_on_failure
from pre_commit.util import resource_bytesio
if TYPE_CHECKING:
from pre_comit.repository import Hook
ENVIRONMENT_DIR = 'rbenv'
get_default_version = helpers.basic_get_default_version
healthy = helpers.basic_healthy

View File

@@ -4,7 +4,6 @@ from typing import Generator
from typing import Sequence
from typing import Set
from typing import Tuple
from typing import TYPE_CHECKING
import toml
@@ -12,14 +11,12 @@ import pre_commit.constants as C
from pre_commit.envcontext import envcontext
from pre_commit.envcontext import PatchesT
from pre_commit.envcontext import Var
from pre_commit.hook import Hook
from pre_commit.languages import helpers
from pre_commit.prefix import Prefix
from pre_commit.util import clean_path_on_failure
from pre_commit.util import cmd_output_b
if TYPE_CHECKING:
from pre_commit.repository import Hook
ENVIRONMENT_DIR = 'rustenv'
get_default_version = helpers.basic_get_default_version
healthy = helpers.basic_healthy

View File

@@ -1,12 +1,9 @@
from typing import Sequence
from typing import Tuple
from typing import TYPE_CHECKING
from pre_commit.hook import Hook
from pre_commit.languages import helpers
if TYPE_CHECKING:
from pre_commit.repository import Hook
ENVIRONMENT_DIR = None
get_default_version = helpers.basic_get_default_version
healthy = helpers.basic_healthy

View File

@@ -3,20 +3,17 @@ import os
from typing import Generator
from typing import Sequence
from typing import Tuple
from typing import TYPE_CHECKING
import pre_commit.constants as C
from pre_commit.envcontext import envcontext
from pre_commit.envcontext import PatchesT
from pre_commit.envcontext import Var
from pre_commit.hook import Hook
from pre_commit.languages import helpers
from pre_commit.prefix import Prefix
from pre_commit.util import clean_path_on_failure
from pre_commit.util import cmd_output_b
if TYPE_CHECKING:
from pre_commit.repository import Hook
ENVIRONMENT_DIR = 'swift_env'
get_default_version = helpers.basic_get_default_version
healthy = helpers.basic_healthy

View File

@@ -1,12 +1,9 @@
from typing import Sequence
from typing import Tuple
from typing import TYPE_CHECKING
from pre_commit.hook import Hook
from pre_commit.languages import helpers
if TYPE_CHECKING:
from pre_commit.repository import Hook
ENVIRONMENT_DIR = None
get_default_version = helpers.basic_get_default_version

View File

@@ -1,11 +1,9 @@
import json
import logging
import os
import shlex
from typing import Any
from typing import Dict
from typing import List
from typing import NamedTuple
from typing import Optional
from typing import Sequence
from typing import Set
@@ -14,8 +12,8 @@ from typing import Tuple
import pre_commit.constants as C
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.hook import Hook
from pre_commit.languages.all import languages
from pre_commit.languages.helpers import environment_dir
from pre_commit.prefix import Prefix
@@ -53,93 +51,39 @@ def _write_state(prefix: Prefix, venv: str, state: object) -> None:
os.rename(staging, state_filename)
_KEYS = tuple(item.key for item in MANIFEST_HOOK_DICT.items)
class Hook(NamedTuple):
src: str
prefix: Prefix
id: str
name: str
entry: str
language: str
alias: str
files: str
exclude: str
types: Sequence[str]
exclude_types: Sequence[str]
additional_dependencies: Sequence[str]
args: Sequence[str]
always_run: bool
pass_filenames: bool
description: str
language_version: str
log_file: str
minimum_pre_commit_version: str
require_serial: bool
stages: Sequence[str]
verbose: bool
@property
def cmd(self) -> Tuple[str, ...]:
return tuple(shlex.split(self.entry)) + tuple(self.args)
@property
def install_key(self) -> Tuple[Prefix, str, str, Tuple[str, ...]]:
return (
self.prefix,
self.language,
self.language_version,
tuple(self.additional_dependencies),
def _hook_installed(hook: Hook) -> bool:
lang = languages[hook.language]
venv = environment_dir(lang.ENVIRONMENT_DIR, hook.language_version)
return (
venv is None or (
(
_read_state(hook.prefix, venv) ==
_state(hook.additional_dependencies)
) and
lang.healthy(hook.prefix, hook.language_version)
)
)
def installed(self) -> bool:
lang = languages[self.language]
venv = environment_dir(lang.ENVIRONMENT_DIR, self.language_version)
return (
venv is None or (
(
_read_state(self.prefix, venv) ==
_state(self.additional_dependencies)
) and
lang.healthy(self.prefix, self.language_version)
)
)
def install(self) -> None:
logger.info(f'Installing environment for {self.src}.')
logger.info('Once installed this environment will be reused.')
logger.info('This may take a few minutes...')
def _hook_install(hook: Hook) -> None:
logger.info(f'Installing environment for {hook.src}.')
logger.info('Once installed this environment will be reused.')
logger.info('This may take a few minutes...')
lang = languages[self.language]
assert lang.ENVIRONMENT_DIR is not None
venv = environment_dir(lang.ENVIRONMENT_DIR, self.language_version)
lang = languages[hook.language]
assert lang.ENVIRONMENT_DIR is not None
venv = environment_dir(lang.ENVIRONMENT_DIR, hook.language_version)
# There's potentially incomplete cleanup from previous runs
# Clean it up!
if self.prefix.exists(venv):
rmtree(self.prefix.path(venv))
# There's potentially incomplete cleanup from previous runs
# Clean it up!
if hook.prefix.exists(venv):
rmtree(hook.prefix.path(venv))
lang.install_environment(
self.prefix, self.language_version, self.additional_dependencies,
)
# Write our state to indicate we're installed
_write_state(self.prefix, venv, _state(self.additional_dependencies))
def run(self, file_args: Sequence[str], color: bool) -> Tuple[int, bytes]:
lang = languages[self.language]
return lang.run_hook(self, file_args, color)
@classmethod
def create(cls, src: str, prefix: Prefix, dct: Dict[str, Any]) -> 'Hook':
# TODO: have cfgv do this (?)
extra_keys = set(dct) - set(_KEYS)
if extra_keys:
logger.warning(
f'Unexpected key(s) present on {src} => {dct["id"]}: '
f'{", ".join(sorted(extra_keys))}',
)
return cls(src=src, prefix=prefix, **{k: dct[k] for k in _KEYS})
lang.install_environment(
hook.prefix, hook.language_version, hook.additional_dependencies,
)
# Write our state to indicate we're installed
_write_state(hook.prefix, venv, _state(hook.additional_dependencies))
def _hook(
@@ -243,7 +187,7 @@ def install_hook_envs(hooks: Sequence[Hook], store: Store) -> None:
seen: Set[Tuple[Prefix, str, str, Tuple[str, ...]]] = set()
ret = []
for hook in hooks:
if hook.install_key not in seen and not hook.installed():
if hook.install_key not in seen and not _hook_installed(hook):
ret.append(hook)
seen.add(hook.install_key)
return ret
@@ -253,7 +197,7 @@ def install_hook_envs(hooks: Sequence[Hook], store: Store) -> None:
with store.exclusive_lock():
# Another process may have already completed this work
for hook in _need_installed():
hook.install()
_hook_install(hook)
def all_hooks(root_config: Dict[str, Any], store: Store) -> Tuple[Hook, ...]:

View File

@@ -13,15 +13,16 @@ import pre_commit.constants as C
from pre_commit.clientlib import CONFIG_SCHEMA
from pre_commit.clientlib import load_manifest
from pre_commit.envcontext import envcontext
from pre_commit.hook import Hook
from pre_commit.languages import golang
from pre_commit.languages import helpers
from pre_commit.languages import node
from pre_commit.languages import python
from pre_commit.languages import ruby
from pre_commit.languages import rust
from pre_commit.languages.all import languages
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.util import cmd_output
from pre_commit.util import cmd_output_b
@@ -40,6 +41,10 @@ def _norm_out(b):
return b.replace(b'\r\n', b'\n')
def _hook_run(hook, filenames, color):
return languages[hook.language].run_hook(hook, filenames, color)
def _get_hook_no_install(repo_config, store, hook_id):
config = {'repos': [repo_config]}
config = cfgv.validate(config, CONFIG_SCHEMA)
@@ -68,7 +73,8 @@ def _test_hook_repo(
):
path = make_repo(tempdir_factory, repo_path)
config = make_config_from_repo(path, **(config_kwargs or {}))
ret, out = _get_hook(config, store, hook_id).run(args, color=color)
hook = _get_hook(config, store, hook_id)
ret, out = _hook_run(hook, args, color=color)
assert ret == expected_return_code
assert _norm_out(out) == expected
@@ -108,7 +114,8 @@ def test_local_conda_additional_dependencies(store):
'additional_dependencies': ['mccabe'],
}],
}
ret, out = _get_hook(config, store, 'local-conda').run((), color=False)
hook = _get_hook(config, store, 'local-conda')
ret, out = _hook_run(hook, (), color=False)
assert ret == 0
assert _norm_out(out) == b'OK\n'
@@ -173,7 +180,7 @@ def test_switch_language_versions_doesnt_clobber(tempdir_factory, store):
config = make_config_from_repo(path)
config['hooks'][0]['language_version'] = version
hook = _get_hook(config, store, 'python3-hook')
ret, out = hook.run([], color=False)
ret, out = _hook_run(hook, [], color=False)
assert ret == 0
assert _norm_out(out) == expected_output
@@ -445,14 +452,14 @@ def greppable_files(tmpdir):
def test_grep_hook_matching(greppable_files, store):
hook = _make_grep_repo('ello', store)
ret, out = hook.run(('f1', 'f2', 'f3'), color=False)
ret, out = _hook_run(hook, ('f1', 'f2', 'f3'), color=False)
assert ret == 1
assert _norm_out(out) == b"f1:1:hello'hi\n"
def test_grep_hook_case_insensitive(greppable_files, store):
hook = _make_grep_repo('ELLO', store, args=['-i'])
ret, out = hook.run(('f1', 'f2', 'f3'), color=False)
ret, out = _hook_run(hook, ('f1', 'f2', 'f3'), color=False)
assert ret == 1
assert _norm_out(out) == b"f1:1:hello'hi\n"
@@ -460,7 +467,7 @@ def test_grep_hook_case_insensitive(greppable_files, store):
@pytest.mark.parametrize('regex', ('nope', "foo'bar", r'^\[INFO\]'))
def test_grep_hook_not_matching(regex, greppable_files, store):
hook = _make_grep_repo(regex, store)
ret, out = hook.run(('f1', 'f2', 'f3'), color=False)
ret, out = _hook_run(hook, ('f1', 'f2', 'f3'), color=False)
assert (ret, out) == (0, b'')
@@ -559,7 +566,8 @@ def test_local_golang_additional_dependencies(store):
'additional_dependencies': ['github.com/golang/example/hello'],
}],
}
ret, out = _get_hook(config, store, 'hello').run((), color=False)
hook = _get_hook(config, store, 'hello')
ret, out = _hook_run(hook, (), color=False)
assert ret == 0
assert _norm_out(out) == b'Hello, Go examples!\n'
@@ -575,7 +583,8 @@ def test_local_rust_additional_dependencies(store):
'additional_dependencies': ['cli:hello-cli:0.2.2'],
}],
}
ret, out = _get_hook(config, store, 'hello').run((), color=False)
hook = _get_hook(config, store, 'hello')
ret, out = _hook_run(hook, (), color=False)
assert ret == 0
assert _norm_out(out) == b'Hello World!\n'
@@ -592,7 +601,9 @@ def test_fail_hooks(store):
}],
}
hook = _get_hook(config, store, 'fail')
ret, out = hook.run(('changelog/123.bugfix', 'changelog/wat'), color=False)
ret, out = _hook_run(
hook, ('changelog/123.bugfix', 'changelog/wat'), color=False,
)
assert ret == 1
assert out == (
b'make sure to name changelogs as .rst!\n'
@@ -661,7 +672,7 @@ def test_control_c_control_c_on_install(tempdir_factory, store):
# However, it should be perfectly runnable (reinstall after botched
# install)
install_hook_envs(hooks, store)
ret, out = hook.run((), color=False)
ret, out = _hook_run(hook, (), color=False)
assert ret == 0
@@ -683,7 +694,8 @@ def test_invalidated_virtualenv(tempdir_factory, store):
cmd_output_b('rm', '-rf', *paths)
# pre-commit should rebuild the virtualenv and it should be runnable
ret, out = _get_hook(config, store, 'foo').run((), color=False)
hook = _get_hook(config, store, 'foo')
ret, out = _hook_run(hook, (), color=False)
assert ret == 0
@@ -724,13 +736,13 @@ def test_tags_on_repositories(in_tmpdir, tempdir_factory, store):
config1 = make_config_from_repo(git1, rev=tag)
hook1 = _get_hook(config1, store, 'prints_cwd')
ret1, out1 = hook1.run(('-L',), color=False)
ret1, out1 = _hook_run(hook1, ('-L',), color=False)
assert ret1 == 0
assert out1.strip() == _norm_pwd(in_tmpdir)
config2 = make_config_from_repo(git2, rev=tag)
hook2 = _get_hook(config2, store, 'bash_hook')
ret2, out2 = hook2.run(('bar',), color=False)
ret2, out2 = _hook_run(hook2, ('bar',), color=False)
assert ret2 == 0
assert out2 == b'bar\nHello World\n'
@@ -754,7 +766,7 @@ 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 != C.DEFAULT
ret, out = hook.run(('filename',), color=False)
ret, out = _hook_run(hook, ('filename',), color=False)
assert ret == 0
assert _norm_out(out) == b"['filename']\nHello World\n"