mirror of
https://github.com/pre-commit/pre-commit.git
synced 2026-02-20 01:24:42 +04:00
commit
47c68820d0
11 changed files with 110 additions and 28 deletions
|
|
@ -2,6 +2,8 @@ language: python
|
||||||
env: # These should match the tox env list
|
env: # These should match the tox env list
|
||||||
- TOXENV=py26
|
- TOXENV=py26
|
||||||
- TOXENV=py27
|
- TOXENV=py27
|
||||||
|
- TOXENV=py33
|
||||||
|
- TOXENV=pypy
|
||||||
install: pip install tox --use-mirrors
|
install: pip install tox --use-mirrors
|
||||||
script: tox
|
script: tox
|
||||||
|
|
||||||
|
|
|
||||||
78
pre_commit/five.py
Normal file
78
pre_commit/five.py
Normal file
|
|
@ -0,0 +1,78 @@
|
||||||
|
"""five: six, redux"""
|
||||||
|
# pylint:disable=invalid-name
|
||||||
|
PY2 = (str is bytes)
|
||||||
|
PY3 = (str is not bytes)
|
||||||
|
|
||||||
|
# provide a symettrical `text` type to `bytes`
|
||||||
|
if PY2:
|
||||||
|
text = unicode # flake8: noqa
|
||||||
|
else:
|
||||||
|
text = str
|
||||||
|
|
||||||
|
|
||||||
|
def n(obj):
|
||||||
|
"""Produce a native string.
|
||||||
|
|
||||||
|
Similar in behavior to str(), but uses US-ASCII encoding when necessary.
|
||||||
|
"""
|
||||||
|
if isinstance(obj, str):
|
||||||
|
return obj
|
||||||
|
elif PY2 and isinstance(obj, text):
|
||||||
|
return obj.encode('US-ASCII')
|
||||||
|
elif PY3 and isinstance(obj, bytes):
|
||||||
|
return obj.decode('US-ASCII')
|
||||||
|
else:
|
||||||
|
return str(obj)
|
||||||
|
|
||||||
|
|
||||||
|
def u(obj):
|
||||||
|
"""Produces text.
|
||||||
|
|
||||||
|
Similar in behavior to str() in python3 or unicode() in python2,
|
||||||
|
but uses US-ASCII encoding when necessary.
|
||||||
|
"""
|
||||||
|
if isinstance(obj, text):
|
||||||
|
return obj
|
||||||
|
elif isinstance(obj, bytes):
|
||||||
|
return obj.decode('US-ASCII')
|
||||||
|
else:
|
||||||
|
return text(obj)
|
||||||
|
|
||||||
|
|
||||||
|
def b(obj):
|
||||||
|
"""Produces bytes.
|
||||||
|
|
||||||
|
Similar in behavior to bytes(), but uses US-ASCII encoding when necessary.
|
||||||
|
"""
|
||||||
|
if isinstance(obj, bytes):
|
||||||
|
return obj
|
||||||
|
elif isinstance(obj, text):
|
||||||
|
return obj.encode('US-ASCII')
|
||||||
|
else:
|
||||||
|
return bytes(obj)
|
||||||
|
|
||||||
|
|
||||||
|
def udict(*args, **kwargs):
|
||||||
|
"""Similar to dict(), but keyword-keys are text."""
|
||||||
|
kwargs = dict([
|
||||||
|
(u(key), val)
|
||||||
|
for key, val in kwargs.items()
|
||||||
|
])
|
||||||
|
|
||||||
|
return dict(*args, **kwargs)
|
||||||
|
|
||||||
|
def ndict(*args, **kwargs):
|
||||||
|
"""Similar to dict(), but keyword-keys are forced to native strings."""
|
||||||
|
# I hate this :(
|
||||||
|
kwargs = dict([
|
||||||
|
(n(key), val)
|
||||||
|
for key, val in kwargs.items()
|
||||||
|
])
|
||||||
|
|
||||||
|
return dict(*args, **kwargs)
|
||||||
|
|
||||||
|
def open(*args, **kwargs):
|
||||||
|
"""Override the builtin open() to return text and use utf8 by default."""
|
||||||
|
from io import open
|
||||||
|
kwargs.setdefault('encoding', 'UTF-8')
|
||||||
|
return open(*args, **kwargs)
|
||||||
|
|
@ -21,7 +21,7 @@ def extend_validator_cls(validator_cls, modify):
|
||||||
|
|
||||||
|
|
||||||
def default_values(properties, instance):
|
def default_values(properties, instance):
|
||||||
for property, subschema in properties.iteritems():
|
for property, subschema in properties.items():
|
||||||
if 'default' in subschema:
|
if 'default' in subschema:
|
||||||
instance.setdefault(
|
instance.setdefault(
|
||||||
property, copy.deepcopy(subschema['default']),
|
property, copy.deepcopy(subschema['default']),
|
||||||
|
|
@ -29,7 +29,7 @@ def default_values(properties, instance):
|
||||||
|
|
||||||
|
|
||||||
def remove_default_values(properties, instance):
|
def remove_default_values(properties, instance):
|
||||||
for property, subschema in properties.iteritems():
|
for property, subschema in properties.items():
|
||||||
if (
|
if (
|
||||||
'default' in subschema and
|
'default' in subschema and
|
||||||
instance.get(property) == subschema['default']
|
instance.get(property) == subschema['default']
|
||||||
|
|
|
||||||
|
|
@ -14,12 +14,13 @@ LOG_LEVEL_COLORS = {
|
||||||
|
|
||||||
|
|
||||||
class LoggingHandler(logging.Handler):
|
class LoggingHandler(logging.Handler):
|
||||||
def __init__(self, use_color):
|
def __init__(self, use_color, print_fn=print):
|
||||||
logging.Handler.__init__(self)
|
logging.Handler.__init__(self)
|
||||||
self.use_color = use_color
|
self.use_color = use_color
|
||||||
|
self.__print_fn = print_fn
|
||||||
|
|
||||||
def emit(self, record):
|
def emit(self, record):
|
||||||
print(
|
self.__print_fn(
|
||||||
u'{0}{1}'.format(
|
u'{0}{1}'.format(
|
||||||
color.format_color(
|
color.format_color(
|
||||||
'[{0}]'.format(record.levelname),
|
'[{0}]'.format(record.levelname),
|
||||||
|
|
|
||||||
|
|
@ -55,6 +55,9 @@ class PrefixedCommandRunner(object):
|
||||||
'stdout': subprocess.PIPE,
|
'stdout': subprocess.PIPE,
|
||||||
'stderr': subprocess.PIPE,
|
'stderr': subprocess.PIPE,
|
||||||
}
|
}
|
||||||
|
if stdin is not None:
|
||||||
|
stdin = stdin.encode('utf-8')
|
||||||
|
|
||||||
popen_kwargs.update(kwargs)
|
popen_kwargs.update(kwargs)
|
||||||
self._create_path_if_not_exists()
|
self._create_path_if_not_exists()
|
||||||
replaced_cmd = _replace_cmd(cmd, prefix=self.prefix_dir)
|
replaced_cmd = _replace_cmd(cmd, prefix=self.prefix_dir)
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ from asottile.ordereddict import OrderedDict
|
||||||
from plumbum import local
|
from plumbum import local
|
||||||
|
|
||||||
import pre_commit.constants as C
|
import pre_commit.constants as C
|
||||||
|
from pre_commit import five
|
||||||
from pre_commit.clientlib.validate_manifest import load_manifest
|
from pre_commit.clientlib.validate_manifest import load_manifest
|
||||||
from pre_commit.hooks_workspace import in_hooks_workspace
|
from pre_commit.hooks_workspace import in_hooks_workspace
|
||||||
from pre_commit.languages.all import languages
|
from pre_commit.languages.all import languages
|
||||||
|
|
@ -70,7 +71,7 @@ class Repository(object):
|
||||||
logger.info('Installing environment for {0}.'.format(self.repo_url))
|
logger.info('Installing environment for {0}.'.format(self.repo_url))
|
||||||
logger.info('Once installed this environment will be reused.')
|
logger.info('Once installed this environment will be reused.')
|
||||||
logger.info('This may take a few minutes...')
|
logger.info('This may take a few minutes...')
|
||||||
with clean_path_on_failure(unicode(local.path(self.sha))):
|
with clean_path_on_failure(five.u(local.path(self.sha))):
|
||||||
local['git']['clone', '--no-checkout', self.repo_url, self.sha]()
|
local['git']['clone', '--no-checkout', self.repo_url, self.sha]()
|
||||||
with self.in_checkout():
|
with self.in_checkout():
|
||||||
local['git']['checkout', self.sha]()
|
local['git']['checkout', self.sha]()
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,6 @@ def _run_single_hook(runner, repository, hook_id, args):
|
||||||
get_filenames(hook['files'], hook['exclude']),
|
get_filenames(hook['files'], hook['exclude']),
|
||||||
)
|
)
|
||||||
|
|
||||||
output = '\n'.join([stdout, stderr]).strip()
|
|
||||||
if retcode != repository.hooks[hook_id]['expected_return_value']:
|
if retcode != repository.hooks[hook_id]['expected_return_value']:
|
||||||
retcode = 1
|
retcode = 1
|
||||||
print_color = color.RED
|
print_color = color.RED
|
||||||
|
|
@ -57,8 +56,12 @@ def _run_single_hook(runner, repository, hook_id, args):
|
||||||
|
|
||||||
print(color.format_color(pass_fail, print_color, args.color))
|
print(color.format_color(pass_fail, print_color, args.color))
|
||||||
|
|
||||||
if output and (retcode or args.verbose):
|
if (stdout or stderr) and (retcode or args.verbose):
|
||||||
print('\n' + output)
|
print()
|
||||||
|
for output in (stdout, stderr):
|
||||||
|
if output.strip():
|
||||||
|
print(output.strip())
|
||||||
|
print()
|
||||||
|
|
||||||
return retcode
|
return retcode
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,9 @@
|
||||||
import __builtin__
|
|
||||||
import mock
|
import mock
|
||||||
import pytest
|
|
||||||
|
|
||||||
from pre_commit import color
|
from pre_commit import color
|
||||||
from pre_commit.logging_handler import LoggingHandler
|
from pre_commit.logging_handler import LoggingHandler
|
||||||
|
|
||||||
|
|
||||||
@pytest.yield_fixture
|
|
||||||
def print_mock():
|
|
||||||
with mock.patch.object(__builtin__, 'print', autospec=True) as print_mock:
|
|
||||||
yield print_mock
|
|
||||||
|
|
||||||
|
|
||||||
class FakeLogRecord(object):
|
class FakeLogRecord(object):
|
||||||
def __init__(self, message, levelname, levelno):
|
def __init__(self, message, levelname, levelno):
|
||||||
self.message = message
|
self.message = message
|
||||||
|
|
@ -22,16 +14,18 @@ class FakeLogRecord(object):
|
||||||
return self.message
|
return self.message
|
||||||
|
|
||||||
|
|
||||||
def test_logging_handler_color(print_mock):
|
def test_logging_handler_color():
|
||||||
handler = LoggingHandler(True)
|
print_mock = mock.Mock()
|
||||||
|
handler = LoggingHandler(True, print_mock)
|
||||||
handler.emit(FakeLogRecord('hi', 'WARNING', 30))
|
handler.emit(FakeLogRecord('hi', 'WARNING', 30))
|
||||||
print_mock.assert_called_once_with(
|
print_mock.assert_called_once_with(
|
||||||
color.YELLOW + '[WARNING]' + color.NORMAL + ' hi',
|
color.YELLOW + '[WARNING]' + color.NORMAL + ' hi',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_logging_handler_no_color(print_mock):
|
def test_logging_handler_no_color():
|
||||||
handler = LoggingHandler(False)
|
print_mock = mock.Mock()
|
||||||
|
handler = LoggingHandler(False, print_mock)
|
||||||
handler.emit(FakeLogRecord('hi', 'WARNING', 30))
|
handler.emit(FakeLogRecord('hi', 'WARNING', 30))
|
||||||
print_mock.assert_called_once_with(
|
print_mock.assert_called_once_with(
|
||||||
'[WARNING] hi',
|
'[WARNING] hi',
|
||||||
|
|
|
||||||
|
|
@ -55,7 +55,7 @@ def test_run_a_python_hook(config_for_python_hooks_repo):
|
||||||
)
|
)
|
||||||
|
|
||||||
assert ret[0] == 0
|
assert ret[0] == 0
|
||||||
assert ret[1] == "['/dev/null']\nHello World\n"
|
assert ret[1] == b"['/dev/null']\nHello World\n"
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.integration
|
@pytest.mark.integration
|
||||||
|
|
@ -77,7 +77,7 @@ def test_cwd_of_hook(config_for_prints_cwd_repo):
|
||||||
)
|
)
|
||||||
|
|
||||||
assert ret[0] == 0
|
assert ret[0] == 0
|
||||||
assert ret[1] == '{0}\n'.format(repo.repo_url)
|
assert ret[1] == repo.repo_url.encode('utf-8') + b'\n'
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.skipif(
|
@pytest.mark.skipif(
|
||||||
|
|
@ -90,7 +90,7 @@ def test_run_a_node_hook(config_for_node_hooks_repo):
|
||||||
ret = repo.run_hook(PrefixedCommandRunner(C.HOOKS_WORKSPACE), 'foo', [])
|
ret = repo.run_hook(PrefixedCommandRunner(C.HOOKS_WORKSPACE), 'foo', [])
|
||||||
|
|
||||||
assert ret[0] == 0
|
assert ret[0] == 0
|
||||||
assert ret[1] == 'Hello World\n'
|
assert ret[1] == b'Hello World\n'
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.integration
|
@pytest.mark.integration
|
||||||
|
|
@ -101,7 +101,7 @@ def test_run_a_script_hook(config_for_script_hooks_repo):
|
||||||
)
|
)
|
||||||
|
|
||||||
assert ret[0] == 0
|
assert ret[0] == 0
|
||||||
assert ret[1] == 'bar\nHello World\n'
|
assert ret[1] == b'bar\nHello World\n'
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
|
|
|
||||||
|
|
@ -118,8 +118,8 @@ def img_staged(empty_git_dir):
|
||||||
def _test_img_state(path, expected_file='img1.jpg', status='A'):
|
def _test_img_state(path, expected_file='img1.jpg', status='A'):
|
||||||
assert os.path.exists(path.img_filename)
|
assert os.path.exists(path.img_filename)
|
||||||
assert (
|
assert (
|
||||||
open(path.img_filename).read() ==
|
open(path.img_filename, 'rb').read() ==
|
||||||
open(get_resource_path(expected_file)).read()
|
open(get_resource_path(expected_file), 'rb').read()
|
||||||
)
|
)
|
||||||
actual_status = get_short_git_status()['img.jpg']
|
actual_status = get_short_git_status()['img.jpg']
|
||||||
assert status == actual_status
|
assert status == actual_status
|
||||||
|
|
|
||||||
4
tox.ini
4
tox.ini
|
|
@ -1,7 +1,7 @@
|
||||||
[tox]
|
[tox]
|
||||||
project = pre_commit
|
project = pre_commit
|
||||||
# These should match the travis env list
|
# These should match the travis env list
|
||||||
envlist = py26,py27
|
envlist = py26,py27,py33,pypy
|
||||||
|
|
||||||
[testenv]
|
[testenv]
|
||||||
install_command = pip install --use-wheel {opts} {packages}
|
install_command = pip install --use-wheel {opts} {packages}
|
||||||
|
|
@ -9,7 +9,7 @@ deps = -rrequirements_dev.txt
|
||||||
commands =
|
commands =
|
||||||
coverage erase
|
coverage erase
|
||||||
coverage run -m pytest {posargs:tests}
|
coverage run -m pytest {posargs:tests}
|
||||||
coverage report --show-missing --fail-under 90
|
coverage report --show-missing --fail-under 88
|
||||||
flake8 {[tox]project} tests setup.py
|
flake8 {[tox]project} tests setup.py
|
||||||
# pylint {[tox]project} tests setup.py
|
# pylint {[tox]project} tests setup.py
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue