summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.github/FUNDING.yml2
-rw-r--r--.pre-commit-config.yaml6
-rw-r--r--CHANGELOG.md62
-rw-r--r--pre_commit/clientlib.py15
-rw-r--r--pre_commit/commands/autoupdate.py8
-rw-r--r--pre_commit/commands/migrate_config.py26
-rw-r--r--pre_commit/commands/run.py22
-rw-r--r--pre_commit/envcontext.py6
-rw-r--r--pre_commit/git.py32
-rw-r--r--pre_commit/hook.py1
-rw-r--r--pre_commit/languages/dotnet.py7
-rw-r--r--pre_commit/languages/python.py2
-rw-r--r--pre_commit/languages/ruby.py4
-rw-r--r--pre_commit/meta_hooks/check_useless_excludes.py6
-rw-r--r--pre_commit/util.py2
-rw-r--r--pre_commit/xargs.py10
-rw-r--r--setup.cfg2
-rwxr-xr-xtesting/resources/exclude_types_repo/bin/hook.sh2
-rwxr-xr-xtesting/resources/failing_hook_repo/bin/hook.sh2
-rwxr-xr-xtesting/resources/modified_file_returns_zero_repo/bin/hook2.sh2
-rwxr-xr-xtesting/resources/script_hooks_repo/bin/hook.sh2
-rw-r--r--testing/resources/types_or_repo/.pre-commit-hooks.yaml6
-rwxr-xr-xtesting/resources/types_or_repo/bin/hook.sh3
-rwxr-xr-xtesting/resources/types_repo/bin/hook.sh2
-rwxr-xr-xtesting/zipapp/make3
-rw-r--r--tests/clientlib_test.py17
-rw-r--r--tests/commands/autoupdate_test.py8
-rw-r--r--tests/commands/migrate_config_test.py15
-rw-r--r--tests/commands/run_test.py34
-rw-r--r--tests/languages/python_test.py7
-rw-r--r--tests/repository_test.py1
-rw-r--r--tests/xargs_test.py9
32 files changed, 253 insertions, 73 deletions
diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
new file mode 100644
index 0000000..9408e44
--- /dev/null
+++ b/.github/FUNDING.yml
@@ -0,0 +1,2 @@
+github: asottile
+open_collective: pre-commit
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 80fa14b..d42bb1b 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -21,16 +21,16 @@ repos:
hooks:
- id: autopep8
- repo: https://github.com/pre-commit/pre-commit
- rev: v2.7.1
+ rev: v2.9.3
hooks:
- id: validate_manifest
- repo: https://github.com/asottile/pyupgrade
- rev: v2.7.3
+ rev: v2.7.4
hooks:
- id: pyupgrade
args: [--py36-plus]
- repo: https://github.com/asottile/reorder_python_imports
- rev: v2.3.5
+ rev: v2.3.6
hooks:
- id: reorder-python-imports
args: [--py3-plus]
diff --git a/CHANGELOG.md b/CHANGELOG.md
index ff1013f..ef36dec 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,65 @@
+2.9.3 - 2020-12-07
+==================
+
+### Fixes
+- Fix crash on cygwin mismatch check outside of a git directory
+ - #1721 PR by @asottile.
+ - #1720 issue by @chronoB.
+- Fix cleanup code on docker volumes for go
+ - #1725 PR by @fsouza.
+- Fix working directory detection on SUBST drives on windows
+ - #1727 PR by mrogaski.
+ - #1610 issue by @jcameron73.
+
+2.9.2 - 2020-11-25
+==================
+
+### Fixes
+- Fix default value for `types_or` so `symlink` and `directory` can be matched
+ - #1716 PR by @asottile.
+ - #1718 issue by @CodeBleu.
+
+2.9.1 - 2020-11-25
+==================
+
+### Fixes
+- Improve error message for "hook goes missing"
+ - #1709 PR by @paulhfischer.
+ - #1708 issue by @theod07.
+- Add warning for `/*` in `files` / `exclude` regexes
+ - #1707 PR by @paulhfischer.
+ - #1702 issue by @asottile.
+- Fix `healthy()` check for `language: python` on windows when the base
+ executable has non-ascii characters.
+ - #1713 PR by @asottile.
+ - #1711 issue by @Najiva.
+
+2.9.0 - 2020-11-21
+==================
+
+### Features
+- Add `types_or` which allows matching multiple disparate `types` in a hook
+ - #1677 by @MarcoGorelli.
+ - #607 by @asottile.
+- Add Github Sponsors / Open Collective links
+ - https://github.com/sponsors/asottile
+ - https://opencollective.com/pre-commit
+
+### Fixes
+- Improve cleanup for `language: dotnet`
+ - #1678 by @rkm.
+- Fix "xargs" when running windows batch files
+ - #1686 PR by @asottile.
+ - #1604 issue by @apietrzak.
+ - #1604 issue by @ufwtlsb.
+- Fix conflict with external `rbenv` and `language_version: default`
+ - #1700 PR by @asottile.
+ - #1699 issue by @abuxton.
+- Improve performance of `git status` / `git diff` commands by ignoring
+ submodules
+ - #1704 PR by @Vynce.
+ - #1701 issue by @Vynce.
+
2.8.2 - 2020-10-30
==================
diff --git a/pre_commit/clientlib.py b/pre_commit/clientlib.py
index 87679bf..20d4492 100644
--- a/pre_commit/clientlib.py
+++ b/pre_commit/clientlib.py
@@ -61,6 +61,7 @@ MANIFEST_HOOK_DICT = cfgv.Map(
cfgv.Optional('files', check_string_regex, ''),
cfgv.Optional('exclude', check_string_regex, '^$'),
cfgv.Optional('types', cfgv.check_array(check_type_tag), ['file']),
+ cfgv.Optional('types_or', cfgv.check_array(check_type_tag), []),
cfgv.Optional('exclude_types', cfgv.check_array(check_type_tag), []),
cfgv.Optional(
@@ -111,6 +112,18 @@ LOCAL = 'local'
META = 'meta'
+class OptionalSensibleRegex(cfgv.OptionalNoDefault):
+ def check(self, dct: Dict[str, Any]) -> None:
+ super().check(dct)
+
+ if '/*' in dct.get(self.key, ''):
+ logger.warning(
+ f'The {self.key!r} field in hook {dct.get("id")!r} is a '
+ f"regex, not a glob -- matching '/*' probably isn't what you "
+ f'want here',
+ )
+
+
class MigrateShaToRev:
key = 'rev'
@@ -226,6 +239,8 @@ CONFIG_HOOK_DICT = cfgv.Map(
for item in MANIFEST_HOOK_DICT.items
if item.key != 'id'
),
+ OptionalSensibleRegex('files', cfgv.check_string),
+ OptionalSensibleRegex('exclude', cfgv.check_string),
)
CONFIG_REPO_DICT = cfgv.Map(
'Repository', 'repo',
diff --git a/pre_commit/commands/autoupdate.py b/pre_commit/commands/autoupdate.py
index 87f6d53..33a3473 100644
--- a/pre_commit/commands/autoupdate.py
+++ b/pre_commit/commands/autoupdate.py
@@ -79,14 +79,12 @@ def _check_hooks_still_exist_at_rev(
hooks_missing = hooks - {hook['id'] for hook in manifest}
if hooks_missing:
raise RepositoryCannotBeUpdatedError(
- f'Cannot update because the tip of HEAD is missing these hooks:\n'
- f'{", ".join(sorted(hooks_missing))}',
+ f'Cannot update because the update target is missing these '
+ f'hooks:\n{", ".join(sorted(hooks_missing))}',
)
-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)$')
def _original_lines(
diff --git a/pre_commit/commands/migrate_config.py b/pre_commit/commands/migrate_config.py
index d580ff1..a155f6b 100644
--- a/pre_commit/commands/migrate_config.py
+++ b/pre_commit/commands/migrate_config.py
@@ -1,4 +1,5 @@
import re
+import textwrap
import yaml
@@ -6,27 +7,22 @@ from pre_commit.clientlib import load_config
from pre_commit.util import yaml_load
-def _indent(s: str) -> str:
- lines = s.splitlines(True)
- return ''.join(' ' * 4 + line if line.strip() else line for line in lines)
-
-
def _is_header_line(line: str) -> bool:
return line.startswith(('#', '---')) or not line.strip()
def _migrate_map(contents: str) -> str:
- # Find the first non-header line
- lines = contents.splitlines(True)
- i = 0
- # Only loop on non empty configuration file
- while i < len(lines) and _is_header_line(lines[i]):
- i += 1
+ if isinstance(yaml_load(contents), list):
+ # Find the first non-header line
+ lines = contents.splitlines(True)
+ i = 0
+ # Only loop on non empty configuration file
+ while i < len(lines) and _is_header_line(lines[i]):
+ i += 1
- header = ''.join(lines[:i])
- rest = ''.join(lines[i:])
+ header = ''.join(lines[:i])
+ rest = ''.join(lines[i:])
- if isinstance(yaml_load(contents), list):
# If they are using the "default" flow style of yaml, this operation
# will yield a valid configuration
try:
@@ -34,7 +30,7 @@ def _migrate_map(contents: str) -> str:
yaml_load(trial_contents)
contents = trial_contents
except yaml.YAMLError:
- contents = f'{header}repos:\n{_indent(rest)}'
+ contents = f'{header}repos:\n{textwrap.indent(rest, " " * 4)}'
return contents
diff --git a/pre_commit/commands/run.py b/pre_commit/commands/run.py
index 0d335e2..1e8fad2 100644
--- a/pre_commit/commands/run.py
+++ b/pre_commit/commands/run.py
@@ -83,20 +83,32 @@ class Classifier:
self,
names: Sequence[str],
types: Collection[str],
+ types_or: Collection[str],
exclude_types: Collection[str],
) -> List[str]:
- types, exclude_types = frozenset(types), frozenset(exclude_types)
+ types = frozenset(types)
+ types_or = frozenset(types_or)
+ exclude_types = frozenset(exclude_types)
ret = []
for filename in names:
tags = self._types_for_file(filename)
- if tags >= types and not tags & exclude_types:
+ if (
+ tags >= types and
+ (not types_or or tags & types_or) and
+ not tags & exclude_types
+ ):
ret.append(filename)
return ret
def filenames_for_hook(self, hook: Hook) -> Tuple[str, ...]:
names = self.filenames
names = filter_by_include_exclude(names, hook.files, hook.exclude)
- names = self.by_types(names, hook.types, hook.exclude_types)
+ names = self.by_types(
+ names,
+ hook.types,
+ hook.types_or,
+ hook.exclude_types,
+ )
return tuple(names)
@classmethod
@@ -250,7 +262,9 @@ def _all_filenames(args: argparse.Namespace) -> Collection[str]:
def _get_diff() -> bytes:
- _, out, _ = cmd_output_b('git', 'diff', '--no-ext-diff', retcode=None)
+ _, out, _ = cmd_output_b(
+ 'git', 'diff', '--no-ext-diff', '--ignore-submodules', retcode=None,
+ )
return out
diff --git a/pre_commit/envcontext.py b/pre_commit/envcontext.py
index 4ab0d8c..92d975d 100644
--- a/pre_commit/envcontext.py
+++ b/pre_commit/envcontext.py
@@ -8,11 +8,7 @@ from typing import Optional
from typing import Tuple
from typing import Union
-
-class _Unset(enum.Enum):
- UNSET = 1
-
-
+_Unset = enum.Enum('_Unset', 'UNSET')
UNSET = _Unset.UNSET
diff --git a/pre_commit/git.py b/pre_commit/git.py
index 13ba664..5096274 100644
--- a/pre_commit/git.py
+++ b/pre_commit/git.py
@@ -47,21 +47,26 @@ def no_git_env(
def get_root() -> str:
+ # Git 2.25 introduced a change to "rev-parse --show-toplevel" that exposed
+ # underlying volumes for Windows drives mapped with SUBST. We use
+ # "rev-parse --show-cdup" to get the appropriate path, but must perform
+ # an extra check to see if we are in the .git directory.
try:
- root = cmd_output('git', 'rev-parse', '--show-toplevel')[1].strip()
+ root = os.path.realpath(
+ cmd_output('git', 'rev-parse', '--show-cdup')[1].strip(),
+ )
+ git_dir = os.path.realpath(get_git_dir())
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
+ if os.path.commonpath((root, git_dir)) == git_dir:
+ raise FatalError(
+ 'git toplevel unexpectedly empty! make sure you are not '
+ 'inside the `.git` directory of your repository.',
+ )
+ return root
def get_git_dir(git_root: str = '.') -> str:
@@ -130,7 +135,9 @@ def get_staged_files(cwd: Optional[str] = None) -> List[str]:
def intent_to_add_files() -> List[str]:
- _, stdout, _ = cmd_output('git', 'status', '--porcelain', '-z')
+ _, stdout, _ = cmd_output(
+ 'git', 'status', '--ignore-submodules', '--porcelain', '-z',
+ )
parts = list(reversed(zsplit(stdout)))
intent_to_add = []
while parts:
@@ -199,7 +206,10 @@ 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 = get_root()
+ try:
+ toplevel = get_root()
+ except FatalError: # skip the check if we're not in a git repo
+ return
is_cygwin_git = toplevel.startswith('/')
if is_cygwin_python ^ is_cygwin_git:
diff --git a/pre_commit/hook.py b/pre_commit/hook.py
index b65ac42..ea77394 100644
--- a/pre_commit/hook.py
+++ b/pre_commit/hook.py
@@ -22,6 +22,7 @@ class Hook(NamedTuple):
files: str
exclude: str
types: Sequence[str]
+ types_or: Sequence[str]
exclude_types: Sequence[str]
additional_dependencies: Sequence[str]
args: Sequence[str]
diff --git a/pre_commit/languages/dotnet.py b/pre_commit/languages/dotnet.py
index a8abc86..094d2f1 100644
--- a/pre_commit/languages/dotnet.py
+++ b/pre_commit/languages/dotnet.py
@@ -12,7 +12,6 @@ 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'
@@ -76,9 +75,9 @@ def install_environment(
),
)
- # Cleanup build output
- for d in ('bin', 'obj', build_dir):
- rmtree(prefix.path(d))
+ # Clean the git dir, ignoring the environment dir
+ clean_cmd = ('git', 'clean', '-ffxd', '-e', f'{ENVIRONMENT_DIR}-*')
+ helpers.run_setup_cmd(prefix, clean_cmd)
def run_hook(
diff --git a/pre_commit/languages/python.py b/pre_commit/languages/python.py
index 65f521c..43b7280 100644
--- a/pre_commit/languages/python.py
+++ b/pre_commit/languages/python.py
@@ -36,7 +36,7 @@ def _version_info(exe: str) -> str:
def _read_pyvenv_cfg(filename: str) -> Dict[str, str]:
ret = {}
- with open(filename) as f:
+ with open(filename, encoding='UTF-8') as f:
for line in f:
try:
k, v = line.split('=')
diff --git a/pre_commit/languages/ruby.py b/pre_commit/languages/ruby.py
index 1a0f0c7..81bc954 100644
--- a/pre_commit/languages/ruby.py
+++ b/pre_commit/languages/ruby.py
@@ -52,7 +52,6 @@ def get_env_patch(
else: # pragma: win32 no cover
patches += (
('RBENV_ROOT', venv),
- ('RBENV_VERSION', language_version),
(
'PATH', (
os.path.join(venv, 'gems', 'bin'), os.pathsep,
@@ -61,6 +60,9 @@ def get_env_patch(
),
),
)
+ if language_version not in {'system', 'default'}: # pragma: win32 no cover
+ patches += (('RBENV_VERSION', language_version),)
+
return patches
diff --git a/pre_commit/meta_hooks/check_useless_excludes.py b/pre_commit/meta_hooks/check_useless_excludes.py
index db6865c..12be03f 100644
--- a/pre_commit/meta_hooks/check_useless_excludes.py
+++ b/pre_commit/meta_hooks/check_useless_excludes.py
@@ -47,8 +47,10 @@ def check_useless_excludes(config_file: str) -> int:
# the defaults applied during runtime
hook = apply_defaults(hook, MANIFEST_HOOK_DICT)
names = classifier.filenames
- types, exclude_types = hook['types'], hook['exclude_types']
- names = classifier.by_types(names, types, exclude_types)
+ types = hook['types']
+ types_or = hook['types_or']
+ exclude_types = hook['exclude_types']
+ names = classifier.by_types(names, types, types_or, exclude_types)
include, exclude = hook['files'], hook['exclude']
if not exclude_matches_any(names, include, exclude):
print(
diff --git a/pre_commit/util.py b/pre_commit/util.py
index f4cf704..b5f40ad 100644
--- a/pre_commit/util.py
+++ b/pre_commit/util.py
@@ -255,7 +255,7 @@ def rmtree(path: str) -> None:
excvalue = exc[1]
if (
func in (os.rmdir, os.remove, os.unlink) and
- excvalue.errno == errno.EACCES
+ excvalue.errno in {errno.EACCES, errno.EPERM}
):
for p in (path, os.path.dirname(path)):
os.chmod(p, os.stat(p).st_mode | stat.S_IWUSR)
diff --git a/pre_commit/xargs.py b/pre_commit/xargs.py
index 7538b54..60a057c 100644
--- a/pre_commit/xargs.py
+++ b/pre_commit/xargs.py
@@ -137,6 +137,16 @@ def xargs(
except parse_shebang.ExecutableNotFoundError as e:
return e.to_output()[:2]
+ # on windows, batch files have a separate length limit than windows itself
+ if (
+ sys.platform == 'win32' and
+ cmd[0].lower().endswith(('.bat', '.cmd'))
+ ): # pragma: win32 cover
+ # this is implementation details but the command gets translated into
+ # full/path/to/cmd.exe /c *cmd
+ cmd_exe = parse_shebang.find_executable('cmd.exe')
+ _max_length = 8192 - len(cmd_exe) - len(' /c ')
+
partitions = partition(cmd, varargs, target_concurrency, _max_length)
def run_cmd_partition(
diff --git a/setup.cfg b/setup.cfg
index 32160b9..2e77fcf 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -1,6 +1,6 @@
[metadata]
name = pre_commit
-version = 2.8.2
+version = 2.9.3
description = A framework for managing and maintaining multi-language pre-commit hooks.
long_description = file: README.md
long_description_content_type = text/markdown
diff --git a/testing/resources/exclude_types_repo/bin/hook.sh b/testing/resources/exclude_types_repo/bin/hook.sh
index bdade51..a828db4 100755
--- a/testing/resources/exclude_types_repo/bin/hook.sh
+++ b/testing/resources/exclude_types_repo/bin/hook.sh
@@ -1,3 +1,3 @@
#!/usr/bin/env bash
-echo $@
+echo "$@"
exit 1
diff --git a/testing/resources/failing_hook_repo/bin/hook.sh b/testing/resources/failing_hook_repo/bin/hook.sh
index 229ccaf..7dcffeb 100755
--- a/testing/resources/failing_hook_repo/bin/hook.sh
+++ b/testing/resources/failing_hook_repo/bin/hook.sh
@@ -1,4 +1,4 @@
#!/usr/bin/env bash
echo 'Fail'
-echo $@
+echo "$@"
exit 1
diff --git a/testing/resources/modified_file_returns_zero_repo/bin/hook2.sh b/testing/resources/modified_file_returns_zero_repo/bin/hook2.sh
index 5af177a..a9f1dcd 100755
--- a/testing/resources/modified_file_returns_zero_repo/bin/hook2.sh
+++ b/testing/resources/modified_file_returns_zero_repo/bin/hook2.sh
@@ -1,2 +1,2 @@
#!/usr/bin/env bash
-echo $@
+echo "$@"
diff --git a/testing/resources/script_hooks_repo/bin/hook.sh b/testing/resources/script_hooks_repo/bin/hook.sh
index 6565ee4..cbc4b35 100755
--- a/testing/resources/script_hooks_repo/bin/hook.sh
+++ b/testing/resources/script_hooks_repo/bin/hook.sh
@@ -1,4 +1,4 @@
#!/usr/bin/env bash
-echo $@
+echo "$@"
echo 'Hello World'
diff --git a/testing/resources/types_or_repo/.pre-commit-hooks.yaml b/testing/resources/types_or_repo/.pre-commit-hooks.yaml
new file mode 100644
index 0000000..a4ea920
--- /dev/null
+++ b/testing/resources/types_or_repo/.pre-commit-hooks.yaml
@@ -0,0 +1,6 @@
+- id: python-cython-files
+ name: Python and Cython files
+ entry: bin/hook.sh
+ language: script
+ types: [file]
+ types_or: [python, cython]
diff --git a/testing/resources/types_or_repo/bin/hook.sh b/testing/resources/types_or_repo/bin/hook.sh
new file mode 100755
index 0000000..a828db4
--- /dev/null
+++ b/testing/resources/types_or_repo/bin/hook.sh
@@ -0,0 +1,3 @@
+#!/usr/bin/env bash
+echo "$@"
+exit 1
diff --git a/testing/resources/types_repo/bin/hook.sh b/testing/resources/types_repo/bin/hook.sh
index bdade51..a828db4 100755
--- a/testing/resources/types_repo/bin/hook.sh
+++ b/testing/resources/types_repo/bin/hook.sh
@@ -1,3 +1,3 @@
#!/usr/bin/env bash
-echo $@
+echo "$@"
exit 1
diff --git a/testing/zipapp/make b/testing/zipapp/make
index a644946..8740b2f 100755
--- a/testing/zipapp/make
+++ b/testing/zipapp/make
@@ -99,6 +99,9 @@ def main() -> int:
shebang = '/usr/bin/env python3'
zipapp.create_archive(tmpdir, filename, interpreter=shebang)
+ with open(f'{filename}.sha256sum', 'w') as f:
+ subprocess.check_call(('sha256sum', filename), stdout=f)
+
return 0
diff --git a/tests/clientlib_test.py b/tests/clientlib_test.py
index 2e2f738..bfb754b 100644
--- a/tests/clientlib_test.py
+++ b/tests/clientlib_test.py
@@ -166,6 +166,23 @@ def test_validate_warn_on_unknown_keys_at_top_level(tmpdir, caplog):
]
+def test_validate_optional_sensible_regex(caplog):
+ config_obj = {
+ 'id': 'flake8',
+ 'files': 'dir/*.py',
+ }
+ cfgv.validate(config_obj, CONFIG_HOOK_DICT)
+
+ assert caplog.record_tuples == [
+ (
+ 'pre_commit',
+ logging.WARNING,
+ "The 'files' field in hook 'flake8' is a regex, not a glob -- "
+ "matching '/*' probably isn't what you want here",
+ ),
+ ]
+
+
@pytest.mark.parametrize('fn', (validate_config_main, validate_manifest_main))
def test_mains_not_ok(tmpdir, fn):
not_yaml = tmpdir.join('f.notyaml')
diff --git a/tests/commands/autoupdate_test.py b/tests/commands/autoupdate_test.py
index bd89c1d..b2bad60 100644
--- a/tests/commands/autoupdate_test.py
+++ b/tests/commands/autoupdate_test.py
@@ -1,9 +1,12 @@
import shlex
+from unittest import mock
import pytest
+import yaml
import pre_commit.constants as C
from pre_commit import git
+from pre_commit import util
from pre_commit.commands.autoupdate import _check_hooks_still_exist_at_rev
from pre_commit.commands.autoupdate import autoupdate
from pre_commit.commands.autoupdate import RepositoryCannotBeUpdatedError
@@ -173,6 +176,11 @@ def test_autoupdate_out_of_date_repo(out_of_date, tmpdir, store):
assert cfg.read() == fmt.format(out_of_date.path, out_of_date.head_rev)
+def test_autoupdate_pure_yaml(out_of_date, tmpdir, store):
+ with mock.patch.object(util, 'Dumper', yaml.SafeDumper):
+ test_autoupdate_out_of_date_repo(out_of_date, tmpdir, store)
+
+
def test_autoupdate_only_one_to_update(up_to_date, out_of_date, tmpdir, store):
fmt = (
'repos:\n'
diff --git a/tests/commands/migrate_config_test.py b/tests/commands/migrate_config_test.py
index 6a049d5..f5c89d0 100644
--- a/tests/commands/migrate_config_test.py
+++ b/tests/commands/migrate_config_test.py
@@ -2,24 +2,9 @@ import pytest
import pre_commit.constants as C
from pre_commit.clientlib import InvalidConfigError
-from pre_commit.commands.migrate_config import _indent
from pre_commit.commands.migrate_config import migrate_config
-@pytest.mark.parametrize(
- ('s', 'expected'),
- (
- ('', ''),
- ('a', ' a'),
- ('foo\nbar', ' foo\n bar'),
- ('foo\n\nbar\n', ' foo\n\n bar\n'),
- ('\n\n\n', '\n\n\n'),
- ),
-)
-def test_indent(s, expected):
- assert _indent(s) == expected
-
-
def test_migrate_config_normal_format(tmpdir, capsys):
cfg = tmpdir.join(C.CONFIG_FILE)
cfg.write(
diff --git a/tests/commands/run_test.py b/tests/commands/run_test.py
index 00b4712..914d567 100644
--- a/tests/commands/run_test.py
+++ b/tests/commands/run_test.py
@@ -219,6 +219,19 @@ def test_types_hook_repository(cap_out, store, tempdir_factory):
assert b'bar.notpy' not in printed
+def test_types_or_hook_repository(cap_out, store, tempdir_factory):
+ git_path = make_consuming_repo(tempdir_factory, 'types_or_repo')
+ with cwd(git_path):
+ stage_a_file('bar.notpy')
+ stage_a_file('bar.pxd')
+ stage_a_file('bar.py')
+ ret, printed = _do_run(cap_out, store, git_path, run_opts())
+ assert ret == 1
+ assert b'bar.notpy' not in printed
+ assert b'bar.pxd' in printed
+ assert b'bar.py' in printed
+
+
def test_exclude_types_hook_repository(cap_out, store, tempdir_factory):
git_path = make_consuming_repo(tempdir_factory, 'exclude_types_repo')
with cwd(git_path):
@@ -951,6 +964,27 @@ def test_classifier_does_not_normalize_backslashes_non_windows(tmpdir):
assert classifier.filenames == [r'a/b\c']
+def test_classifier_empty_types_or(tmpdir):
+ tmpdir.join('bar').ensure()
+ os.symlink(tmpdir.join('bar'), tmpdir.join('foo'))
+ with tmpdir.as_cwd():
+ classifier = Classifier(('foo', 'bar'))
+ for_symlink = classifier.by_types(
+ classifier.filenames,
+ types=['symlink'],
+ types_or=[],
+ exclude_types=[],
+ )
+ for_file = classifier.by_types(
+ classifier.filenames,
+ types=['file'],
+ types_or=[],
+ exclude_types=[],
+ )
+ assert for_symlink == ['foo']
+ assert for_file == ['bar']
+
+
@pytest.fixture
def some_filenames():
return (
diff --git a/tests/languages/python_test.py b/tests/languages/python_test.py
index cfe1483..90d1036 100644
--- a/tests/languages/python_test.py
+++ b/tests/languages/python_test.py
@@ -23,6 +23,13 @@ def test_read_pyvenv_cfg(tmpdir):
assert python._read_pyvenv_cfg(pyvenv_cfg) == expected
+def test_read_pyvenv_cfg_non_utf8(tmpdir):
+ pyvenv_cfg = tmpdir.join('pyvenv_cfg')
+ pyvenv_cfg.write_binary('hello = hello john.š\n'.encode())
+ expected = {'hello': 'hello john.š'}
+ assert python._read_pyvenv_cfg(pyvenv_cfg) == expected
+
+
def test_norm_version_expanduser():
home = os.path.expanduser('~')
if os.name == 'nt': # pragma: nt cover
diff --git a/tests/repository_test.py b/tests/repository_test.py
index 3d5093d..d513cb7 100644
--- a/tests/repository_test.py
+++ b/tests/repository_test.py
@@ -901,6 +901,7 @@ def test_manifest_hooks(tempdir_factory, store):
'post-commit', 'manual', 'post-checkout', 'push',
),
types=['file'],
+ types_or=[],
verbose=False,
)
diff --git a/tests/xargs_test.py b/tests/xargs_test.py
index 4f6136e..7e83ef5 100644
--- a/tests/xargs_test.py
+++ b/tests/xargs_test.py
@@ -195,3 +195,12 @@ def test_xargs_color_true_makes_tty():
)
assert retcode == 0
assert out == b'True\n'
+
+
+@pytest.mark.xfail(os.name == 'posix', reason='nt only')
+@pytest.mark.parametrize('filename', ('t.bat', 't.cmd', 'T.CMD'))
+def test_xargs_with_batch_files(tmpdir, filename):
+ f = tmpdir.join(filename)
+ f.write('echo it works\n')
+ retcode, out = xargs.xargs((str(f),), ('x',) * 8192)
+ assert retcode == 0, (retcode, out)