summaryrefslogtreecommitdiffstats
path: root/pre_commit
diff options
context:
space:
mode:
Diffstat (limited to 'pre_commit')
-rw-r--r--pre_commit/clientlib.py2
-rw-r--r--pre_commit/commands/install_uninstall.py2
-rw-r--r--pre_commit/commands/run.py8
-rw-r--r--pre_commit/commands/sample_config.py2
-rw-r--r--pre_commit/envcontext.py9
-rw-r--r--pre_commit/error_handler.py24
-rw-r--r--pre_commit/errors.py2
-rw-r--r--pre_commit/git.py26
-rw-r--r--pre_commit/languages/all.py4
-rw-r--r--pre_commit/languages/conda.py2
-rw-r--r--pre_commit/languages/coursier.py71
-rw-r--r--pre_commit/languages/docker.py5
-rw-r--r--pre_commit/languages/dotnet.py90
-rw-r--r--pre_commit/languages/helpers.py27
-rw-r--r--pre_commit/languages/node.py31
-rw-r--r--pre_commit/languages/pygrep.py48
-rw-r--r--pre_commit/languages/python.py27
-rw-r--r--pre_commit/languages/ruby.py10
-rw-r--r--pre_commit/main.py23
-rw-r--r--pre_commit/make_archives.py4
-rw-r--r--pre_commit/resources/rbenv.tar.gzbin31781 -> 34224 bytes
-rw-r--r--pre_commit/resources/ruby-build.tar.gzbin62567 -> 72807 bytes
-rw-r--r--pre_commit/util.py3
-rw-r--r--pre_commit/xargs.py4
24 files changed, 332 insertions, 92 deletions
diff --git a/pre_commit/clientlib.py b/pre_commit/clientlib.py
index 8dfa947..87679bf 100644
--- a/pre_commit/clientlib.py
+++ b/pre_commit/clientlib.py
@@ -13,7 +13,7 @@ from identify.identify import ALL_TAGS
import pre_commit.constants as C
from pre_commit.color import add_color_option
-from pre_commit.error_handler import FatalError
+from pre_commit.errors import FatalError
from pre_commit.languages.all import all_languages
from pre_commit.logging_handler import logging_handler
from pre_commit.util import parse_version
diff --git a/pre_commit/commands/install_uninstall.py b/pre_commit/commands/install_uninstall.py
index 85fa53c..684b598 100644
--- a/pre_commit/commands/install_uninstall.py
+++ b/pre_commit/commands/install_uninstall.py
@@ -55,7 +55,7 @@ def is_our_script(filename: str) -> bool:
def shebang() -> str:
if sys.platform == 'win32':
- py = SYS_EXE
+ py, _ = os.path.splitext(SYS_EXE)
else:
exe_choices = [
f'python{sys.version_info[0]}.{sys.version_info[1]}',
diff --git a/pre_commit/commands/run.py b/pre_commit/commands/run.py
index 1f28c8c..0d335e2 100644
--- a/pre_commit/commands/run.py
+++ b/pre_commit/commands/run.py
@@ -11,6 +11,7 @@ from typing import Any
from typing import Collection
from typing import Dict
from typing import List
+from typing import MutableMapping
from typing import Sequence
from typing import Set
from typing import Tuple
@@ -28,7 +29,6 @@ from pre_commit.repository import install_hook_envs
from pre_commit.staged_files_only import staged_files_only
from pre_commit.store import Store
from pre_commit.util import cmd_output_b
-from pre_commit.util import EnvironT
logger = logging.getLogger('pre_commit')
@@ -116,7 +116,7 @@ class Classifier:
return Classifier(filenames)
-def _get_skips(environ: EnvironT) -> Set[str]:
+def _get_skips(environ: MutableMapping[str, str]) -> Set[str]:
skips = environ.get('SKIP', '')
return {skip.strip() for skip in skips.split(',') if skip.strip()}
@@ -258,7 +258,7 @@ def _run_hooks(
config: Dict[str, Any],
hooks: Sequence[Hook],
args: argparse.Namespace,
- environ: EnvironT,
+ environ: MutableMapping[str, str],
) -> int:
"""Actually run the hooks."""
skips = _get_skips(environ)
@@ -315,7 +315,7 @@ def run(
config_file: str,
store: Store,
args: argparse.Namespace,
- environ: EnvironT = os.environ,
+ environ: MutableMapping[str, str] = os.environ,
) -> int:
stash = not args.all_files and not args.files
diff --git a/pre_commit/commands/sample_config.py b/pre_commit/commands/sample_config.py
index d435faa..64617c3 100644
--- a/pre_commit/commands/sample_config.py
+++ b/pre_commit/commands/sample_config.py
@@ -7,7 +7,7 @@ SAMPLE_CONFIG = '''\
# See https://pre-commit.com/hooks.html for more hooks
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
- rev: v2.4.0
+ rev: v3.2.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
diff --git a/pre_commit/envcontext.py b/pre_commit/envcontext.py
index 16d3d15..4ab0d8c 100644
--- a/pre_commit/envcontext.py
+++ b/pre_commit/envcontext.py
@@ -2,13 +2,12 @@ import contextlib
import enum
import os
from typing import Generator
+from typing import MutableMapping
from typing import NamedTuple
from typing import Optional
from typing import Tuple
from typing import Union
-from pre_commit.util import EnvironT
-
class _Unset(enum.Enum):
UNSET = 1
@@ -27,7 +26,7 @@ ValueT = Union[str, _Unset, SubstitutionT]
PatchesT = Tuple[Tuple[str, ValueT], ...]
-def format_env(parts: SubstitutionT, env: EnvironT) -> str:
+def format_env(parts: SubstitutionT, env: MutableMapping[str, str]) -> str:
return ''.join(
env.get(part.name, part.default) if isinstance(part, Var) else part
for part in parts
@@ -37,7 +36,7 @@ def format_env(parts: SubstitutionT, env: EnvironT) -> str:
@contextlib.contextmanager
def envcontext(
patch: PatchesT,
- _env: Optional[EnvironT] = None,
+ _env: Optional[MutableMapping[str, str]] = None,
) -> Generator[None, None, None]:
"""In this context, `os.environ` is modified according to `patch`.
@@ -50,7 +49,7 @@ def envcontext(
replaced with the previous environment
"""
env = os.environ if _env is None else _env
- before = env.copy()
+ before = dict(env)
for k, v in patch:
if v is UNSET:
diff --git a/pre_commit/error_handler.py b/pre_commit/error_handler.py
index 13d78cb..023dd35 100644
--- a/pre_commit/error_handler.py
+++ b/pre_commit/error_handler.py
@@ -7,15 +7,17 @@ from typing import Generator
import pre_commit.constants as C
from pre_commit import output
+from pre_commit.errors import FatalError
from pre_commit.store import Store
from pre_commit.util import force_bytes
-class FatalError(RuntimeError):
- pass
-
-
-def _log_and_exit(msg: str, exc: BaseException, formatted: str) -> None:
+def _log_and_exit(
+ msg: str,
+ ret_code: int,
+ exc: BaseException,
+ formatted: str,
+) -> None:
error_msg = f'{msg}: {type(exc).__name__}: '.encode() + force_bytes(exc)
output.write_line_b(error_msg)
@@ -52,9 +54,9 @@ def _log_and_exit(msg: str, exc: BaseException, formatted: str) -> None:
_log_line('```')
_log_line()
_log_line('```')
- _log_line(formatted)
+ _log_line(formatted.rstrip())
_log_line('```')
- raise SystemExit(1)
+ raise SystemExit(ret_code)
@contextlib.contextmanager
@@ -63,9 +65,9 @@ def error_handler() -> Generator[None, None, None]:
yield
except (Exception, KeyboardInterrupt) as e:
if isinstance(e, FatalError):
- msg = 'An error has occurred'
+ msg, ret_code = 'An error has occurred', 1
elif isinstance(e, KeyboardInterrupt):
- msg = 'Interrupted (^C)'
+ msg, ret_code = 'Interrupted (^C)', 130
else:
- msg = 'An unexpected error has occurred'
- _log_and_exit(msg, e, traceback.format_exc())
+ msg, ret_code = 'An unexpected error has occurred', 3
+ _log_and_exit(msg, ret_code, e, traceback.format_exc())
diff --git a/pre_commit/errors.py b/pre_commit/errors.py
new file mode 100644
index 0000000..f84d3f1
--- /dev/null
+++ b/pre_commit/errors.py
@@ -0,0 +1,2 @@
+class FatalError(RuntimeError):
+ pass
diff --git a/pre_commit/git.py b/pre_commit/git.py
index 576bef8..13ba664 100644
--- a/pre_commit/git.py
+++ b/pre_commit/git.py
@@ -3,12 +3,14 @@ import os.path
import sys
from typing import Dict
from typing import List
+from typing import MutableMapping
from typing import Optional
from typing import Set
+from pre_commit.errors import FatalError
+from pre_commit.util import CalledProcessError
from pre_commit.util import cmd_output
from pre_commit.util import cmd_output_b
-from pre_commit.util import EnvironT
logger = logging.getLogger(__name__)
@@ -22,7 +24,9 @@ def zsplit(s: str) -> List[str]:
return []
-def no_git_env(_env: Optional[EnvironT] = None) -> Dict[str, str]:
+def no_git_env(
+ _env: Optional[MutableMapping[str, str]] = None,
+) -> Dict[str, str]:
# Too many bugs dealing with environment variables and GIT:
# https://github.com/pre-commit/pre-commit/issues/300
# In git 2.6.3 (maybe others), git exports GIT_WORK_TREE while running
@@ -43,7 +47,21 @@ def no_git_env(_env: Optional[EnvironT] = None) -> Dict[str, str]:
def get_root() -> str:
- return cmd_output('git', 'rev-parse', '--show-toplevel')[1].strip()
+ try:
+ root = cmd_output('git', 'rev-parse', '--show-toplevel')[1].strip()
+ except CalledProcessError:
+ raise FatalError(
+ 'git failed. Is it installed, and are you in a Git repository '
+ 'directory?',
+ )
+ else:
+ if root == '': # pragma: no cover (old git)
+ raise FatalError(
+ 'git toplevel unexpectedly empty! make sure you are not '
+ 'inside the `.git` directory of your repository.',
+ )
+ else:
+ return root
def get_git_dir(git_root: str = '.') -> str:
@@ -181,7 +199,7 @@ def check_for_cygwin_mismatch() -> None:
"""See https://github.com/pre-commit/pre-commit/issues/354"""
if sys.platform in ('cygwin', 'win32'): # pragma: no cover (windows)
is_cygwin_python = sys.platform == 'cygwin'
- toplevel = cmd_output('git', 'rev-parse', '--show-toplevel')[1]
+ toplevel = get_root()
is_cygwin_git = toplevel.startswith('/')
if is_cygwin_python ^ is_cygwin_git:
diff --git a/pre_commit/languages/all.py b/pre_commit/languages/all.py
index 5609631..9c2e59d 100644
--- a/pre_commit/languages/all.py
+++ b/pre_commit/languages/all.py
@@ -6,8 +6,10 @@ from typing import Tuple
from pre_commit.hook import Hook
from pre_commit.languages import conda
+from pre_commit.languages import coursier
from pre_commit.languages import docker
from pre_commit.languages import docker_image
+from pre_commit.languages import dotnet
from pre_commit.languages import fail
from pre_commit.languages import golang
from pre_commit.languages import node
@@ -40,8 +42,10 @@ class Language(NamedTuple):
languages = {
# BEGIN GENERATED (testing/gen-languages-all)
'conda': Language(name='conda', ENVIRONMENT_DIR=conda.ENVIRONMENT_DIR, get_default_version=conda.get_default_version, healthy=conda.healthy, install_environment=conda.install_environment, run_hook=conda.run_hook), # noqa: E501
+ 'coursier': Language(name='coursier', ENVIRONMENT_DIR=coursier.ENVIRONMENT_DIR, get_default_version=coursier.get_default_version, healthy=coursier.healthy, install_environment=coursier.install_environment, run_hook=coursier.run_hook), # noqa: E501
'docker': Language(name='docker', ENVIRONMENT_DIR=docker.ENVIRONMENT_DIR, get_default_version=docker.get_default_version, healthy=docker.healthy, install_environment=docker.install_environment, run_hook=docker.run_hook), # noqa: E501
'docker_image': Language(name='docker_image', ENVIRONMENT_DIR=docker_image.ENVIRONMENT_DIR, get_default_version=docker_image.get_default_version, healthy=docker_image.healthy, install_environment=docker_image.install_environment, run_hook=docker_image.run_hook), # noqa: E501
+ 'dotnet': Language(name='dotnet', ENVIRONMENT_DIR=dotnet.ENVIRONMENT_DIR, get_default_version=dotnet.get_default_version, healthy=dotnet.healthy, install_environment=dotnet.install_environment, run_hook=dotnet.run_hook), # noqa: E501
'fail': Language(name='fail', ENVIRONMENT_DIR=fail.ENVIRONMENT_DIR, get_default_version=fail.get_default_version, healthy=fail.healthy, install_environment=fail.install_environment, run_hook=fail.run_hook), # noqa: E501
'golang': Language(name='golang', ENVIRONMENT_DIR=golang.ENVIRONMENT_DIR, get_default_version=golang.get_default_version, healthy=golang.healthy, install_environment=golang.install_environment, run_hook=golang.run_hook), # noqa: E501
'node': Language(name='node', ENVIRONMENT_DIR=node.ENVIRONMENT_DIR, get_default_version=node.get_default_version, healthy=node.healthy, install_environment=node.install_environment, run_hook=node.run_hook), # noqa: E501
diff --git a/pre_commit/languages/conda.py b/pre_commit/languages/conda.py
index 071757a..d634e49 100644
--- a/pre_commit/languages/conda.py
+++ b/pre_commit/languages/conda.py
@@ -77,7 +77,7 @@ def run_hook(
color: bool,
) -> Tuple[int, bytes]:
# TODO: Some rare commands need to be run using `conda run` but mostly we
- # can run them withot which is much quicker and produces a better
+ # can run them without which is much quicker and produces a better
# output.
# cmd = ('conda', 'run', '-p', env_dir) + hook.cmd
with in_env(hook.prefix, hook.language_version):
diff --git a/pre_commit/languages/coursier.py b/pre_commit/languages/coursier.py
new file mode 100644
index 0000000..2841467
--- /dev/null
+++ b/pre_commit/languages/coursier.py
@@ -0,0 +1,71 @@
+import contextlib
+import os
+from typing import Generator
+from typing import Sequence
+from typing import Tuple
+
+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
+
+ENVIRONMENT_DIR = 'coursier'
+
+get_default_version = helpers.basic_get_default_version
+healthy = helpers.basic_healthy
+
+
+def install_environment(
+ prefix: Prefix,
+ version: str,
+ additional_dependencies: Sequence[str],
+) -> None: # pragma: win32 no cover
+ helpers.assert_version_default('coursier', version)
+ helpers.assert_no_additional_deps('coursier', additional_dependencies)
+
+ envdir = prefix.path(helpers.environment_dir(ENVIRONMENT_DIR, version))
+ channel = prefix.path('.pre-commit-channel')
+ with clean_path_on_failure(envdir):
+ for app_descriptor in os.listdir(channel):
+ _, app_file = os.path.split(app_descriptor)
+ app, _ = os.path.splitext(app_file)
+ helpers.run_setup_cmd(
+ prefix,
+ (
+ 'cs',
+ 'install',
+ '--default-channels=false',
+ f'--channel={channel}',
+ app,
+ f'--dir={envdir}',
+ ),
+ )
+
+
+def get_env_patch(target_dir: str) -> PatchesT: # pragma: win32 no cover
+ return (
+ ('PATH', (target_dir, os.pathsep, Var('PATH'))),
+ )
+
+
+@contextlib.contextmanager
+def in_env(
+ prefix: Prefix,
+) -> Generator[None, None, None]: # pragma: win32 no cover
+ target_dir = prefix.path(
+ helpers.environment_dir(ENVIRONMENT_DIR, get_default_version()),
+ )
+ with envcontext(get_env_patch(target_dir)):
+ yield
+
+
+def run_hook(
+ hook: Hook,
+ file_args: Sequence[str],
+ color: bool,
+) -> Tuple[int, bytes]: # pragma: win32 no cover
+ with in_env(hook.prefix):
+ return helpers.run_xargs(hook, hook.cmd, file_args, color=color)
diff --git a/pre_commit/languages/docker.py b/pre_commit/languages/docker.py
index 9c13119..9d30568 100644
--- a/pre_commit/languages/docker.py
+++ b/pre_commit/languages/docker.py
@@ -87,9 +87,8 @@ def run_hook(
# automated cleanup of docker images.
build_docker_image(hook.prefix, pull=False)
- hook_cmd = hook.cmd
- entry_exe, cmd_rest = hook.cmd[0], hook_cmd[1:]
+ entry_exe, *cmd_rest = hook.cmd
entry_tag = ('--entrypoint', entry_exe, docker_tag(hook.prefix))
- cmd = docker_cmd() + entry_tag + cmd_rest
+ cmd = (*docker_cmd(), *entry_tag, *cmd_rest)
return helpers.run_xargs(hook, cmd, file_args, color=color)
diff --git a/pre_commit/languages/dotnet.py b/pre_commit/languages/dotnet.py
new file mode 100644
index 0000000..a8abc86
--- /dev/null
+++ b/pre_commit/languages/dotnet.py
@@ -0,0 +1,90 @@
+import contextlib
+import os.path
+from typing import Generator
+from typing import Sequence
+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 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 rmtree
+
+ENVIRONMENT_DIR = 'dotnetenv'
+BIN_DIR = 'bin'
+
+get_default_version = helpers.basic_get_default_version
+healthy = helpers.basic_healthy
+
+
+def get_env_patch(venv: str) -> PatchesT:
+ return (
+ ('PATH', (os.path.join(venv, BIN_DIR), os.pathsep, Var('PATH'))),
+ )
+
+
+@contextlib.contextmanager
+def in_env(prefix: Prefix) -> Generator[None, None, None]:
+ directory = helpers.environment_dir(ENVIRONMENT_DIR, C.DEFAULT)
+ envdir = prefix.path(directory)
+ with envcontext(get_env_patch(envdir)):
+ yield
+
+
+def install_environment(
+ prefix: Prefix,
+ version: str,
+ additional_dependencies: Sequence[str],
+) -> None:
+ helpers.assert_version_default('dotnet', version)
+ helpers.assert_no_additional_deps('dotnet', additional_dependencies)
+
+ envdir = prefix.path(helpers.environment_dir(ENVIRONMENT_DIR, version))
+ with clean_path_on_failure(envdir):
+ build_dir = 'pre-commit-build'
+
+ # Build & pack nupkg file
+ helpers.run_setup_cmd(
+ prefix,
+ (
+ 'dotnet', 'pack',
+ '--configuration', 'Release',
+ '--output', build_dir,
+ ),
+ )
+
+ # Determine tool from the packaged file <tool_name>.<version>.nupkg
+ build_outputs = os.listdir(os.path.join(prefix.prefix_dir, build_dir))
+ if len(build_outputs) != 1:
+ raise NotImplementedError(
+ f"Can't handle multiple build outputs. Got {build_outputs}",
+ )
+ tool_name = build_outputs[0].split('.')[0]
+
+ # Install to bin dir
+ helpers.run_setup_cmd(
+ prefix,
+ (
+ 'dotnet', 'tool', 'install',
+ '--tool-path', os.path.join(envdir, BIN_DIR),
+ '--add-source', build_dir,
+ tool_name,
+ ),
+ )
+
+ # Cleanup build output
+ for d in ('bin', 'obj', build_dir):
+ rmtree(prefix.path(d))
+
+
+def run_hook(
+ hook: Hook,
+ file_args: Sequence[str],
+ color: bool,
+) -> Tuple[int, bytes]:
+ with in_env(hook.prefix):
+ return helpers.run_xargs(hook, hook.cmd, file_args, color=color)
diff --git a/pre_commit/languages/helpers.py b/pre_commit/languages/helpers.py
index 01c65ab..29138fd 100644
--- a/pre_commit/languages/helpers.py
+++ b/pre_commit/languages/helpers.py
@@ -1,6 +1,7 @@
import multiprocessing
import os
import random
+import re
from typing import Any
from typing import List
from typing import Optional
@@ -10,6 +11,7 @@ from typing import Tuple
from typing import TYPE_CHECKING
import pre_commit.constants as C
+from pre_commit import parse_shebang
from pre_commit.hook import Hook
from pre_commit.prefix import Prefix
from pre_commit.util import cmd_output_b
@@ -20,6 +22,31 @@ if TYPE_CHECKING:
FIXED_RANDOM_SEED = 1542676187
+SHIMS_RE = re.compile(r'[/\\]shims[/\\]')
+
+
+def exe_exists(exe: str) -> bool:
+ found = parse_shebang.find_executable(exe)
+ if found is None: # exe exists
+ return False
+
+ homedir = os.path.expanduser('~')
+ try:
+ common: Optional[str] = os.path.commonpath((found, homedir))
+ except ValueError: # on windows, different drives raises ValueError
+ common = None
+
+ return (
+ # it is not in a /shims/ directory
+ not SHIMS_RE.search(found) and
+ (
+ # the homedir is / (docker, service user, etc.)
+ os.path.dirname(homedir) == homedir or
+ # the exe is not contained in the home directory
+ common != homedir
+ )
+ )
+
def run_setup_cmd(prefix: Prefix, cmd: Tuple[str, ...]) -> None:
cmd_output_b(*cmd, cwd=prefix.prefix_dir)
diff --git a/pre_commit/languages/node.py b/pre_commit/languages/node.py
index d99e6f2..8dc4e8b 100644
--- a/pre_commit/languages/node.py
+++ b/pre_commit/languages/node.py
@@ -7,7 +7,6 @@ from typing import Sequence
from typing import Tuple
import pre_commit.constants as C
-from pre_commit import parse_shebang
from pre_commit.envcontext import envcontext
from pre_commit.envcontext import PatchesT
from pre_commit.envcontext import UNSET
@@ -19,9 +18,9 @@ from pre_commit.prefix import Prefix
from pre_commit.util import clean_path_on_failure
from pre_commit.util import cmd_output
from pre_commit.util import cmd_output_b
+from pre_commit.util import rmtree
ENVIRONMENT_DIR = 'node_env'
-healthy = helpers.basic_healthy
@functools.lru_cache(maxsize=1)
@@ -31,7 +30,7 @@ def get_default_version() -> str:
return C.DEFAULT
# if node is already installed, we can save a bunch of setup time by
# using the installed version
- elif all(parse_shebang.find_executable(exe) for exe in ('node', 'npm')):
+ elif all(helpers.exe_exists(exe) for exe in ('node', 'npm')):
return 'system'
else:
return C.DEFAULT
@@ -73,6 +72,12 @@ def in_env(
yield
+def healthy(prefix: Prefix, language_version: str) -> bool:
+ with in_env(prefix, language_version):
+ retcode, _, _ = cmd_output_b('node', '--version', retcode=None)
+ return retcode == 0
+
+
def install_environment(
prefix: Prefix, version: str, additional_dependencies: Sequence[str],
) -> None:
@@ -94,11 +99,23 @@ def install_environment(
with in_env(prefix, version):
# https://npm.community/t/npm-install-g-git-vs-git-clone-cd-npm-install-g/5449
# install as if we installed from git
- helpers.run_setup_cmd(prefix, ('npm', 'install'))
- helpers.run_setup_cmd(
- prefix,
- ('npm', 'install', '-g', '.', *additional_dependencies),
+
+ local_install_cmd = (
+ 'npm', 'install', '--dev', '--prod',
+ '--ignore-prepublish', '--no-progress', '--no-save',
)
+ helpers.run_setup_cmd(prefix, local_install_cmd)
+
+ _, pkg, _ = cmd_output('npm', 'pack', cwd=prefix.prefix_dir)
+ pkg = prefix.path(pkg.strip())
+
+ install = ('npm', 'install', '-g', pkg, *additional_dependencies)
+ helpers.run_setup_cmd(prefix, install)
+
+ # clean these up after installation
+ if prefix.exists('node_modules'): # pragma: win32 no cover
+ rmtree(prefix.path('node_modules'))
+ os.remove(pkg)
def run_hook(
diff --git a/pre_commit/languages/pygrep.py b/pre_commit/languages/pygrep.py
index 40adba0..c80d679 100644
--- a/pre_commit/languages/pygrep.py
+++ b/pre_commit/languages/pygrep.py
@@ -1,6 +1,7 @@
import argparse
import re
import sys
+from typing import NamedTuple
from typing import Optional
from typing import Pattern
from typing import Sequence
@@ -45,6 +46,46 @@ def _process_filename_at_once(pattern: Pattern[bytes], filename: str) -> int:
return retv
+def _process_filename_by_line_negated(
+ pattern: Pattern[bytes],
+ filename: str,
+) -> int:
+ with open(filename, 'rb') as f:
+ for line in f:
+ if pattern.search(line):
+ return 0
+ else:
+ output.write_line(filename)
+ return 1
+
+
+def _process_filename_at_once_negated(
+ pattern: Pattern[bytes],
+ filename: str,
+) -> int:
+ with open(filename, 'rb') as f:
+ contents = f.read()
+ match = pattern.search(contents)
+ if match:
+ return 0
+ else:
+ output.write_line(filename)
+ return 1
+
+
+class Choice(NamedTuple):
+ multiline: bool
+ negate: bool
+
+
+FNS = {
+ Choice(multiline=True, negate=True): _process_filename_at_once_negated,
+ Choice(multiline=True, negate=False): _process_filename_at_once,
+ Choice(multiline=False, negate=True): _process_filename_by_line_negated,
+ Choice(multiline=False, negate=False): _process_filename_by_line,
+}
+
+
def run_hook(
hook: Hook,
file_args: Sequence[str],
@@ -64,6 +105,7 @@ def main(argv: Optional[Sequence[str]] = None) -> int:
)
parser.add_argument('-i', '--ignore-case', action='store_true')
parser.add_argument('--multiline', action='store_true')
+ parser.add_argument('--negate', action='store_true')
parser.add_argument('pattern', help='python regex pattern.')
parser.add_argument('filenames', nargs='*')
args = parser.parse_args(argv)
@@ -75,11 +117,9 @@ def main(argv: Optional[Sequence[str]] = None) -> int:
pattern = re.compile(args.pattern.encode(), flags)
retv = 0
+ process_fn = FNS[Choice(multiline=args.multiline, negate=args.negate)]
for filename in args.filenames:
- if args.multiline:
- retv |= _process_filename_at_once(pattern, filename)
- else:
- retv |= _process_filename_by_line(pattern, filename)
+ retv |= process_fn(pattern, filename)
return retv
diff --git a/pre_commit/languages/python.py b/pre_commit/languages/python.py
index 7a68580..65f521c 100644
--- a/pre_commit/languages/python.py
+++ b/pre_commit/languages/python.py
@@ -114,11 +114,6 @@ def get_default_version() -> str: # pragma: no cover (platform dependent)
if _find_by_py_launcher(exe):
return exe
- # Give a best-effort try for windows
- default_folder_name = exe.replace('.', '')
- if os.path.exists(fr'C:\{default_folder_name}\python.exe'):
- return exe
-
# We tried!
return C.DEFAULT
@@ -137,13 +132,11 @@ def _sys_executable_matches(version: str) -> bool:
return sys.version_info[:len(info)] == info
-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
+def norm_version(version: str) -> Optional[str]:
+ if version == C.DEFAULT: # use virtualenv's default
+ return None
+ elif _sys_executable_matches(version): # virtualenv defaults to our exe
+ return None
if os.name == 'nt': # pragma: no cover (windows)
version_exec = _find_by_py_launcher(version)
@@ -155,12 +148,6 @@ def norm_version(version: str) -> str:
if version_exec and version_exec != version:
return version_exec
- # If it is in the form pythonx.x search in the default
- # place on windows
- if version.startswith('python'):
- default_folder_name = version.replace('.', '')
- return fr'C:\{default_folder_name}\python.exe'
-
# Otherwise assume it is a path
return os.path.expanduser(version)
@@ -205,8 +192,10 @@ def install_environment(
additional_dependencies: Sequence[str],
) -> None:
envdir = prefix.path(helpers.environment_dir(ENVIRONMENT_DIR, version))
+ venv_cmd = [sys.executable, '-mvirtualenv', envdir]
python = norm_version(version)
- venv_cmd = (sys.executable, '-mvirtualenv', envdir, '-p', python)
+ if python is not None:
+ venv_cmd.extend(('-p', python))
install_cmd = ('python', '-mpip', 'install', '.', *additional_dependencies)
with clean_path_on_failure(envdir):
diff --git a/pre_commit/languages/ruby.py b/pre_commit/languages/ruby.py
index 73b23cc..1a0f0c7 100644
--- a/pre_commit/languages/ruby.py
+++ b/pre_commit/languages/ruby.py
@@ -8,7 +8,6 @@ from typing import Sequence
from typing import Tuple
import pre_commit.constants as C
-from pre_commit import parse_shebang
from pre_commit.envcontext import envcontext
from pre_commit.envcontext import PatchesT
from pre_commit.envcontext import UNSET
@@ -26,7 +25,7 @@ healthy = helpers.basic_healthy
@functools.lru_cache(maxsize=1)
def get_default_version() -> str:
- if all(parse_shebang.find_executable(exe) for exe in ('ruby', 'gem')):
+ if all(helpers.exe_exists(exe) for exe in ('ruby', 'gem')):
return 'system'
else:
return C.DEFAULT
@@ -122,8 +121,8 @@ def install_environment(
# Need to call this before installing so rbenv's directories
# are set up
helpers.run_setup_cmd(prefix, ('rbenv', 'init', '-'))
- # XXX: this will *always* fail if `version == C.DEFAULT`
- _install_ruby(prefix, version)
+ 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'))
@@ -134,7 +133,8 @@ def install_environment(
helpers.run_setup_cmd(
prefix,
(
- 'gem', 'install', '--no-document',
+ 'gem', 'install',
+ '--no-document', '--no-format-executable',
*prefix.star('.gem'), *additional_dependencies,
),
)
diff --git a/pre_commit/main.py b/pre_commit/main.py
index 8647960..c1eb104 100644
--- a/pre_commit/main.py
+++ b/pre_commit/main.py
@@ -23,10 +23,8 @@ from pre_commit.commands.run import run
from pre_commit.commands.sample_config import sample_config
from pre_commit.commands.try_repo import try_repo
from pre_commit.error_handler import error_handler
-from pre_commit.error_handler import FatalError
from pre_commit.logging_handler import logging_handler
from pre_commit.store import Store
-from pre_commit.util import CalledProcessError
logger = logging.getLogger('pre_commit')
@@ -146,21 +144,8 @@ def _adjust_args_and_chdir(args: argparse.Namespace) -> None:
if args.command == 'try-repo' and os.path.exists(args.repo):
args.repo = os.path.abspath(args.repo)
- try:
- toplevel = git.get_root()
- except CalledProcessError:
- raise FatalError(
- 'git failed. Is it installed, and are you in a Git repository '
- 'directory?',
- )
- else:
- if toplevel == '': # pragma: no cover (old git)
- raise FatalError(
- 'git toplevel unexpectedly empty! make sure you are not '
- 'inside the `.git` directory of your repository.',
- )
- else:
- os.chdir(toplevel)
+ toplevel = git.get_root()
+ os.chdir(toplevel)
args.config = os.path.relpath(args.config)
if args.command in {'run', 'try-repo'}:
@@ -339,11 +324,11 @@ def main(argv: Optional[Sequence[str]] = None) -> int:
parser.parse_args(['--help'])
with error_handler(), logging_handler(args.color):
+ git.check_for_cygwin_mismatch()
+
if args.command not in COMMANDS_NO_GIT:
_adjust_args_and_chdir(args)
- git.check_for_cygwin_mismatch()
-
store = Store()
store.mark_config_used(args.config)
diff --git a/pre_commit/make_archives.py b/pre_commit/make_archives.py
index c31bcd7..d320b83 100644
--- a/pre_commit/make_archives.py
+++ b/pre_commit/make_archives.py
@@ -15,8 +15,8 @@ from pre_commit.util import tmpdir
REPOS = (
- ('rbenv', 'git://github.com/rbenv/rbenv', 'a3fa9b7'),
- ('ruby-build', 'git://github.com/rbenv/ruby-build', '1a902f3'),
+ ('rbenv', 'git://github.com/rbenv/rbenv', '0843745'),
+ ('ruby-build', 'git://github.com/rbenv/ruby-build', '258455e'),
(
'ruby-download',
'git://github.com/garnieretienne/rvm-download',
diff --git a/pre_commit/resources/rbenv.tar.gz b/pre_commit/resources/rbenv.tar.gz
index 5307b19..97ac469 100644
--- a/pre_commit/resources/rbenv.tar.gz
+++ b/pre_commit/resources/rbenv.tar.gz
Binary files differ
diff --git a/pre_commit/resources/ruby-build.tar.gz b/pre_commit/resources/ruby-build.tar.gz
index 4a69a09..4412ed4 100644
--- a/pre_commit/resources/ruby-build.tar.gz
+++ b/pre_commit/resources/ruby-build.tar.gz
Binary files differ
diff --git a/pre_commit/util.py b/pre_commit/util.py
index 0338b37..f4cf704 100644
--- a/pre_commit/util.py
+++ b/pre_commit/util.py
@@ -16,7 +16,6 @@ from typing import IO
from typing import Optional
from typing import Tuple
from typing import Type
-from typing import Union
import yaml
@@ -29,8 +28,6 @@ else: # pragma: no cover (<PY37)
from importlib_resources import open_binary
from importlib_resources import read_text
-EnvironT = Union[Dict[str, str], 'os._Environ']
-
Loader = getattr(yaml, 'CSafeLoader', yaml.SafeLoader)
yaml_load = functools.partial(yaml.load, Loader=Loader)
Dumper = getattr(yaml, 'CSafeDumper', yaml.SafeDumper)
diff --git a/pre_commit/xargs.py b/pre_commit/xargs.py
index 5235dc6..7538b54 100644
--- a/pre_commit/xargs.py
+++ b/pre_commit/xargs.py
@@ -9,6 +9,7 @@ from typing import Callable
from typing import Generator
from typing import Iterable
from typing import List
+from typing import MutableMapping
from typing import Optional
from typing import Sequence
from typing import Tuple
@@ -17,13 +18,12 @@ from typing import TypeVar
from pre_commit import parse_shebang
from pre_commit.util import cmd_output_b
from pre_commit.util import cmd_output_p
-from pre_commit.util import EnvironT
TArg = TypeVar('TArg')
TRet = TypeVar('TRet')
-def _environ_size(_env: Optional[EnvironT] = None) -> int:
+def _environ_size(_env: Optional[MutableMapping[str, str]] = None) -> int:
environ = _env if _env is not None else getattr(os, 'environb', os.environ)
size = 8 * len(environ) # number of pointers in `envp`
for k, v in environ.items():