summaryrefslogtreecommitdiffstats
path: root/pre_commit
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2020-06-14 09:11:24 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2020-06-14 09:11:24 +0000
commit1312af87e5908ac252c0659a60216e96ec5b23bd (patch)
tree7621ad7267ffb9ecd8339983fd83150abc99082f /pre_commit
parentReleasing debian version 2.3.0-1. (diff)
downloadpre-commit-1312af87e5908ac252c0659a60216e96ec5b23bd.tar.xz
pre-commit-1312af87e5908ac252c0659a60216e96ec5b23bd.zip
Merging upstream version 2.5.1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'pre_commit')
-rw-r--r--pre_commit/commands/autoupdate.py12
-rw-r--r--pre_commit/commands/hook_impl.py3
-rw-r--r--pre_commit/commands/migrate_config.py4
-rw-r--r--pre_commit/commands/run.py39
-rw-r--r--pre_commit/constants.py4
-rw-r--r--pre_commit/git.py3
-rw-r--r--pre_commit/languages/all.py4
-rw-r--r--pre_commit/languages/helpers.py4
-rw-r--r--pre_commit/languages/node.py2
-rw-r--r--pre_commit/languages/python.py147
-rw-r--r--pre_commit/languages/python_venv.py46
-rw-r--r--pre_commit/languages/ruby.py2
-rw-r--r--pre_commit/main.py2
-rw-r--r--pre_commit/meta_hooks/check_hooks_apply.py7
-rw-r--r--pre_commit/meta_hooks/check_useless_excludes.py7
-rw-r--r--pre_commit/resources/empty_template_go.mod0
-rw-r--r--pre_commit/staged_files_only.py9
-rw-r--r--pre_commit/store.py9
-rw-r--r--pre_commit/util.py3
19 files changed, 158 insertions, 149 deletions
diff --git a/pre_commit/commands/autoupdate.py b/pre_commit/commands/autoupdate.py
index 8c9fdd7..87f6d53 100644
--- a/pre_commit/commands/autoupdate.py
+++ b/pre_commit/commands/autoupdate.py
@@ -84,7 +84,9 @@ def _check_hooks_still_exist_at_rev(
)
-REV_LINE_RE = re.compile(r'^(\s+)rev:(\s*)([^\s#]+)(.*)(\r?\n)$', re.DOTALL)
+REV_LINE_RE = re.compile(
+ r'^(\s+)rev:(\s*)([\'"]?)([^\s#]+)(.*)(\r?\n)$', re.DOTALL,
+)
def _original_lines(
@@ -116,15 +118,15 @@ def _write_new_config(path: str, rev_infos: List[Optional[RevInfo]]) -> None:
continue
match = REV_LINE_RE.match(lines[idx])
assert match is not None
- new_rev_s = yaml_dump({'rev': rev_info.rev})
+ new_rev_s = yaml_dump({'rev': rev_info.rev}, default_style=match[3])
new_rev = new_rev_s.split(':', 1)[1].strip()
if rev_info.frozen is not None:
comment = f' # frozen: {rev_info.frozen}'
- elif match[4].strip().startswith('# frozen:'):
+ elif match[5].strip().startswith('# frozen:'):
comment = ''
else:
- comment = match[4]
- lines[idx] = f'{match[1]}rev:{match[2]}{new_rev}{comment}{match[5]}'
+ comment = match[5]
+ lines[idx] = f'{match[1]}rev:{match[2]}{new_rev}{comment}{match[6]}'
with open(path, 'w', newline='') as f:
f.write(''.join(lines))
diff --git a/pre_commit/commands/hook_impl.py b/pre_commit/commands/hook_impl.py
index 4843fc7..d0e226f 100644
--- a/pre_commit/commands/hook_impl.py
+++ b/pre_commit/commands/hook_impl.py
@@ -150,6 +150,7 @@ def _pre_push_ns(
_EXPECTED_ARG_LENGTH_BY_HOOK = {
'commit-msg': 1,
'post-checkout': 3,
+ 'post-commit': 0,
'pre-commit': 0,
'pre-merge-commit': 0,
'pre-push': 2,
@@ -186,7 +187,7 @@ def _run_ns(
return _pre_push_ns(color, args, stdin)
elif hook_type in {'commit-msg', 'prepare-commit-msg'}:
return _ns(hook_type, color, commit_msg_filename=args[0])
- elif hook_type in {'pre-merge-commit', 'pre-commit'}:
+ elif hook_type in {'post-commit', 'pre-merge-commit', 'pre-commit'}:
return _ns(hook_type, color)
elif hook_type == 'post-checkout':
return _ns(
diff --git a/pre_commit/commands/migrate_config.py b/pre_commit/commands/migrate_config.py
index d83b8e9..d580ff1 100644
--- a/pre_commit/commands/migrate_config.py
+++ b/pre_commit/commands/migrate_config.py
@@ -2,6 +2,7 @@ import re
import yaml
+from pre_commit.clientlib import load_config
from pre_commit.util import yaml_load
@@ -43,6 +44,9 @@ def _migrate_sha_to_rev(contents: str) -> str:
def migrate_config(config_file: str, quiet: bool = False) -> int:
+ # ensure that the configuration is a valid pre-commit configuration
+ load_config(config_file)
+
with open(config_file) as f:
orig_contents = contents = f.read()
diff --git a/pre_commit/commands/run.py b/pre_commit/commands/run.py
index 8c8401c..567b7cd 100644
--- a/pre_commit/commands/run.py
+++ b/pre_commit/commands/run.py
@@ -72,13 +72,7 @@ def filter_by_include_exclude(
class Classifier:
- def __init__(self, filenames: Sequence[str]) -> None:
- # on windows we normalize all filenames to use forward slashes
- # this makes it easier to filter using the `files:` regex
- # this also makes improperly quoted shell-based hooks work better
- # see #1173
- if os.altsep == '/' and os.sep == '\\':
- filenames = [f.replace(os.sep, os.altsep) for f in filenames]
+ def __init__(self, filenames: Collection[str]) -> None:
self.filenames = [f for f in filenames if os.path.lexists(f)]
@functools.lru_cache(maxsize=None)
@@ -105,6 +99,22 @@ class Classifier:
names = self.by_types(names, hook.types, hook.exclude_types)
return tuple(names)
+ @classmethod
+ def from_config(
+ cls,
+ filenames: Collection[str],
+ include: str,
+ exclude: str,
+ ) -> 'Classifier':
+ # on windows we normalize all filenames to use forward slashes
+ # this makes it easier to filter using the `files:` regex
+ # this also makes improperly quoted shell-based hooks work better
+ # see #1173
+ if os.altsep == '/' and os.sep == '\\':
+ filenames = [f.replace(os.sep, os.altsep) for f in filenames]
+ filenames = filter_by_include_exclude(filenames, include, exclude)
+ return Classifier(filenames)
+
def _get_skips(environ: EnvironT) -> Set[str]:
skips = environ.get('SKIP', '')
@@ -221,7 +231,8 @@ def _compute_cols(hooks: Sequence[Hook]) -> int:
def _all_filenames(args: argparse.Namespace) -> Collection[str]:
- if args.hook_stage == 'post-checkout': # no files for post-checkout
+ # these hooks do not operate on files
+ if args.hook_stage in {'post-checkout', 'post-commit'}:
return ()
elif args.hook_stage in {'prepare-commit-msg', 'commit-msg'}:
return (args.commit_msg_filename,)
@@ -246,10 +257,9 @@ def _run_hooks(
"""Actually run the hooks."""
skips = _get_skips(environ)
cols = _compute_cols(hooks)
- filenames = filter_by_include_exclude(
+ classifier = Classifier.from_config(
_all_filenames(args), config['files'], config['exclude'],
)
- classifier = Classifier(filenames)
retval = 0
for hook in hooks:
retval |= _run_single_hook(
@@ -323,6 +333,12 @@ def run(
f'`--hook-stage {args.hook_stage}`',
)
return 1
+ # prevent recursive post-checkout hooks (#1418)
+ if (
+ args.hook_stage == 'post-checkout' and
+ environ.get('_PRE_COMMIT_SKIP_POST_CHECKOUT')
+ ):
+ return 0
# Expose from-ref / to-ref as environment variables for hooks to consume
if args.from_ref and args.to_ref:
@@ -340,6 +356,9 @@ def run(
if args.checkout_type:
environ['PRE_COMMIT_CHECKOUT_TYPE'] = args.checkout_type
+ # Set pre_commit flag
+ environ['PRE_COMMIT'] = '1'
+
with contextlib.ExitStack() as exit_stack:
if stash:
exit_stack.enter_context(staged_files_only(store.directory))
diff --git a/pre_commit/constants.py b/pre_commit/constants.py
index e2b8e3a..5150fdc 100644
--- a/pre_commit/constants.py
+++ b/pre_commit/constants.py
@@ -17,8 +17,8 @@ VERSION = importlib_metadata.version('pre_commit')
# `manual` is not invoked by any installed git hook. See #719
STAGES = (
- 'commit', 'merge-commit', 'prepare-commit-msg', 'commit-msg', 'manual',
- 'post-checkout', 'push',
+ 'commit', 'merge-commit', 'prepare-commit-msg', 'commit-msg',
+ 'post-commit', 'manual', 'post-checkout', 'push',
)
DEFAULT = 'default'
diff --git a/pre_commit/git.py b/pre_commit/git.py
index 7e757f2..576bef8 100644
--- a/pre_commit/git.py
+++ b/pre_commit/git.py
@@ -158,7 +158,8 @@ def init_repo(path: str, remote: str) -> None:
remote = os.path.abspath(remote)
env = no_git_env()
- cmd_output_b('git', 'init', path, env=env)
+ # avoid the user's template so that hooks do not recurse
+ cmd_output_b('git', 'init', '--template=', path, env=env)
cmd_output_b('git', 'remote', 'add', 'origin', remote, cwd=path, env=env)
diff --git a/pre_commit/languages/all.py b/pre_commit/languages/all.py
index 8f4ffa8..5609631 100644
--- a/pre_commit/languages/all.py
+++ b/pre_commit/languages/all.py
@@ -14,7 +14,6 @@ from pre_commit.languages import node
from pre_commit.languages import perl
from pre_commit.languages import pygrep
from pre_commit.languages import python
-from pre_commit.languages import python_venv
from pre_commit.languages import ruby
from pre_commit.languages import rust
from pre_commit.languages import script
@@ -49,7 +48,6 @@ languages = {
'perl': Language(name='perl', ENVIRONMENT_DIR=perl.ENVIRONMENT_DIR, get_default_version=perl.get_default_version, healthy=perl.healthy, install_environment=perl.install_environment, run_hook=perl.run_hook), # noqa: E501
'pygrep': Language(name='pygrep', ENVIRONMENT_DIR=pygrep.ENVIRONMENT_DIR, get_default_version=pygrep.get_default_version, healthy=pygrep.healthy, install_environment=pygrep.install_environment, run_hook=pygrep.run_hook), # noqa: E501
'python': Language(name='python', ENVIRONMENT_DIR=python.ENVIRONMENT_DIR, get_default_version=python.get_default_version, healthy=python.healthy, install_environment=python.install_environment, run_hook=python.run_hook), # noqa: E501
- 'python_venv': Language(name='python_venv', ENVIRONMENT_DIR=python_venv.ENVIRONMENT_DIR, get_default_version=python_venv.get_default_version, healthy=python_venv.healthy, install_environment=python_venv.install_environment, run_hook=python_venv.run_hook), # noqa: E501
'ruby': Language(name='ruby', ENVIRONMENT_DIR=ruby.ENVIRONMENT_DIR, get_default_version=ruby.get_default_version, healthy=ruby.healthy, install_environment=ruby.install_environment, run_hook=ruby.run_hook), # noqa: E501
'rust': Language(name='rust', ENVIRONMENT_DIR=rust.ENVIRONMENT_DIR, get_default_version=rust.get_default_version, healthy=rust.healthy, install_environment=rust.install_environment, run_hook=rust.run_hook), # noqa: E501
'script': Language(name='script', ENVIRONMENT_DIR=script.ENVIRONMENT_DIR, get_default_version=script.get_default_version, healthy=script.healthy, install_environment=script.install_environment, run_hook=script.run_hook), # noqa: E501
@@ -57,4 +55,6 @@ languages = {
'system': Language(name='system', ENVIRONMENT_DIR=system.ENVIRONMENT_DIR, get_default_version=system.get_default_version, healthy=system.healthy, install_environment=system.install_environment, run_hook=system.run_hook), # noqa: E501
# END GENERATED
}
+# TODO: fully deprecate `python_venv`
+languages['python_venv'] = languages['python']
all_languages = sorted(languages)
diff --git a/pre_commit/languages/helpers.py b/pre_commit/languages/helpers.py
index b5c95e5..01c65ab 100644
--- a/pre_commit/languages/helpers.py
+++ b/pre_commit/languages/helpers.py
@@ -18,7 +18,7 @@ from pre_commit.xargs import xargs
if TYPE_CHECKING:
from typing import NoReturn
-FIXED_RANDOM_SEED = 1542676186
+FIXED_RANDOM_SEED = 1542676187
def run_setup_cmd(prefix: Prefix, cmd: Tuple[str, ...]) -> None:
@@ -92,7 +92,7 @@ def _shuffled(seq: Sequence[str]) -> List[str]:
fixed_random.seed(FIXED_RANDOM_SEED, version=1)
seq = list(seq)
- random.shuffle(seq, random=fixed_random.random)
+ fixed_random.shuffle(seq)
return seq
diff --git a/pre_commit/languages/node.py b/pre_commit/languages/node.py
index 9b636d3..26f4919 100644
--- a/pre_commit/languages/node.py
+++ b/pre_commit/languages/node.py
@@ -79,7 +79,7 @@ def install_environment(
# https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx?f=255&MSPPError=-2147217396#maxpath
if sys.platform == 'win32': # pragma: no cover
- envdir = f'\\\\?\\{os.path.normpath(envdir)}'
+ envdir = fr'\\?\{os.path.normpath(envdir)}'
with clean_path_on_failure(envdir):
cmd = [
sys.executable, '-mnodeenv', '--prebuilt', '--clean-src', envdir,
diff --git a/pre_commit/languages/python.py b/pre_commit/languages/python.py
index 85d8281..6f7c900 100644
--- a/pre_commit/languages/python.py
+++ b/pre_commit/languages/python.py
@@ -2,8 +2,7 @@ import contextlib
import functools
import os
import sys
-from typing import Callable
-from typing import ContextManager
+from typing import Dict
from typing import Generator
from typing import Optional
from typing import Sequence
@@ -26,6 +25,28 @@ from pre_commit.util import cmd_output_b
ENVIRONMENT_DIR = 'py_env'
+@functools.lru_cache(maxsize=None)
+def _version_info(exe: str) -> str:
+ prog = 'import sys;print(".".join(str(p) for p in sys.version_info))'
+ try:
+ return cmd_output(exe, '-S', '-c', prog)[1].strip()
+ except CalledProcessError:
+ return f'<<error retrieving version from {exe}>>'
+
+
+def _read_pyvenv_cfg(filename: str) -> Dict[str, str]:
+ ret = {}
+ with open(filename) as f:
+ for line in f:
+ try:
+ k, v = line.split('=')
+ except ValueError: # blank line / comment / etc.
+ continue
+ else:
+ ret[k.strip()] = v.strip()
+ return ret
+
+
def bin_dir(venv: str) -> str:
"""On windows there's a different directory for the virtualenv"""
bin_part = 'Scripts' if os.name == 'nt' else 'bin'
@@ -34,6 +55,7 @@ def bin_dir(venv: str) -> str:
def get_env_patch(venv: str) -> PatchesT:
return (
+ ('PIP_DISABLE_PIP_VERSION_CHECK', '1'),
('PYTHONHOME', UNSET),
('VIRTUAL_ENV', venv),
('PATH', (bin_dir(venv), os.pathsep, Var('PATH'))),
@@ -45,9 +67,10 @@ def _find_by_py_launcher(
) -> Optional[str]: # pragma: no cover (windows only)
if version.startswith('python'):
num = version[len('python'):]
+ cmd = ('py', f'-{num}', '-c', 'import sys; print(sys.executable)')
+ env = dict(os.environ, PYTHONIOENCODING='UTF-8')
try:
- cmd = ('py', f'-{num}', '-c', 'import sys; print(sys.executable)')
- return cmd_output(*cmd)[1].strip()
+ return cmd_output(*cmd, env=env)[1].strip()
except CalledProcessError:
pass
return None
@@ -115,6 +138,9 @@ def _sys_executable_matches(version: str) -> bool:
def norm_version(version: str) -> str:
+ if version == C.DEFAULT:
+ return os.path.realpath(sys.executable)
+
# first see if our current executable is appropriate
if _sys_executable_matches(version):
return sys.executable
@@ -139,70 +165,59 @@ def norm_version(version: str) -> str:
return os.path.expanduser(version)
-def py_interface(
- _dir: str,
- _make_venv: Callable[[str, str], None],
-) -> Tuple[
- Callable[[Prefix, str], ContextManager[None]],
- Callable[[Prefix, str], bool],
- Callable[[Hook, Sequence[str], bool], Tuple[int, bytes]],
- Callable[[Prefix, str, Sequence[str]], None],
-]:
- @contextlib.contextmanager
- def in_env(
- prefix: Prefix,
- language_version: str,
- ) -> Generator[None, None, None]:
- envdir = prefix.path(helpers.environment_dir(_dir, language_version))
- with envcontext(get_env_patch(envdir)):
- yield
-
- def healthy(prefix: Prefix, language_version: str) -> bool:
- envdir = helpers.environment_dir(_dir, language_version)
- exe_name = 'python.exe' if sys.platform == 'win32' else 'python'
- py_exe = prefix.path(bin_dir(envdir), exe_name)
- with in_env(prefix, language_version):
- retcode, _, _ = cmd_output_b(
- py_exe, '-c', 'import ctypes, datetime, io, os, ssl, weakref',
- cwd='/',
- retcode=None,
- )
- return retcode == 0
-
- def run_hook(
- hook: Hook,
- file_args: Sequence[str],
- color: bool,
- ) -> Tuple[int, bytes]:
- with in_env(hook.prefix, hook.language_version):
- return helpers.run_xargs(hook, hook.cmd, file_args, color=color)
-
- def install_environment(
- prefix: Prefix,
- version: str,
- additional_dependencies: Sequence[str],
- ) -> None:
- directory = helpers.environment_dir(_dir, version)
- install = ('python', '-mpip', 'install', '.', *additional_dependencies)
-
- env_dir = prefix.path(directory)
- with clean_path_on_failure(env_dir):
- if version != C.DEFAULT:
- python = norm_version(version)
- else:
- python = os.path.realpath(sys.executable)
- _make_venv(env_dir, python)
- with in_env(prefix, version):
- helpers.run_setup_cmd(prefix, install)
+@contextlib.contextmanager
+def in_env(
+ prefix: Prefix,
+ language_version: str,
+) -> Generator[None, None, None]:
+ directory = helpers.environment_dir(ENVIRONMENT_DIR, language_version)
+ envdir = prefix.path(directory)
+ with envcontext(get_env_patch(envdir)):
+ yield
- return in_env, healthy, run_hook, install_environment
+def healthy(prefix: Prefix, language_version: str) -> bool:
+ directory = helpers.environment_dir(ENVIRONMENT_DIR, language_version)
+ envdir = prefix.path(directory)
+ pyvenv_cfg = os.path.join(envdir, 'pyvenv.cfg')
-def make_venv(envdir: str, python: str) -> None:
- env = dict(os.environ, VIRTUALENV_NO_DOWNLOAD='1')
- cmd = (sys.executable, '-mvirtualenv', envdir, '-p', python)
- cmd_output_b(*cmd, env=env, cwd='/')
+ # created with "old" virtualenv
+ if not os.path.exists(pyvenv_cfg):
+ return False
+
+ exe_name = 'python.exe' if sys.platform == 'win32' else 'python'
+ py_exe = prefix.path(bin_dir(envdir), exe_name)
+ cfg = _read_pyvenv_cfg(pyvenv_cfg)
+
+ return (
+ 'version_info' in cfg and
+ _version_info(py_exe) == cfg['version_info'] and (
+ 'base-executable' not in cfg or
+ _version_info(cfg['base-executable']) == cfg['version_info']
+ )
+ )
-_interface = py_interface(ENVIRONMENT_DIR, make_venv)
-in_env, healthy, run_hook, install_environment = _interface
+def install_environment(
+ prefix: Prefix,
+ version: str,
+ additional_dependencies: Sequence[str],
+) -> None:
+ envdir = prefix.path(helpers.environment_dir(ENVIRONMENT_DIR, version))
+ python = norm_version(version)
+ venv_cmd = (sys.executable, '-mvirtualenv', envdir, '-p', python)
+ install_cmd = ('python', '-mpip', 'install', '.', *additional_dependencies)
+
+ with clean_path_on_failure(envdir):
+ cmd_output_b(*venv_cmd, cwd='/')
+ with in_env(prefix, version):
+ helpers.run_setup_cmd(prefix, install_cmd)
+
+
+def run_hook(
+ hook: Hook,
+ file_args: Sequence[str],
+ color: bool,
+) -> Tuple[int, bytes]:
+ with in_env(hook.prefix, hook.language_version):
+ return helpers.run_xargs(hook, hook.cmd, file_args, color=color)
diff --git a/pre_commit/languages/python_venv.py b/pre_commit/languages/python_venv.py
deleted file mode 100644
index 5404c8b..0000000
--- a/pre_commit/languages/python_venv.py
+++ /dev/null
@@ -1,46 +0,0 @@
-import os.path
-
-from pre_commit.languages import python
-from pre_commit.util import CalledProcessError
-from pre_commit.util import cmd_output
-from pre_commit.util import cmd_output_b
-
-ENVIRONMENT_DIR = 'py_venv'
-get_default_version = python.get_default_version
-
-
-def orig_py_exe(exe: str) -> str: # pragma: no cover (platform specific)
- """A -mvenv virtualenv made from a -mvirtualenv virtualenv installs
- packages to the incorrect location. Attempt to find the _original_ exe
- and invoke `-mvenv` from there.
-
- See:
- - https://github.com/pre-commit/pre-commit/issues/755
- - https://github.com/pypa/virtualenv/issues/1095
- - https://bugs.python.org/issue30811
- """
- try:
- prefix_script = 'import sys; print(sys.real_prefix)'
- _, prefix, _ = cmd_output(exe, '-c', prefix_script)
- prefix = prefix.strip()
- except CalledProcessError:
- # not created from -mvirtualenv
- return exe
-
- if os.name == 'nt':
- expected = os.path.join(prefix, 'python.exe')
- else:
- expected = os.path.join(prefix, 'bin', os.path.basename(exe))
-
- if os.path.exists(expected):
- return expected
- else:
- return exe
-
-
-def make_venv(envdir: str, python: str) -> None:
- cmd_output_b(orig_py_exe(python), '-mvenv', envdir, cwd='/')
-
-
-_interface = python.py_interface(ENVIRONMENT_DIR, make_venv)
-in_env, healthy, run_hook, install_environment = _interface
diff --git a/pre_commit/languages/ruby.py b/pre_commit/languages/ruby.py
index 61241f8..fe524ec 100644
--- a/pre_commit/languages/ruby.py
+++ b/pre_commit/languages/ruby.py
@@ -9,6 +9,7 @@ from typing import Tuple
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
@@ -28,6 +29,7 @@ def get_env_patch(
) -> PatchesT: # pragma: win32 no cover
patches: PatchesT = (
('GEM_HOME', os.path.join(venv, 'gems')),
+ ('GEM_PATH', UNSET),
('RBENV_ROOT', venv),
('BUNDLE_IGNORE_CONFIG', '1'),
(
diff --git a/pre_commit/main.py b/pre_commit/main.py
index 790b347..874eb53 100644
--- a/pre_commit/main.py
+++ b/pre_commit/main.py
@@ -79,7 +79,7 @@ def _add_hook_type_option(parser: argparse.ArgumentParser) -> None:
parser.add_argument(
'-t', '--hook-type', choices=(
'pre-commit', 'pre-merge-commit', 'pre-push',
- 'prepare-commit-msg', 'commit-msg', 'post-checkout',
+ 'prepare-commit-msg', 'commit-msg', 'post-commit', 'post-checkout',
),
action=AppendReplaceDefault,
default=['pre-commit'],
diff --git a/pre_commit/meta_hooks/check_hooks_apply.py b/pre_commit/meta_hooks/check_hooks_apply.py
index d0244a9..a1e9352 100644
--- a/pre_commit/meta_hooks/check_hooks_apply.py
+++ b/pre_commit/meta_hooks/check_hooks_apply.py
@@ -11,10 +11,13 @@ from pre_commit.store import Store
def check_all_hooks_match_files(config_file: str) -> int:
- classifier = Classifier(git.get_all_files())
+ config = load_config(config_file)
+ classifier = Classifier.from_config(
+ git.get_all_files(), config['files'], config['exclude'],
+ )
retv = 0
- for hook in all_hooks(load_config(config_file), Store()):
+ for hook in all_hooks(config, Store()):
if hook.always_run or hook.language == 'fail':
continue
elif not classifier.filenames_for_hook(hook):
diff --git a/pre_commit/meta_hooks/check_useless_excludes.py b/pre_commit/meta_hooks/check_useless_excludes.py
index 30b8d81..db6865c 100644
--- a/pre_commit/meta_hooks/check_useless_excludes.py
+++ b/pre_commit/meta_hooks/check_useless_excludes.py
@@ -28,11 +28,14 @@ def exclude_matches_any(
def check_useless_excludes(config_file: str) -> int:
config = load_config(config_file)
- classifier = Classifier(git.get_all_files())
+ filenames = git.get_all_files()
+ classifier = Classifier.from_config(
+ filenames, config['files'], config['exclude'],
+ )
retv = 0
exclude = config['exclude']
- if not exclude_matches_any(classifier.filenames, '', exclude):
+ if not exclude_matches_any(filenames, '', exclude):
print(
f'The global exclude pattern {exclude!r} does not match any files',
)
diff --git a/pre_commit/resources/empty_template_go.mod b/pre_commit/resources/empty_template_go.mod
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/pre_commit/resources/empty_template_go.mod
diff --git a/pre_commit/staged_files_only.py b/pre_commit/staged_files_only.py
index 09d323d..6179301 100644
--- a/pre_commit/staged_files_only.py
+++ b/pre_commit/staged_files_only.py
@@ -56,8 +56,10 @@ def _unstaged_changes_cleared(patch_dir: str) -> Generator[None, None, None]:
with open(patch_filename, 'wb') as patch_file:
patch_file.write(diff_stdout_binary)
- # Clear the working directory of unstaged changes
- cmd_output_b('git', 'checkout', '--', '.')
+ # prevent recursive post-checkout hooks (#1418)
+ no_checkout_env = dict(os.environ, _PRE_COMMIT_SKIP_POST_CHECKOUT='1')
+ cmd_output_b('git', 'checkout', '--', '.', env=no_checkout_env)
+
try:
yield
finally:
@@ -72,8 +74,9 @@ def _unstaged_changes_cleared(patch_dir: str) -> Generator[None, None, None]:
# We failed to apply the patch, presumably due to fixes made
# by hooks.
# Roll back the changes made by hooks.
- cmd_output_b('git', 'checkout', '--', '.')
+ cmd_output_b('git', 'checkout', '--', '.', env=no_checkout_env)
_git_apply(patch_filename)
+
logger.info(f'Restored changes from {patch_filename}.')
else:
# There weren't any staged files so we don't need to do anything
diff --git a/pre_commit/store.py b/pre_commit/store.py
index 760b37a..6d8c40a 100644
--- a/pre_commit/store.py
+++ b/pre_commit/store.py
@@ -30,10 +30,11 @@ def _get_default_directory() -> str:
`Store.get_default_directory` can be mocked in tests and
`_get_default_directory` can be tested.
"""
- return os.environ.get('PRE_COMMIT_HOME') or os.path.join(
+ ret = os.environ.get('PRE_COMMIT_HOME') or os.path.join(
os.environ.get('XDG_CACHE_HOME') or os.path.expanduser('~/.cache'),
'pre-commit',
)
+ return os.path.realpath(ret)
class Store:
@@ -182,9 +183,9 @@ class Store:
return self._new_repo(repo, ref, deps, clone_strategy)
LOCAL_RESOURCES = (
- 'Cargo.toml', 'main.go', 'main.rs', '.npmignore', 'package.json',
- 'pre_commit_dummy_package.gemspec', 'setup.py', 'environment.yml',
- 'Makefile.PL',
+ 'Cargo.toml', 'main.go', 'go.mod', 'main.rs', '.npmignore',
+ 'package.json', 'pre_commit_dummy_package.gemspec', 'setup.py',
+ 'environment.yml', 'Makefile.PL',
)
def make_local(self, deps: Sequence[str]) -> str:
diff --git a/pre_commit/util.py b/pre_commit/util.py
index 2db579a..0338b37 100644
--- a/pre_commit/util.py
+++ b/pre_commit/util.py
@@ -36,10 +36,11 @@ yaml_load = functools.partial(yaml.load, Loader=Loader)
Dumper = getattr(yaml, 'CSafeDumper', yaml.SafeDumper)
-def yaml_dump(o: Any) -> str:
+def yaml_dump(o: Any, **kwargs: Any) -> str:
# when python/mypy#1484 is solved, this can be `functools.partial`
return yaml.dump(
o, Dumper=Dumper, default_flow_style=False, indent=4, sort_keys=False,
+ **kwargs,
)