Merging upstream version 4.0.1.
Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
parent
06469805a5
commit
0b6981c15c
44 changed files with 596 additions and 105 deletions
|
@ -1,7 +0,0 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from pre_commit.all_languages import languages
|
||||
|
||||
|
||||
def test_python_venv_is_an_alias_to_python():
|
||||
assert languages['python_venv'] is languages['python']
|
|
@ -256,6 +256,24 @@ def test_validate_optional_sensible_regex_at_local_hook(caplog):
|
|||
]
|
||||
|
||||
|
||||
def test_validate_optional_sensible_regex_at_meta_hook(caplog):
|
||||
config_obj = {
|
||||
'repo': 'meta',
|
||||
'hooks': [{'id': 'identity', 'files': 'dir/*.py'}],
|
||||
}
|
||||
|
||||
cfgv.validate(config_obj, CONFIG_REPO_DICT)
|
||||
|
||||
assert caplog.record_tuples == [
|
||||
(
|
||||
'pre_commit',
|
||||
logging.WARNING,
|
||||
"The 'files' field in hook 'identity' is a regex, not a glob "
|
||||
"-- matching '/*' probably isn't what you want here",
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
('regex', 'warning'),
|
||||
(
|
||||
|
@ -291,6 +309,56 @@ def test_validate_optional_sensible_regex_at_top_level(caplog, regex, warning):
|
|||
assert caplog.record_tuples == [('pre_commit', logging.WARNING, warning)]
|
||||
|
||||
|
||||
def test_warning_for_deprecated_stages(caplog):
|
||||
config_obj = sample_local_config()
|
||||
config_obj['hooks'][0]['stages'] = ['commit', 'push']
|
||||
|
||||
cfgv.validate(config_obj, CONFIG_REPO_DICT)
|
||||
|
||||
assert caplog.record_tuples == [
|
||||
(
|
||||
'pre_commit',
|
||||
logging.WARNING,
|
||||
'hook id `do_not_commit` uses deprecated stage names '
|
||||
'(commit, push) which will be removed in a future version. '
|
||||
'run: `pre-commit migrate-config` to automatically fix this.',
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
def test_no_warning_for_non_deprecated_stages(caplog):
|
||||
config_obj = sample_local_config()
|
||||
config_obj['hooks'][0]['stages'] = ['pre-commit', 'pre-push']
|
||||
|
||||
cfgv.validate(config_obj, CONFIG_REPO_DICT)
|
||||
|
||||
assert caplog.record_tuples == []
|
||||
|
||||
|
||||
def test_warning_for_deprecated_default_stages(caplog):
|
||||
cfg = {'default_stages': ['commit', 'push'], 'repos': []}
|
||||
|
||||
cfgv.validate(cfg, CONFIG_SCHEMA)
|
||||
|
||||
assert caplog.record_tuples == [
|
||||
(
|
||||
'pre_commit',
|
||||
logging.WARNING,
|
||||
'top-level `default_stages` uses deprecated stage names '
|
||||
'(commit, push) which will be removed in a future version. '
|
||||
'run: `pre-commit migrate-config` to automatically fix this.',
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
def test_no_warning_for_non_deprecated_default_stages(caplog):
|
||||
cfg = {'default_stages': ['pre-commit', 'pre-push'], 'repos': []}
|
||||
|
||||
cfgv.validate(cfg, CONFIG_SCHEMA)
|
||||
|
||||
assert caplog.record_tuples == []
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
'manifest_obj',
|
||||
(
|
||||
|
|
|
@ -1,10 +1,26 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from unittest import mock
|
||||
|
||||
import pytest
|
||||
import yaml
|
||||
|
||||
import pre_commit.constants as C
|
||||
from pre_commit.clientlib import InvalidConfigError
|
||||
from pre_commit.commands.migrate_config import migrate_config
|
||||
from pre_commit.yaml import yaml_compose
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True, params=['c', 'pure'])
|
||||
def switch_pyyaml_impl(request):
|
||||
if request.param == 'c':
|
||||
yield
|
||||
else:
|
||||
with mock.patch.dict(
|
||||
yaml_compose.keywords,
|
||||
{'Loader': yaml.SafeLoader},
|
||||
):
|
||||
yield
|
||||
|
||||
|
||||
def test_migrate_config_normal_format(tmpdir, capsys):
|
||||
|
@ -134,6 +150,27 @@ def test_migrate_config_sha_to_rev(tmpdir):
|
|||
)
|
||||
|
||||
|
||||
def test_migrate_config_sha_to_rev_json(tmp_path):
|
||||
contents = """\
|
||||
{"repos": [{
|
||||
"repo": "https://github.com/pre-commit/pre-commit-hooks",
|
||||
"sha": "v1.2.0",
|
||||
"hooks": []
|
||||
}]}
|
||||
"""
|
||||
expected = """\
|
||||
{"repos": [{
|
||||
"repo": "https://github.com/pre-commit/pre-commit-hooks",
|
||||
"rev": "v1.2.0",
|
||||
"hooks": []
|
||||
}]}
|
||||
"""
|
||||
cfg = tmp_path.joinpath('cfg.yaml')
|
||||
cfg.write_text(contents)
|
||||
assert not migrate_config(str(cfg))
|
||||
assert cfg.read_text() == expected
|
||||
|
||||
|
||||
def test_migrate_config_language_python_venv(tmp_path):
|
||||
src = '''\
|
||||
repos:
|
||||
|
@ -167,6 +204,73 @@ repos:
|
|||
assert cfg.read_text() == expected
|
||||
|
||||
|
||||
def test_migrate_config_quoted_python_venv(tmp_path):
|
||||
src = '''\
|
||||
repos:
|
||||
- repo: local
|
||||
hooks:
|
||||
- id: example
|
||||
name: example
|
||||
entry: example
|
||||
language: "python_venv"
|
||||
'''
|
||||
expected = '''\
|
||||
repos:
|
||||
- repo: local
|
||||
hooks:
|
||||
- id: example
|
||||
name: example
|
||||
entry: example
|
||||
language: "python"
|
||||
'''
|
||||
cfg = tmp_path.joinpath('cfg.yaml')
|
||||
cfg.write_text(src)
|
||||
assert migrate_config(str(cfg)) == 0
|
||||
assert cfg.read_text() == expected
|
||||
|
||||
|
||||
def test_migrate_config_default_stages(tmp_path):
|
||||
src = '''\
|
||||
default_stages: [commit, push, merge-commit, commit-msg]
|
||||
repos: []
|
||||
'''
|
||||
expected = '''\
|
||||
default_stages: [pre-commit, pre-push, pre-merge-commit, commit-msg]
|
||||
repos: []
|
||||
'''
|
||||
cfg = tmp_path.joinpath('cfg.yaml')
|
||||
cfg.write_text(src)
|
||||
assert migrate_config(str(cfg)) == 0
|
||||
assert cfg.read_text() == expected
|
||||
|
||||
|
||||
def test_migrate_config_hook_stages(tmp_path):
|
||||
src = '''\
|
||||
repos:
|
||||
- repo: local
|
||||
hooks:
|
||||
- id: example
|
||||
name: example
|
||||
entry: example
|
||||
language: system
|
||||
stages: ["commit", "push", "merge-commit", "commit-msg"]
|
||||
'''
|
||||
expected = '''\
|
||||
repos:
|
||||
- repo: local
|
||||
hooks:
|
||||
- id: example
|
||||
name: example
|
||||
entry: example
|
||||
language: system
|
||||
stages: ["pre-commit", "pre-push", "pre-merge-commit", "commit-msg"]
|
||||
'''
|
||||
cfg = tmp_path.joinpath('cfg.yaml')
|
||||
cfg.write_text(src)
|
||||
assert migrate_config(str(cfg)) == 0
|
||||
assert cfg.read_text() == expected
|
||||
|
||||
|
||||
def test_migrate_config_invalid_yaml(tmpdir):
|
||||
contents = '['
|
||||
cfg = tmpdir.join(C.CONFIG_FILE)
|
||||
|
|
|
@ -2,7 +2,6 @@ from __future__ import annotations
|
|||
|
||||
import functools
|
||||
import io
|
||||
import logging
|
||||
import os.path
|
||||
from unittest import mock
|
||||
|
||||
|
@ -203,12 +202,6 @@ def store(tempdir_factory):
|
|||
yield Store(os.path.join(tempdir_factory.get(), '.pre-commit'))
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def log_info_mock():
|
||||
with mock.patch.object(logging.getLogger('pre_commit'), 'info') as mck:
|
||||
yield mck
|
||||
|
||||
|
||||
class Fixture:
|
||||
def __init__(self, stream: io.BytesIO) -> None:
|
||||
self._stream = stream
|
||||
|
|
|
@ -80,24 +80,6 @@ def _test_hook_repo(
|
|||
assert out == expected
|
||||
|
||||
|
||||
def test_python_venv_deprecation(store, caplog):
|
||||
config = {
|
||||
'repo': 'local',
|
||||
'hooks': [{
|
||||
'id': 'example',
|
||||
'name': 'example',
|
||||
'language': 'python_venv',
|
||||
'entry': 'echo hi',
|
||||
}],
|
||||
}
|
||||
_get_hook(config, store, 'example')
|
||||
assert caplog.messages[-1] == (
|
||||
'`repo: local` uses deprecated `language: python_venv`. '
|
||||
'This is an alias for `language: python`. '
|
||||
'Often `pre-commit autoupdate --repo local` will fix this.'
|
||||
)
|
||||
|
||||
|
||||
def test_system_hook_with_spaces(tempdir_factory, store):
|
||||
_test_hook_repo(
|
||||
tempdir_factory, store, 'system_hook_with_spaces_repo',
|
||||
|
@ -240,16 +222,16 @@ def test_unknown_keys(store, caplog):
|
|||
assert msg == 'Unexpected key(s) present on local => too-much: foo, hello'
|
||||
|
||||
|
||||
def test_reinstall(tempdir_factory, store, log_info_mock):
|
||||
def test_reinstall(tempdir_factory, store, caplog):
|
||||
path = make_repo(tempdir_factory, 'python_hooks_repo')
|
||||
config = make_config_from_repo(path)
|
||||
_get_hook(config, store, 'foo')
|
||||
# We print some logging during clone (1) + install (3)
|
||||
assert log_info_mock.call_count == 4
|
||||
log_info_mock.reset_mock()
|
||||
assert len(caplog.record_tuples) == 4
|
||||
caplog.clear()
|
||||
# Reinstall on another run should not trigger another install
|
||||
_get_hook(config, store, 'foo')
|
||||
assert log_info_mock.call_count == 0
|
||||
assert len(caplog.record_tuples) == 0
|
||||
|
||||
|
||||
def test_control_c_control_c_on_install(tempdir_factory, store):
|
||||
|
|
|
@ -1,12 +1,15 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
import os.path
|
||||
import shlex
|
||||
import sqlite3
|
||||
import stat
|
||||
from unittest import mock
|
||||
|
||||
import pytest
|
||||
|
||||
import pre_commit.constants as C
|
||||
from pre_commit import git
|
||||
from pre_commit.store import _get_default_directory
|
||||
from pre_commit.store import _LOCAL_RESOURCES
|
||||
|
@ -65,7 +68,7 @@ def test_store_init(store):
|
|||
assert text_line in readme_contents
|
||||
|
||||
|
||||
def test_clone(store, tempdir_factory, log_info_mock):
|
||||
def test_clone(store, tempdir_factory, caplog):
|
||||
path = git_dir(tempdir_factory)
|
||||
with cwd(path):
|
||||
git_commit()
|
||||
|
@ -74,7 +77,7 @@ def test_clone(store, tempdir_factory, log_info_mock):
|
|||
|
||||
ret = store.clone(path, rev)
|
||||
# Should have printed some stuff
|
||||
assert log_info_mock.call_args_list[0][0][0].startswith(
|
||||
assert caplog.record_tuples[0][-1].startswith(
|
||||
'Initializing environment for ',
|
||||
)
|
||||
|
||||
|
@ -91,6 +94,72 @@ def test_clone(store, tempdir_factory, log_info_mock):
|
|||
assert store.select_all_repos() == [(path, rev, ret)]
|
||||
|
||||
|
||||
def test_warning_for_deprecated_stages_on_init(store, tempdir_factory, caplog):
|
||||
manifest = '''\
|
||||
- id: hook1
|
||||
name: hook1
|
||||
language: system
|
||||
entry: echo hook1
|
||||
stages: [commit, push]
|
||||
- id: hook2
|
||||
name: hook2
|
||||
language: system
|
||||
entry: echo hook2
|
||||
stages: [push, merge-commit]
|
||||
'''
|
||||
|
||||
path = git_dir(tempdir_factory)
|
||||
with open(os.path.join(path, C.MANIFEST_FILE), 'w') as f:
|
||||
f.write(manifest)
|
||||
cmd_output('git', 'add', '.', cwd=path)
|
||||
git_commit(cwd=path)
|
||||
rev = git.head_rev(path)
|
||||
|
||||
store.clone(path, rev)
|
||||
assert caplog.record_tuples[1] == (
|
||||
'pre_commit',
|
||||
logging.WARNING,
|
||||
f'repo `{path}` uses deprecated stage names '
|
||||
f'(commit, push, merge-commit) which will be removed in a future '
|
||||
f'version. '
|
||||
f'Hint: often `pre-commit autoupdate --repo {shlex.quote(path)}` '
|
||||
f'will fix this. '
|
||||
f'if it does not -- consider reporting an issue to that repo.',
|
||||
)
|
||||
|
||||
# should not re-warn
|
||||
caplog.clear()
|
||||
store.clone(path, rev)
|
||||
assert caplog.record_tuples == []
|
||||
|
||||
|
||||
def test_no_warning_for_non_deprecated_stages_on_init(
|
||||
store, tempdir_factory, caplog,
|
||||
):
|
||||
manifest = '''\
|
||||
- id: hook1
|
||||
name: hook1
|
||||
language: system
|
||||
entry: echo hook1
|
||||
stages: [pre-commit, pre-push]
|
||||
- id: hook2
|
||||
name: hook2
|
||||
language: system
|
||||
entry: echo hook2
|
||||
stages: [pre-push, pre-merge-commit]
|
||||
'''
|
||||
|
||||
path = git_dir(tempdir_factory)
|
||||
with open(os.path.join(path, C.MANIFEST_FILE), 'w') as f:
|
||||
f.write(manifest)
|
||||
cmd_output('git', 'add', '.', cwd=path)
|
||||
git_commit(cwd=path)
|
||||
rev = git.head_rev(path)
|
||||
|
||||
store.clone(path, rev)
|
||||
assert logging.WARNING not in {tup[1] for tup in caplog.record_tuples}
|
||||
|
||||
|
||||
def test_clone_cleans_up_on_checkout_failure(store):
|
||||
with pytest.raises(Exception) as excinfo:
|
||||
# This raises an exception because you can't clone something that
|
||||
|
@ -118,7 +187,7 @@ def test_clone_when_repo_already_exists(store):
|
|||
|
||||
def test_clone_shallow_failure_fallback_to_complete(
|
||||
store, tempdir_factory,
|
||||
log_info_mock,
|
||||
caplog,
|
||||
):
|
||||
path = git_dir(tempdir_factory)
|
||||
with cwd(path):
|
||||
|
@ -134,7 +203,7 @@ def test_clone_shallow_failure_fallback_to_complete(
|
|||
ret = store.clone(path, rev)
|
||||
|
||||
# Should have printed some stuff
|
||||
assert log_info_mock.call_args_list[0][0][0].startswith(
|
||||
assert caplog.record_tuples[0][-1].startswith(
|
||||
'Initializing environment for ',
|
||||
)
|
||||
|
||||
|
|
47
tests/yaml_rewrite_test.py
Normal file
47
tests/yaml_rewrite_test.py
Normal file
|
@ -0,0 +1,47 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import pytest
|
||||
|
||||
from pre_commit.yaml import yaml_compose
|
||||
from pre_commit.yaml_rewrite import MappingKey
|
||||
from pre_commit.yaml_rewrite import MappingValue
|
||||
from pre_commit.yaml_rewrite import match
|
||||
from pre_commit.yaml_rewrite import SequenceItem
|
||||
|
||||
|
||||
def test_match_produces_scalar_values_only():
|
||||
src = '''\
|
||||
- name: foo
|
||||
- name: [not, foo] # not a scalar: should be skipped!
|
||||
- name: bar
|
||||
'''
|
||||
matcher = (SequenceItem(), MappingValue('name'))
|
||||
ret = [n.value for n in match(yaml_compose(src), matcher)]
|
||||
assert ret == ['foo', 'bar']
|
||||
|
||||
|
||||
@pytest.mark.parametrize('cls', (MappingKey, MappingValue))
|
||||
def test_mapping_not_a_map(cls):
|
||||
m = cls('s')
|
||||
assert list(m.match(yaml_compose('[foo]'))) == []
|
||||
|
||||
|
||||
def test_sequence_item_not_a_sequence():
|
||||
assert list(SequenceItem().match(yaml_compose('s: val'))) == []
|
||||
|
||||
|
||||
def test_mapping_key():
|
||||
m = MappingKey('s')
|
||||
ret = [n.value for n in m.match(yaml_compose('s: val\nt: val2'))]
|
||||
assert ret == ['s']
|
||||
|
||||
|
||||
def test_mapping_value():
|
||||
m = MappingValue('s')
|
||||
ret = [n.value for n in m.match(yaml_compose('s: val\nt: val2'))]
|
||||
assert ret == ['val']
|
||||
|
||||
|
||||
def test_sequence_item():
|
||||
ret = [n.value for n in SequenceItem().match(yaml_compose('[a, b, c]'))]
|
||||
assert ret == ['a', 'b', 'c']
|
Loading…
Add table
Add a link
Reference in a new issue