Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 28 additions & 2 deletions pre_commit/commands/run.py
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,8 @@ def _run_single_hook(
diff_before: bytes,
verbose: bool,
use_color: bool,
is_tool: bool = False,
extra_args: Sequence[str] = (),
) -> tuple[bool, bytes]:
filenames = tuple(classifier.filenames_for_hook(hook))

Expand Down Expand Up @@ -190,10 +192,11 @@ def _run_single_hook(
time_before = time.monotonic()
language = languages[hook.language]
with language.in_env(hook.prefix, hook.language_version):
hook_args = tuple(extra_args) if is_tool else hook.args
retcode, out = language.run_hook(
hook.prefix,
hook.entry,
hook.args,
hook_args,
filenames,
is_local=hook.src == 'local',
require_serial=hook.require_serial,
Expand Down Expand Up @@ -284,6 +287,8 @@ def _run_hooks(
hooks: Sequence[Hook],
skips: set[str],
args: argparse.Namespace,
is_tool: bool = False,
extra_args: Sequence[str] = (),
) -> int:
"""Actually run the hooks."""
cols = _compute_cols(hooks)
Expand All @@ -296,6 +301,7 @@ def _run_hooks(
current_retval, prior_diff = _run_single_hook(
classifier, hook, skips, cols, prior_diff,
verbose=args.verbose, use_color=args.color,
is_tool=is_tool, extra_args=extra_args,
)
retval |= current_retval
fail_fast = (config['fail_fast'] or hook.fail_fast or args.fail_fast)
Expand Down Expand Up @@ -341,6 +347,20 @@ def run(
args: argparse.Namespace,
environ: MutableMapping[str, str] = os.environ,
) -> int:
extra_args = getattr(args, 'extra_args', [])
is_tool = getattr(args, 'tool', False)

if extra_args and not is_tool:
logger.error('`--` args require `--tool`.')
return 1

if is_tool:
if not args.hook:
logger.error('`--tool` requires a specific hook id.')
return 1
args.all_files = True
args.hook_stage = 'manual'

stash = not args.all_files and not args.files

# Check if we have unresolved merge conflict files and fail fast.
Expand Down Expand Up @@ -434,6 +454,9 @@ def run(
)
return 1

if is_tool:
hooks = [hook._replace(always_run=True) for hook in hooks]

skips = _get_skips(environ)
to_install = [
hook
Expand All @@ -442,7 +465,10 @@ def run(
]
install_hook_envs(to_install, store)

return _run_hooks(config, hooks, skips, args)
return _run_hooks(
config, hooks, skips, args,
is_tool=is_tool, extra_args=extra_args,
)

# https://github.com/python/mypy/issues/7726
raise AssertionError('unreachable')
18 changes: 18 additions & 0 deletions pre_commit/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,11 @@ def _add_cmd(name: str, *, help: str) -> argparse.ArgumentParser:
run_parser = _add_cmd('run', help='Run hooks.')
_add_config_option(run_parser)
_add_run_options(run_parser)
run_parser.add_argument(
'--tool', action='store_true',
help='Run as a tool: ignores config args, implies --all-files. '
'Pass tool args after --.',
)

_add_cmd('sample-config', help=f'Produce a sample {C.CONFIG_FILE} file')

Expand Down Expand Up @@ -367,7 +372,20 @@ def _add_cmd(name: str, *, help: str) -> argparse.ArgumentParser:
# argparse doesn't really provide a way to use a `default` subparser
if len(argv) == 0:
argv = ['run']

# split off extra args after `--` for --tool mode (run command only)
extra_args: list[str] = []
argv = list(argv)
if argv and argv[0] == 'run':
try:
sep_idx = argv.index('--')
extra_args = argv[sep_idx + 1:]
argv = argv[:sep_idx]
except ValueError:
pass

args = parser.parse_args(argv)
args.extra_args = extra_args

if args.command == 'help' and args.help_cmd:
parser.parse_args([args.help_cmd, '--help'])
Expand Down
Loading