mirror of
https://github.com/pre-commit/pre-commit.git
synced 2026-02-17 08:14:42 +04:00
Add pcre type.
This commit is contained in:
parent
38673941dc
commit
2cfd2818b5
11 changed files with 184 additions and 10 deletions
|
|
@ -1,6 +1,7 @@
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from pre_commit.languages import node
|
from pre_commit.languages import node
|
||||||
|
from pre_commit.languages import pcre
|
||||||
from pre_commit.languages import python
|
from pre_commit.languages import python
|
||||||
from pre_commit.languages import ruby
|
from pre_commit.languages import ruby
|
||||||
from pre_commit.languages import script
|
from pre_commit.languages import script
|
||||||
|
|
@ -36,6 +37,7 @@ from pre_commit.languages import system
|
||||||
|
|
||||||
languages = {
|
languages = {
|
||||||
'node': node,
|
'node': node,
|
||||||
|
'pcre': pcre,
|
||||||
'python': python,
|
'python': python,
|
||||||
'ruby': ruby,
|
'ruby': ruby,
|
||||||
'script': script,
|
'script': script,
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,14 @@
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
|
||||||
|
def file_args_to_stdin(file_args):
|
||||||
|
return '\n'.join(list(file_args) + [''])
|
||||||
|
|
||||||
|
|
||||||
def run_hook(env, hook, file_args):
|
def run_hook(env, hook, file_args):
|
||||||
return env.run(
|
return env.run(
|
||||||
' '.join(['xargs', hook['entry']] + hook['args']),
|
' '.join(['xargs', hook['entry']] + hook['args']),
|
||||||
stdin='\n'.join(list(file_args) + ['']),
|
stdin=file_args_to_stdin(file_args),
|
||||||
retcode=None,
|
retcode=None,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
27
pre_commit/languages/pcre.py
Normal file
27
pre_commit/languages/pcre.py
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from pre_commit.languages.helpers import file_args_to_stdin
|
||||||
|
from pre_commit.util import shell_escape
|
||||||
|
|
||||||
|
|
||||||
|
ENVIRONMENT_DIR = None
|
||||||
|
|
||||||
|
|
||||||
|
def install_environment(repo_cmd_runner, version='default'):
|
||||||
|
"""Installation for pcre type is a noop."""
|
||||||
|
raise AssertionError('Cannot install pcre repo.')
|
||||||
|
|
||||||
|
|
||||||
|
def run_hook(repo_cmd_runner, hook, file_args):
|
||||||
|
# For PCRE the entry is the regular expression to match
|
||||||
|
return repo_cmd_runner.run(
|
||||||
|
[
|
||||||
|
'xargs', 'sh', '-c',
|
||||||
|
# Grep usually returns 0 for matches, and nonzero for non-matches
|
||||||
|
# so we flip it here.
|
||||||
|
'! grep -H -n -P {0} $@'.format(shell_escape(hook['entry'])),
|
||||||
|
'--',
|
||||||
|
],
|
||||||
|
stdin=file_args_to_stdin(file_args),
|
||||||
|
retcode=None,
|
||||||
|
)
|
||||||
|
|
@ -1,16 +1,20 @@
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from pre_commit.languages.helpers import file_args_to_stdin
|
||||||
|
|
||||||
|
|
||||||
ENVIRONMENT_DIR = None
|
ENVIRONMENT_DIR = None
|
||||||
|
|
||||||
|
|
||||||
def install_environment(repo_cmd_runner, version='default'):
|
def install_environment(repo_cmd_runner, version='default'):
|
||||||
"""Installation for script type is a noop."""
|
"""Installation for script type is a noop."""
|
||||||
|
raise AssertionError('Cannot install script repo.')
|
||||||
|
|
||||||
|
|
||||||
def run_hook(repo_cmd_runner, hook, file_args):
|
def run_hook(repo_cmd_runner, hook, file_args):
|
||||||
return repo_cmd_runner.run(
|
return repo_cmd_runner.run(
|
||||||
['xargs', '{{prefix}}{0}'.format(hook['entry'])] + hook['args'],
|
['xargs', '{{prefix}}{0}'.format(hook['entry'])] + hook['args'],
|
||||||
# TODO: this is duplicated in pre_commit/languages/helpers.py
|
# TODO: this is duplicated in pre_commit/languages/helpers.py
|
||||||
stdin='\n'.join(list(file_args) + ['']),
|
stdin=file_args_to_stdin(file_args),
|
||||||
retcode=None,
|
retcode=None,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -2,18 +2,20 @@ from __future__ import unicode_literals
|
||||||
|
|
||||||
import shlex
|
import shlex
|
||||||
|
|
||||||
|
from pre_commit.languages.helpers import file_args_to_stdin
|
||||||
|
|
||||||
|
|
||||||
ENVIRONMENT_DIR = None
|
ENVIRONMENT_DIR = None
|
||||||
|
|
||||||
|
|
||||||
def install_environment(repo_cmd_runner, version='default'):
|
def install_environment(repo_cmd_runner, version='default'):
|
||||||
"""Installation for system type is a noop."""
|
"""Installation for system type is a noop."""
|
||||||
|
raise AssertionError('Cannot install system repo.')
|
||||||
|
|
||||||
|
|
||||||
def run_hook(repo_cmd_runner, hook, file_args):
|
def run_hook(repo_cmd_runner, hook, file_args):
|
||||||
return repo_cmd_runner.run(
|
return repo_cmd_runner.run(
|
||||||
['xargs'] + shlex.split(hook['entry']) + hook['args'],
|
['xargs'] + shlex.split(hook['entry']) + hook['args'],
|
||||||
# TODO: this is duplicated in pre_commit/languages/helpers.py
|
stdin=file_args_to_stdin(file_args),
|
||||||
stdin='\n'.join(list(file_args) + ['']),
|
|
||||||
retcode=None,
|
retcode=None,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -49,7 +49,10 @@ def clean_path_on_failure(path):
|
||||||
raise
|
raise
|
||||||
|
|
||||||
|
|
||||||
# TODO: asottile.contextlib this with a forward port of nested
|
|
||||||
@contextlib.contextmanager
|
@contextlib.contextmanager
|
||||||
def noop_context():
|
def noop_context():
|
||||||
yield
|
yield
|
||||||
|
|
||||||
|
|
||||||
|
def shell_escape(arg):
|
||||||
|
return "'" + arg.replace("'", "'\"'\"'".strip()) + "'"
|
||||||
|
|
|
||||||
10
testing/resources/pcre_hooks_repo/hooks.yaml
Normal file
10
testing/resources/pcre_hooks_repo/hooks.yaml
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
- id: regex-with-quotes
|
||||||
|
name: Regex with quotes
|
||||||
|
entry: "foo'bar"
|
||||||
|
language: pcre
|
||||||
|
files: ''
|
||||||
|
- id: other-regex
|
||||||
|
name: Other regex
|
||||||
|
entry: ^\[INFO\]
|
||||||
|
language: pcre
|
||||||
|
files: ''
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
import inspect
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from pre_commit.languages.all import all_languages
|
from pre_commit.languages.all import all_languages
|
||||||
|
|
@ -7,7 +8,27 @@ from pre_commit.languages.all import languages
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('language', all_languages)
|
@pytest.mark.parametrize('language', all_languages)
|
||||||
def test_all_languages_support_interface(language):
|
def test_install_environment_argspec(language):
|
||||||
assert hasattr(languages[language], 'install_environment')
|
expected_argspec = inspect.ArgSpec(
|
||||||
assert hasattr(languages[language], 'run_hook')
|
args=['repo_cmd_runner', 'version'],
|
||||||
|
varargs=None,
|
||||||
|
keywords=None,
|
||||||
|
defaults=('default',),
|
||||||
|
)
|
||||||
|
argspec = inspect.getargspec(languages[language].install_environment)
|
||||||
|
assert argspec == expected_argspec
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('language', all_languages)
|
||||||
|
def test_ENVIRONMENT_DIR(language):
|
||||||
assert hasattr(languages[language], 'ENVIRONMENT_DIR')
|
assert hasattr(languages[language], 'ENVIRONMENT_DIR')
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('language', all_languages)
|
||||||
|
def test_run_hook_argpsec(language):
|
||||||
|
expected_argspec = inspect.ArgSpec(
|
||||||
|
args=['repo_cmd_runner', 'hook', 'file_args'],
|
||||||
|
varargs=None, keywords=None, defaults=None,
|
||||||
|
)
|
||||||
|
argspec = inspect.getargspec(languages[language].run_hook)
|
||||||
|
assert argspec == expected_argspec
|
||||||
|
|
|
||||||
16
tests/languages/helpers_test.py
Normal file
16
tests/languages/helpers_test.py
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
from __future__ import absolute_import
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from pre_commit.languages.helpers import file_args_to_stdin
|
||||||
|
|
||||||
|
|
||||||
|
def test_file_args_to_stdin_empty():
|
||||||
|
assert file_args_to_stdin([]) == ''
|
||||||
|
|
||||||
|
|
||||||
|
def test_file_args_to_stdin_some():
|
||||||
|
assert file_args_to_stdin(['foo', 'bar']) == 'foo\nbar\n'
|
||||||
|
|
||||||
|
|
||||||
|
def test_file_args_to_stdin_tuple():
|
||||||
|
assert file_args_to_stdin(('foo', 'bar')) == 'foo\nbar\n'
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
from __future__ import absolute_import
|
from __future__ import absolute_import
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
import io
|
||||||
import mock
|
import mock
|
||||||
import os.path
|
import os.path
|
||||||
import pytest
|
import pytest
|
||||||
|
|
@ -25,12 +26,20 @@ def test_install_python_repo_in_env(tmpdir_factory, store):
|
||||||
assert os.path.exists(os.path.join(store.directory, repo.sha, 'py_env'))
|
assert os.path.exists(os.path.join(store.directory, repo.sha, 'py_env'))
|
||||||
|
|
||||||
|
|
||||||
def _test_hook_repo(tmpdir_factory, store, repo_path, hook_id, args, expected):
|
def _test_hook_repo(
|
||||||
|
tmpdir_factory,
|
||||||
|
store,
|
||||||
|
repo_path,
|
||||||
|
hook_id,
|
||||||
|
args,
|
||||||
|
expected,
|
||||||
|
expected_return_code=0,
|
||||||
|
):
|
||||||
path = make_repo(tmpdir_factory, repo_path)
|
path = make_repo(tmpdir_factory, repo_path)
|
||||||
config = make_config_from_repo(path)
|
config = make_config_from_repo(path)
|
||||||
repo = Repository.create(config, store)
|
repo = Repository.create(config, store)
|
||||||
ret = repo.run_hook(hook_id, args)
|
ret = repo.run_hook(hook_id, args)
|
||||||
assert ret[0] == 0
|
assert ret[0] == expected_return_code
|
||||||
assert ret[1] == expected
|
assert ret[1] == expected
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -102,6 +111,69 @@ def test_run_a_script_hook(tmpdir_factory, store):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.integration
|
||||||
|
def test_pcre_hook_no_match(tmpdir_factory, store):
|
||||||
|
path = git_dir(tmpdir_factory)
|
||||||
|
with local.cwd(path):
|
||||||
|
with io.open('herp', 'w') as herp:
|
||||||
|
herp.write('foo')
|
||||||
|
|
||||||
|
with io.open('derp', 'w') as derp:
|
||||||
|
derp.write('bar')
|
||||||
|
|
||||||
|
_test_hook_repo(
|
||||||
|
tmpdir_factory, store, 'pcre_hooks_repo',
|
||||||
|
'regex-with-quotes', ['herp', 'derp'], '',
|
||||||
|
)
|
||||||
|
|
||||||
|
_test_hook_repo(
|
||||||
|
tmpdir_factory, store, 'pcre_hooks_repo',
|
||||||
|
'other-regex', ['herp', 'derp'], '',
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.integration
|
||||||
|
def test_pcre_hook_matching(tmpdir_factory, store):
|
||||||
|
path = git_dir(tmpdir_factory)
|
||||||
|
with local.cwd(path):
|
||||||
|
with io.open('herp', 'w') as herp:
|
||||||
|
herp.write("\nherpfoo'bard\n")
|
||||||
|
|
||||||
|
with io.open('derp', 'w') as derp:
|
||||||
|
derp.write('[INFO] information yo\n')
|
||||||
|
|
||||||
|
_test_hook_repo(
|
||||||
|
tmpdir_factory, store, 'pcre_hooks_repo',
|
||||||
|
'regex-with-quotes', ['herp', 'derp'], "herp:2:herpfoo'bard\n",
|
||||||
|
expected_return_code=123,
|
||||||
|
)
|
||||||
|
|
||||||
|
_test_hook_repo(
|
||||||
|
tmpdir_factory, store, 'pcre_hooks_repo',
|
||||||
|
'other-regex', ['herp', 'derp'], 'derp:1:[INFO] information yo\n',
|
||||||
|
expected_return_code=123,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.integration
|
||||||
|
def test_pcre_many_files(tmpdir_factory, store):
|
||||||
|
# This is intended to simulate lots of passing files and one failing file
|
||||||
|
# to make sure it still fails. This is not the case when naively using
|
||||||
|
# a system hook with `grep -H -n '...'` and expected_return_code=123.
|
||||||
|
path = git_dir(tmpdir_factory)
|
||||||
|
with local.cwd(path):
|
||||||
|
with io.open('herp', 'w') as herp:
|
||||||
|
herp.write('[INFO] info\n')
|
||||||
|
|
||||||
|
_test_hook_repo(
|
||||||
|
tmpdir_factory, store, 'pcre_hooks_repo',
|
||||||
|
'other-regex',
|
||||||
|
['/dev/null'] * 15000 + ['herp'],
|
||||||
|
'herp:1:[INFO] info\n',
|
||||||
|
expected_return_code=123,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.integration
|
@pytest.mark.integration
|
||||||
def test_cwd_of_hook(tmpdir_factory, store):
|
def test_cwd_of_hook(tmpdir_factory, store):
|
||||||
# Note: this doubles as a test for `system` hooks
|
# Note: this doubles as a test for `system` hooks
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ from plumbum import local
|
||||||
from pre_commit.util import clean_path_on_failure
|
from pre_commit.util import clean_path_on_failure
|
||||||
from pre_commit.util import entry
|
from pre_commit.util import entry
|
||||||
from pre_commit.util import memoize_by_cwd
|
from pre_commit.util import memoize_by_cwd
|
||||||
|
from pre_commit.util import shell_escape
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
|
|
@ -99,3 +100,15 @@ def test_clean_path_on_failure_cleans_for_system_exit(in_tmpdir):
|
||||||
raise MySystemExit
|
raise MySystemExit
|
||||||
|
|
||||||
assert not os.path.exists('foo')
|
assert not os.path.exists('foo')
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
('input_str', 'expected'),
|
||||||
|
(
|
||||||
|
('', "''"),
|
||||||
|
('foo"bar', "'foo\"bar'"),
|
||||||
|
("foo'bar", "'foo'\"'\"'bar'")
|
||||||
|
),
|
||||||
|
)
|
||||||
|
def test_shell_escape(input_str, expected):
|
||||||
|
assert shell_escape(input_str) == expected
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue