From ee4a830f35bcbba4ecd8a6dba3bc6b41c5b5880f Mon Sep 17 00:00:00 2001 From: rmawatson Date: Mon, 9 Feb 2026 00:18:41 +0000 Subject: [PATCH] implemented --tool flag to allow arbirary arguments to be passed --- pre_commit/commands/run.py | 30 ++++++++++++++++++++++++++++-- pre_commit/main.py | 18 ++++++++++++++++++ 2 files changed, 46 insertions(+), 2 deletions(-) diff --git a/pre_commit/commands/run.py b/pre_commit/commands/run.py index 8ab505ff..be3999cf 100644 --- a/pre_commit/commands/run.py +++ b/pre_commit/commands/run.py @@ -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)) @@ -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, @@ -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) @@ -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) @@ -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. @@ -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 @@ -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') diff --git a/pre_commit/main.py b/pre_commit/main.py index 0c3eefda..4941cd70 100644 --- a/pre_commit/main.py +++ b/pre_commit/main.py @@ -311,6 +311,11 @@ def main(argv: Sequence[str] | None = None) -> int: 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') @@ -367,7 +372,20 @@ def main(argv: Sequence[str] | None = None) -> int: # 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'])