mirror of
https://github.com/pre-commit/pre-commit.git
synced 2026-01-14 04:50:20 -06:00
More miscellaneous cleanup
This commit is contained in:
@@ -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]
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 (
|
||||
|
||||
@@ -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)
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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('```')
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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),
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -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'))),
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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':
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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')
|
||||
|
||||
@@ -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$',
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -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')
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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):
|
||||
|
||||
Reference in New Issue
Block a user