More miscellaneous cleanup

This commit is contained in:
Anthony Sottile
2020-01-12 21:17:59 -08:00
parent 489d9f9926
commit df40e862f4
33 changed files with 209 additions and 296 deletions

View File

@@ -7,7 +7,6 @@ omit =
setup.py
# Don't complain if non-runnable code isn't run
*/__main__.py
pre_commit/color_windows.py
pre_commit/resources/*
[report]

View File

@@ -192,19 +192,20 @@ META_HOOK_DICT = cfgv.Map(
cfgv.Required('id', cfgv.check_one_of(tuple(k for k, _ in _meta))),
# language must be system
cfgv.Optional('language', cfgv.check_one_of({'system'}), 'system'),
*([
*(
# default to the hook definition for the meta hooks
cfgv.ConditionalOptional(key, cfgv.check_any, value, 'id', hook_id)
for hook_id, values in _meta
for key, value in values
] + [
),
*(
# default to the "manifest" parsing
cfgv.OptionalNoDefault(item.key, item.check_fn)
# these will always be defaulted above
if item.key in {'name', 'language', 'entry'} else
item
for item in MANIFEST_HOOK_DICT.items
]),
),
)
CONFIG_HOOK_DICT = cfgv.Map(
'Hook', 'id',
@@ -215,11 +216,11 @@ CONFIG_HOOK_DICT = cfgv.Map(
# are optional.
# No defaults are provided here as the config is merged on top of the
# manifest.
*[
*(
cfgv.OptionalNoDefault(item.key, item.check_fn)
for item in MANIFEST_HOOK_DICT.items
if item.key != 'id'
],
),
)
CONFIG_REPO_DICT = cfgv.Map(
'Repository', 'repo',
@@ -245,7 +246,7 @@ CONFIG_REPO_DICT = cfgv.Map(
DEFAULT_LANGUAGE_VERSION = cfgv.Map(
'DefaultLanguageVersion', None,
cfgv.NoAdditionalKeys(all_languages),
*[cfgv.Optional(x, cfgv.check_string, C.DEFAULT) for x in all_languages],
*(cfgv.Optional(x, cfgv.check_string, C.DEFAULT) for x in all_languages),
)
CONFIG_SCHEMA = cfgv.Map(
'Config', None,

View File

@@ -1,24 +1,64 @@
import os
import sys
terminal_supports_color = True
if sys.platform == 'win32': # pragma: no cover (windows)
from pre_commit.color_windows import enable_virtual_terminal_processing
def _enable() -> None:
from ctypes import POINTER
from ctypes import windll
from ctypes import WinError
from ctypes import WINFUNCTYPE
from ctypes.wintypes import BOOL
from ctypes.wintypes import DWORD
from ctypes.wintypes import HANDLE
STD_OUTPUT_HANDLE = -11
ENABLE_VIRTUAL_TERMINAL_PROCESSING = 4
def bool_errcheck(result, func, args):
if not result:
raise WinError()
return args
GetStdHandle = WINFUNCTYPE(HANDLE, DWORD)(
('GetStdHandle', windll.kernel32), ((1, 'nStdHandle'),),
)
GetConsoleMode = WINFUNCTYPE(BOOL, HANDLE, POINTER(DWORD))(
('GetConsoleMode', windll.kernel32),
((1, 'hConsoleHandle'), (2, 'lpMode')),
)
GetConsoleMode.errcheck = bool_errcheck
SetConsoleMode = WINFUNCTYPE(BOOL, HANDLE, DWORD)(
('SetConsoleMode', windll.kernel32),
((1, 'hConsoleHandle'), (1, 'dwMode')),
)
SetConsoleMode.errcheck = bool_errcheck
# As of Windows 10, the Windows console supports (some) ANSI escape
# sequences, but it needs to be enabled using `SetConsoleMode` first.
#
# More info on the escape sequences supported:
# https://msdn.microsoft.com/en-us/library/windows/desktop/mt638032(v=vs.85).aspx
stdout = GetStdHandle(STD_OUTPUT_HANDLE)
flags = GetConsoleMode(stdout)
SetConsoleMode(stdout, flags | ENABLE_VIRTUAL_TERMINAL_PROCESSING)
try:
enable_virtual_terminal_processing()
_enable()
except OSError:
terminal_supports_color = False
else:
terminal_supports_color = True
else: # pragma: windows no cover
terminal_supports_color = True
RED = '\033[41m'
GREEN = '\033[42m'
YELLOW = '\033[43;30m'
TURQUOISE = '\033[46;30m'
SUBTLE = '\033[2m'
NORMAL = '\033[0m'
class InvalidColorSetting(ValueError):
pass
NORMAL = '\033[m'
def format_color(text: str, color: str, use_color_setting: bool) -> str:
@@ -29,10 +69,10 @@ def format_color(text: str, color: str, use_color_setting: bool) -> str:
color - The color start string
use_color_setting - Whether or not to color
"""
if not use_color_setting:
return text
else:
if use_color_setting:
return f'{color}{text}{NORMAL}'
else:
return text
COLOR_CHOICES = ('auto', 'always', 'never')
@@ -45,7 +85,7 @@ def use_color(setting: str) -> bool:
setting - Either `auto`, `always`, or `never`
"""
if setting not in COLOR_CHOICES:
raise InvalidColorSetting(setting)
raise ValueError(setting)
return (
setting == 'always' or (

View File

@@ -1,49 +0,0 @@
import sys
assert sys.platform == 'win32'
from ctypes import POINTER # noqa: E402
from ctypes import windll # noqa: E402
from ctypes import WinError # noqa: E402
from ctypes import WINFUNCTYPE # noqa: E402
from ctypes.wintypes import BOOL # noqa: E402
from ctypes.wintypes import DWORD # noqa: E402
from ctypes.wintypes import HANDLE # noqa: E402
STD_OUTPUT_HANDLE = -11
ENABLE_VIRTUAL_TERMINAL_PROCESSING = 4
def bool_errcheck(result, func, args):
if not result:
raise WinError()
return args
GetStdHandle = WINFUNCTYPE(HANDLE, DWORD)(
('GetStdHandle', windll.kernel32), ((1, 'nStdHandle'),),
)
GetConsoleMode = WINFUNCTYPE(BOOL, HANDLE, POINTER(DWORD))(
('GetConsoleMode', windll.kernel32),
((1, 'hConsoleHandle'), (2, 'lpMode')),
)
GetConsoleMode.errcheck = bool_errcheck
SetConsoleMode = WINFUNCTYPE(BOOL, HANDLE, DWORD)(
('SetConsoleMode', windll.kernel32),
((1, 'hConsoleHandle'), (1, 'dwMode')),
)
SetConsoleMode.errcheck = bool_errcheck
def enable_virtual_terminal_processing():
"""As of Windows 10, the Windows console supports (some) ANSI escape
sequences, but it needs to be enabled using `SetConsoleMode` first.
More info on the escape sequences supported:
https://msdn.microsoft.com/en-us/library/windows/desktop/mt638032(v=vs.85).aspx
"""
stdout = GetStdHandle(STD_OUTPUT_HANDLE)
flags = GetConsoleMode(stdout)
SetConsoleMode(stdout, flags | ENABLE_VIRTUAL_TERMINAL_PROCESSING)

View File

@@ -29,7 +29,5 @@ def init_templatedir(
dest = os.path.realpath(directory)
if configured_path != dest:
logger.warning('`init.templateDir` not set to the target directory')
logger.warning(
f'maybe `git config --global init.templateDir {dest}`?',
)
logger.warning(f'maybe `git config --global init.templateDir {dest}`?')
return 0

View File

@@ -28,18 +28,17 @@ def _migrate_map(contents: str) -> str:
# If they are using the "default" flow style of yaml, this operation
# will yield a valid configuration
try:
trial_contents = header + 'repos:\n' + rest
trial_contents = f'{header}repos:\n{rest}'
ordered_load(trial_contents)
contents = trial_contents
except yaml.YAMLError:
contents = header + 'repos:\n' + _indent(rest)
contents = f'{header}repos:\n{_indent(rest)}'
return contents
def _migrate_sha_to_rev(contents: str) -> str:
reg = re.compile(r'(\n\s+)sha:')
return reg.sub(r'\1rev:', contents)
return re.sub(r'(\n\s+)sha:', r'\1rev:', contents)
def migrate_config(config_file: str, quiet: bool = False) -> int:

View File

@@ -20,7 +20,6 @@ from pre_commit import color
from pre_commit import git
from pre_commit import output
from pre_commit.clientlib import load_config
from pre_commit.output import get_hook_message
from pre_commit.repository import all_hooks
from pre_commit.repository import Hook
from pre_commit.repository import install_hook_envs
@@ -33,6 +32,25 @@ from pre_commit.util import EnvironT
logger = logging.getLogger('pre_commit')
def _start_msg(*, start: str, cols: int, end_len: int) -> str:
dots = '.' * (cols - len(start) - end_len - 1)
return f'{start}{dots}'
def _full_msg(
*,
start: str,
cols: int,
end_msg: str,
end_color: str,
use_color: bool,
postfix: str = '',
) -> str:
dots = '.' * (cols - len(start) - len(postfix) - len(end_msg) - 1)
end = color.format_color(end_msg, end_color, use_color)
return f'{start}{dots}{postfix}{end}\n'
def filter_by_include_exclude(
names: Collection[str],
include: str,
@@ -106,8 +124,8 @@ def _run_single_hook(
if hook.id in skips or hook.alias in skips:
output.write(
get_hook_message(
hook.name,
_full_msg(
start=hook.name,
end_msg=SKIPPED,
end_color=color.YELLOW,
use_color=use_color,
@@ -120,8 +138,8 @@ def _run_single_hook(
out = b''
elif not filenames and not hook.always_run:
output.write(
get_hook_message(
hook.name,
_full_msg(
start=hook.name,
postfix=NO_FILES,
end_msg=SKIPPED,
end_color=color.TURQUOISE,
@@ -135,7 +153,7 @@ def _run_single_hook(
out = b''
else:
# print hook and dots first in case the hook takes a while to run
output.write(get_hook_message(hook.name, end_len=6, cols=cols))
output.write(_start_msg(start=hook.name, end_len=6, cols=cols))
diff_cmd = ('git', 'diff', '--no-ext-diff')
diff_before = cmd_output_b(*diff_cmd, retcode=None)
@@ -218,9 +236,8 @@ def _run_hooks(
"""Actually run the hooks."""
skips = _get_skips(environ)
cols = _compute_cols(hooks)
filenames = _all_filenames(args)
filenames = filter_by_include_exclude(
filenames, config['files'], config['exclude'],
_all_filenames(args), config['files'], config['exclude'],
)
classifier = Classifier(filenames)
retval = 0

View File

@@ -1,6 +1,7 @@
import argparse
import logging
import os.path
from typing import Optional
from typing import Tuple
from aspy.yaml import ordered_dump
@@ -18,9 +19,9 @@ from pre_commit.xargs import xargs
logger = logging.getLogger(__name__)
def _repo_ref(tmpdir: str, repo: str, ref: str) -> Tuple[str, str]:
def _repo_ref(tmpdir: str, repo: str, ref: Optional[str]) -> Tuple[str, str]:
# if `ref` is explicitly passed, use it
if ref:
if ref is not None:
return repo, ref
ref = git.head_rev(repo)

View File

@@ -8,12 +8,7 @@ else: # pragma: no cover (PY38+)
CONFIG_FILE = '.pre-commit-config.yaml'
MANIFEST_FILE = '.pre-commit-hooks.yaml'
YAML_DUMP_KWARGS = {
'default_flow_style': False,
# Use unicode
'encoding': None,
'indent': 4,
}
YAML_DUMP_KWARGS = {'default_flow_style': False, 'indent': 4}
# Bump when installation changes in a backwards / forwards incompatible way
INSTALLED_STATE_VERSION = '1'

View File

@@ -1,9 +1,9 @@
import contextlib
import functools
import os.path
import sys
import traceback
from typing import Generator
from typing import Optional
import pre_commit.constants as C
from pre_commit import output
@@ -15,22 +15,13 @@ class FatalError(RuntimeError):
def _log_and_exit(msg: str, exc: BaseException, formatted: str) -> None:
error_msg = b''.join((
msg.encode(), b': ',
type(exc).__name__.encode(), b': ',
str(exc).encode(),
))
output.write_line_b(error_msg)
store = Store()
log_path = os.path.join(store.directory, 'pre-commit.log')
error_msg = f'{msg}: {type(exc).__name__}: {exc}'
output.write_line(error_msg)
log_path = os.path.join(Store().directory, 'pre-commit.log')
output.write_line(f'Check the log at {log_path}')
with open(log_path, 'wb') as log:
def _log_line(s: Optional[str] = None) -> None:
output.write_line(s, stream=log)
def _log_line_b(s: Optional[bytes] = None) -> None:
output.write_line_b(s, stream=log)
_log_line = functools.partial(output.write_line, stream=log)
_log_line('### version information')
_log_line()
@@ -48,7 +39,7 @@ def _log_and_exit(msg: str, exc: BaseException, formatted: str) -> None:
_log_line('### error information')
_log_line()
_log_line('```')
_log_line_b(error_msg)
_log_line(error_msg)
_log_line('```')
_log_line()
_log_line('```')

View File

@@ -141,7 +141,7 @@ def head_rev(remote: str) -> str:
def has_diff(*args: str, repo: str = '.') -> bool:
cmd = ('git', 'diff', '--quiet', '--no-ext-diff') + args
cmd = ('git', 'diff', '--quiet', '--no-ext-diff', *args)
return cmd_output_b(*cmd, cwd=repo, retcode=None)[0] == 1

View File

@@ -30,9 +30,9 @@ def get_env_patch(env: str) -> PatchesT:
# seems to be used for python.exe.
path: SubstitutionT = (os.path.join(env, 'bin'), os.pathsep, Var('PATH'))
if os.name == 'nt': # pragma: no cover (platform specific)
path = (env, os.pathsep) + path
path = (os.path.join(env, 'Scripts'), os.pathsep) + path
path = (os.path.join(env, 'Library', 'bin'), os.pathsep) + path
path = (env, os.pathsep, *path)
path = (os.path.join(env, 'Scripts'), os.pathsep, *path)
path = (os.path.join(env, 'Library', 'bin'), os.pathsep, *path)
return (
('PYTHONHOME', UNSET),

View File

@@ -18,6 +18,6 @@ def run_hook(
file_args: Sequence[str],
color: bool,
) -> Tuple[int, bytes]:
out = hook.entry.encode() + b'\n\n'
out = f'{hook.entry}\n\n'.encode()
out += b'\n'.join(f.encode() for f in file_args) + b'\n'
return 1, out

View File

@@ -68,7 +68,7 @@ def install_environment(
# https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx?f=255&MSPPError=-2147217396#maxpath
if sys.platform == 'win32': # pragma: no cover
envdir = '\\\\?\\' + os.path.normpath(envdir)
envdir = f'\\\\?\\{os.path.normpath(envdir)}'
with clean_path_on_failure(envdir):
cmd = [
sys.executable, '-mnodeenv', '--prebuilt', '--clean-src', envdir,
@@ -83,7 +83,7 @@ def install_environment(
helpers.run_setup_cmd(prefix, ('npm', 'install'))
helpers.run_setup_cmd(
prefix,
('npm', 'install', '-g', '.') + additional_dependencies,
('npm', 'install', '-g', '.', *additional_dependencies),
)

View File

@@ -49,9 +49,8 @@ def _find_by_py_launcher(
if version.startswith('python'):
num = version[len('python'):]
try:
return cmd_output(
'py', f'-{num}', '-c', 'import sys; print(sys.executable)',
)[1].strip()
cmd = ('py', f'-{num}', '-c', 'import sys; print(sys.executable)')
return cmd_output(*cmd)[1].strip()
except CalledProcessError:
pass
return None

View File

@@ -109,12 +109,14 @@ def install_environment(
# Need to call this after installing to set up the shims
helpers.run_setup_cmd(prefix, ('rbenv', 'rehash'))
helpers.run_setup_cmd(
prefix, ('gem', 'build') + prefix.star('.gemspec'),
prefix, ('gem', 'build', *prefix.star('.gemspec')),
)
helpers.run_setup_cmd(
prefix,
('gem', 'install', '--no-document') +
prefix.star('.gem') + additional_dependencies,
(
'gem', 'install', '--no-document',
*prefix.star('.gem'), *additional_dependencies,
),
)

View File

@@ -27,10 +27,7 @@ healthy = helpers.basic_healthy
def get_env_patch(target_dir: str) -> PatchesT:
return (
(
'PATH',
(os.path.join(target_dir, 'bin'), os.pathsep, Var('PATH')),
),
('PATH', (os.path.join(target_dir, 'bin'), os.pathsep, Var('PATH'))),
)

View File

@@ -18,6 +18,5 @@ def run_hook(
file_args: Sequence[str],
color: bool,
) -> Tuple[int, bytes]:
cmd = hook.cmd
cmd = (hook.prefix.path(cmd[0]),) + cmd[1:]
cmd = (hook.prefix.path(hook.cmd[0]), *hook.cmd[1:])
return helpers.run_xargs(hook, cmd, file_args, color=color)

View File

@@ -329,7 +329,8 @@ def main(argv: Optional[Sequence[str]] = None) -> int:
return install(
args.config, store,
hook_types=args.hook_types,
overwrite=args.overwrite, hooks=args.install_hooks,
overwrite=args.overwrite,
hooks=args.install_hooks,
skip_on_missing_config=args.allow_missing_config,
)
elif args.command == 'init-templatedir':

View File

@@ -34,7 +34,7 @@ def make_archive(name: str, repo: str, ref: str, destdir: str) -> str:
:param text ref: Tag/SHA/branch to check out.
:param text destdir: Directory to place archives in.
"""
output_path = os.path.join(destdir, name + '.tar.gz')
output_path = os.path.join(destdir, f'{name}.tar.gz')
with tmpdir() as tempdir:
# Clone the repository to the temporary directory
cmd_output_b('git', 'clone', repo, tempdir)
@@ -56,9 +56,7 @@ def main(argv: Optional[Sequence[str]] = None) -> int:
parser.add_argument('--dest', default='pre_commit/resources')
args = parser.parse_args(argv)
for archive_name, repo, ref in REPOS:
output.write_line(
f'Making {archive_name}.tar.gz for {repo}@{ref}',
)
output.write_line(f'Making {archive_name}.tar.gz for {repo}@{ref}')
make_archive(archive_name, repo, ref, args.dest)
return 0

View File

@@ -4,59 +4,6 @@ from typing import Any
from typing import IO
from typing import Optional
from pre_commit import color
def get_hook_message(
start: str,
postfix: str = '',
end_msg: Optional[str] = None,
end_len: int = 0,
end_color: Optional[str] = None,
use_color: Optional[bool] = None,
cols: int = 80,
) -> str:
"""Prints a message for running a hook.
This currently supports three approaches:
# Print `start` followed by dots, leaving 6 characters at the end
>>> print_hook_message('start', end_len=6)
start...............................................................
# Print `start` followed by dots with the end message colored if coloring
# is specified and a newline afterwards
>>> print_hook_message(
'start',
end_msg='end',
end_color=color.RED,
use_color=True,
)
start...................................................................end
# Print `start` followed by dots, followed by the `postfix` message
# uncolored, followed by the `end_msg` colored if specified and a newline
# afterwards
>>> print_hook_message(
'start',
postfix='postfix ',
end_msg='end',
end_color=color.RED,
use_color=True,
)
start...........................................................postfix end
"""
if end_len:
assert end_msg is None, end_msg
return start + '.' * (cols - len(start) - end_len - 1)
else:
assert end_msg is not None
assert end_color is not None
assert use_color is not None
dots = '.' * (cols - len(start) - len(postfix) - len(end_msg) - 1)
end = color.format_color(end_msg, end_color, use_color)
return f'{start}{dots}{postfix}{end}\n'
def write(s: str, stream: IO[bytes] = sys.stdout.buffer) -> None:
stream.write(s.encode())

View File

@@ -20,8 +20,7 @@ def parse_filename(filename: str) -> Tuple[str, ...]:
def find_executable(
exe: str,
_environ: Optional[Mapping[str, str]] = None,
exe: str, _environ: Optional[Mapping[str, str]] = None,
) -> Optional[str]:
exe = os.path.normpath(exe)
if os.sep in exe:

View File

@@ -32,7 +32,7 @@ def _state(additional_deps: Sequence[str]) -> object:
def _state_filename(prefix: Prefix, venv: str) -> str:
return prefix.path(venv, '.install_state_v' + C.INSTALLED_STATE_VERSION)
return prefix.path(venv, f'.install_state_v{C.INSTALLED_STATE_VERSION}')
def _read_state(prefix: Prefix, venv: str) -> Optional[object]:
@@ -46,7 +46,7 @@ def _read_state(prefix: Prefix, venv: str) -> Optional[object]:
def _write_state(prefix: Prefix, venv: str, state: object) -> None:
state_filename = _state_filename(prefix, venv)
staging = state_filename + 'staging'
staging = f'{state_filename}staging'
with open(staging, 'w') as state_file:
state_file.write(json.dumps(state))
# Move the file into place atomically to indicate we've installed

View File

@@ -50,9 +50,7 @@ def _unstaged_changes_cleared(patch_dir: str) -> Generator[None, None, None]:
patch_filename = f'patch{int(time.time())}'
patch_filename = os.path.join(patch_dir, patch_filename)
logger.warning('Unstaged files detected.')
logger.info(
f'Stashing unstaged files to {patch_filename}.',
)
logger.info(f'Stashing unstaged files to {patch_filename}.')
# Save the current unstaged changes as a patch
os.makedirs(patch_dir, exist_ok=True)
with open(patch_filename, 'wb') as patch_file:

View File

@@ -6,13 +6,12 @@ import pytest
from pre_commit import envcontext
from pre_commit.color import format_color
from pre_commit.color import GREEN
from pre_commit.color import InvalidColorSetting
from pre_commit.color import use_color
@pytest.mark.parametrize(
('in_text', 'in_color', 'in_use_color', 'expected'), (
('foo', GREEN, True, f'{GREEN}foo\033[0m'),
('foo', GREEN, True, f'{GREEN}foo\033[m'),
('foo', GREEN, False, 'foo'),
),
)
@@ -56,5 +55,5 @@ def test_use_color_dumb_term():
def test_use_color_raises_if_given_shenanigans():
with pytest.raises(InvalidColorSetting):
with pytest.raises(ValueError):
use_color('herpaderp')

View File

@@ -34,7 +34,7 @@ def test_is_script():
def test_is_previous_pre_commit(tmpdir):
f = tmpdir.join('foo')
f.write(PRIOR_HASHES[0] + '\n')
f.write(f'{PRIOR_HASHES[0]}\n')
assert is_our_script(f.strpath)
@@ -129,11 +129,11 @@ FILES_CHANGED = (
NORMAL_PRE_COMMIT_RUN = re.compile(
r'^\[INFO\] Initializing environment for .+\.\n'
r'Bash hook\.+Passed\n'
r'\[master [a-f0-9]{7}\] commit!\n' +
FILES_CHANGED +
r' create mode 100644 foo\n$',
fr'^\[INFO\] Initializing environment for .+\.\n'
fr'Bash hook\.+Passed\n'
fr'\[master [a-f0-9]{{7}}\] commit!\n'
fr'{FILES_CHANGED}'
fr' create mode 100644 foo\n$',
)
@@ -296,10 +296,10 @@ def test_failing_hooks_returns_nonzero(tempdir_factory, store):
EXISTING_COMMIT_RUN = re.compile(
r'^legacy hook\n'
r'\[master [a-f0-9]{7}\] commit!\n' +
FILES_CHANGED +
r' create mode 100644 baz\n$',
fr'^legacy hook\n'
fr'\[master [a-f0-9]{{7}}\] commit!\n'
fr'{FILES_CHANGED}'
fr' create mode 100644 baz\n$',
)
@@ -453,10 +453,10 @@ def test_uninstall_doesnt_remove_not_our_hooks(in_git_dir):
PRE_INSTALLED = re.compile(
r'Bash hook\.+Passed\n'
r'\[master [a-f0-9]{7}\] commit!\n' +
FILES_CHANGED +
r' create mode 100644 foo\n$',
fr'Bash hook\.+Passed\n'
fr'\[master [a-f0-9]{{7}}\] commit!\n'
fr'{FILES_CHANGED}'
fr' create mode 100644 foo\n$',
)

View File

@@ -7,10 +7,13 @@ from unittest import mock
import pytest
import pre_commit.constants as C
from pre_commit import color
from pre_commit.commands.install_uninstall import install
from pre_commit.commands.run import _compute_cols
from pre_commit.commands.run import _full_msg
from pre_commit.commands.run import _get_skips
from pre_commit.commands.run import _has_unmerged_paths
from pre_commit.commands.run import _start_msg
from pre_commit.commands.run import Classifier
from pre_commit.commands.run import filter_by_include_exclude
from pre_commit.commands.run import run
@@ -29,6 +32,62 @@ from testing.util import git_commit
from testing.util import run_opts
def test_start_msg():
ret = _start_msg(start='start', end_len=5, cols=15)
# 4 dots: 15 - 5 - 5 - 1
assert ret == 'start....'
def test_full_msg():
ret = _full_msg(
start='start',
end_msg='end',
end_color='',
use_color=False,
cols=15,
)
# 6 dots: 15 - 5 - 3 - 1
assert ret == 'start......end\n'
def test_full_msg_with_color():
ret = _full_msg(
start='start',
end_msg='end',
end_color=color.RED,
use_color=True,
cols=15,
)
# 6 dots: 15 - 5 - 3 - 1
assert ret == f'start......{color.RED}end{color.NORMAL}\n'
def test_full_msg_with_postfix():
ret = _full_msg(
start='start',
postfix='post ',
end_msg='end',
end_color='',
use_color=False,
cols=20,
)
# 6 dots: 20 - 5 - 5 - 3 - 1
assert ret == 'start......post end\n'
def test_full_msg_postfix_not_colored():
ret = _full_msg(
start='start',
postfix='post ',
end_msg='end',
end_color=color.RED,
use_color=True,
cols=20,
)
# 6 dots: 20 - 5 - 5 - 3 - 1
assert ret == f'start......post {color.RED}end{color.NORMAL}\n'
@pytest.fixture
def repo_with_passing_hook(tempdir_factory):
git_path = make_consuming_repo(tempdir_factory, 'script_hooks_repo')
@@ -173,7 +232,7 @@ def test_global_exclude(cap_out, store, in_git_dir):
ret, printed = _do_run(cap_out, store, str(in_git_dir), opts)
assert ret == 0
# Does not contain foo.py since it was excluded
assert printed.startswith(b'identity' + b'.' * 65 + b'Passed\n')
assert printed.startswith(f'identity{"." * 65}Passed\n'.encode())
assert printed.endswith(b'\n\n.pre-commit-config.yaml\nbar.py\n\n')
@@ -190,7 +249,7 @@ def test_global_files(cap_out, store, in_git_dir):
ret, printed = _do_run(cap_out, store, str(in_git_dir), opts)
assert ret == 0
# Does not contain foo.py since it was excluded
assert printed.startswith(b'identity' + b'.' * 65 + b'Passed\n')
assert printed.startswith(f'identity{"." * 65}Passed\n'.encode())
assert printed.endswith(b'\n\nbar.py\n\n')

View File

@@ -21,7 +21,7 @@ def try_repo_opts(repo, ref=None, **kwargs):
def _get_out(cap_out):
out = re.sub(r'\[INFO\].+\n', '', cap_out.get())
start, using_config, config, rest = out.split('=' * 79 + '\n')
start, using_config, config, rest = out.split(f'{"=" * 79}\n')
assert using_config == 'Using config:\n'
return start, config, rest

View File

@@ -140,7 +140,6 @@ def test_error_handler_no_tty(tempdir_factory):
ret, out, _ = cmd_output_mocked_pre_commit_home(
sys.executable,
'-c',
'from __future__ import unicode_literals\n'
'from pre_commit.error_handler import error_handler\n'
'with error_handler():\n'
' raise ValueError("\\u2603")\n',

View File

@@ -16,7 +16,7 @@ def test_norm_version_expanduser():
expected_path = fr'{home}\python343'
else: # pragma: windows no cover
path = '~/.pyenv/versions/3.4.3/bin/python'
expected_path = home + '/.pyenv/versions/3.4.3/bin/python'
expected_path = f'{home}/.pyenv/versions/3.4.3/bin/python'
result = python.norm_version(path)
assert result == expected_path

View File

@@ -12,7 +12,7 @@ def test_logging_handler_color(cap_out):
handler = LoggingHandler(True)
handler.emit(_log_record('hi', logging.WARNING))
ret = cap_out.get()
assert ret == color.YELLOW + '[WARNING]' + color.NORMAL + ' hi\n'
assert ret == f'{color.YELLOW}[WARNING]{color.NORMAL} hi\n'
def test_logging_handler_no_color(cap_out):

View File

@@ -1,85 +1,9 @@
from unittest import mock
import io
import pytest
from pre_commit import color
from pre_commit import output
@pytest.mark.parametrize(
'kwargs',
(
# both end_msg and end_len
{'end_msg': 'end', 'end_len': 1, 'end_color': '', 'use_color': True},
# Neither end_msg nor end_len
{},
# Neither color option for end_msg
{'end_msg': 'end'},
# No use_color for end_msg
{'end_msg': 'end', 'end_color': ''},
# No end_color for end_msg
{'end_msg': 'end', 'use_color': ''},
),
)
def test_get_hook_message_raises(kwargs):
with pytest.raises(AssertionError):
output.get_hook_message('start', **kwargs)
def test_case_with_end_len():
ret = output.get_hook_message('start', end_len=5, cols=15)
assert ret == 'start' + '.' * 4
def test_case_with_end_msg():
ret = output.get_hook_message(
'start',
end_msg='end',
end_color='',
use_color=False,
cols=15,
)
assert ret == 'start' + '.' * 6 + 'end' + '\n'
def test_case_with_end_msg_using_color():
ret = output.get_hook_message(
'start',
end_msg='end',
end_color=color.RED,
use_color=True,
cols=15,
)
assert ret == 'start' + '.' * 6 + color.RED + 'end' + color.NORMAL + '\n'
def test_case_with_postfix_message():
ret = output.get_hook_message(
'start',
postfix='post ',
end_msg='end',
end_color='',
use_color=False,
cols=20,
)
assert ret == 'start' + '.' * 6 + 'post ' + 'end' + '\n'
def test_make_sure_postfix_is_not_colored():
ret = output.get_hook_message(
'start',
postfix='post ',
end_msg='end',
end_color=color.RED,
use_color=True,
cols=20,
)
assert ret == (
'start' + '.' * 6 + 'post ' + color.RED + 'end' + color.NORMAL + '\n'
)
def test_output_write_writes():
fake_stream = mock.Mock()
output.write('hello world', fake_stream)
assert fake_stream.write.call_count == 1
stream = io.BytesIO()
output.write('hello world', stream)
assert stream.getvalue() == b'hello world'

View File

@@ -94,9 +94,9 @@ def test_foo_something_unstaged_diff_color_always(foo_staged, patch_dir):
def test_foo_both_modify_non_conflicting(foo_staged, patch_dir):
with open(foo_staged.foo_filename, 'w') as foo_file:
foo_file.write(FOO_CONTENTS + '9\n')
foo_file.write(f'{FOO_CONTENTS}9\n')
_test_foo_state(foo_staged, FOO_CONTENTS + '9\n', 'AM')
_test_foo_state(foo_staged, f'{FOO_CONTENTS}9\n', 'AM')
with staged_files_only(patch_dir):
_test_foo_state(foo_staged)
@@ -107,7 +107,7 @@ def test_foo_both_modify_non_conflicting(foo_staged, patch_dir):
_test_foo_state(foo_staged, FOO_CONTENTS.replace('1', 'a'), 'AM')
_test_foo_state(foo_staged, FOO_CONTENTS.replace('1', 'a') + '9\n', 'AM')
_test_foo_state(foo_staged, f'{FOO_CONTENTS.replace("1", "a")}9\n', 'AM')
def test_foo_both_modify_conflicting(foo_staged, patch_dir):