diff --git a/pre_commit/commands/run.py b/pre_commit/commands/run.py index 076f16d8..359d189a 100644 --- a/pre_commit/commands/run.py +++ b/pre_commit/commands/run.py @@ -147,6 +147,7 @@ def _run_single_hook( diff_before: bytes, verbose: bool, use_color: bool, + dry_run: bool, ) -> tuple[bool, bytes]: filenames = tuple(classifier.filenames_for_hook(hook)) @@ -198,6 +199,7 @@ def _run_single_hook( is_local=hook.src == 'local', require_serial=hook.require_serial, color=use_color, + dry_run=dry_run, ) duration = round(time.monotonic() - time_before, 2) or 0 diff_after = _get_diff() @@ -296,6 +298,7 @@ def _run_hooks( current_retval, prior_diff = _run_single_hook( classifier, hook, skips, cols, prior_diff, verbose=args.verbose, use_color=args.color, + dry_run=args.dry_run, ) retval |= current_retval if retval and (config['fail_fast'] or hook.fail_fast): diff --git a/pre_commit/lang_base.py b/pre_commit/lang_base.py index 5303948b..b7459add 100644 --- a/pre_commit/lang_base.py +++ b/pre_commit/lang_base.py @@ -55,6 +55,7 @@ class Language(Protocol): is_local: bool, require_serial: bool, color: bool, + dry_run: bool, ) -> tuple[int, bytes]: ... @@ -158,6 +159,7 @@ def run_xargs( *, require_serial: bool, color: bool, + dry_run: bool, ) -> tuple[int, bytes]: if require_serial: jobs = 1 @@ -167,7 +169,7 @@ def run_xargs( # ordering. file_args = _shuffled(file_args) jobs = target_concurrency() - return xargs.xargs(cmd, file_args, target_concurrency=jobs, color=color) + return xargs.xargs(cmd, file_args, target_concurrency=jobs, color=color, dry_run=dry_run) def hook_cmd(entry: str, args: Sequence[str]) -> tuple[str, ...]: @@ -183,10 +185,12 @@ def basic_run_hook( is_local: bool, require_serial: bool, color: bool, + dry_run: bool, ) -> tuple[int, bytes]: return run_xargs( hook_cmd(entry, args), file_args, require_serial=require_serial, color=color, + dry_run=dry_run, ) diff --git a/pre_commit/languages/docker.py b/pre_commit/languages/docker.py index 26328515..90aa1d39 100644 --- a/pre_commit/languages/docker.py +++ b/pre_commit/languages/docker.py @@ -130,6 +130,7 @@ def run_hook( is_local: bool, require_serial: bool, color: bool, + dry_run: bool, ) -> tuple[int, bytes]: # pragma: win32 no cover # Rebuild the docker image in case it has gone missing, as many people do # automated cleanup of docker images. @@ -143,4 +144,5 @@ def run_hook( file_args, require_serial=require_serial, color=color, + dry_run=dry_run, ) diff --git a/pre_commit/languages/docker_image.py b/pre_commit/languages/docker_image.py index a1a2c169..18be2201 100644 --- a/pre_commit/languages/docker_image.py +++ b/pre_commit/languages/docker_image.py @@ -22,6 +22,7 @@ def run_hook( is_local: bool, require_serial: bool, color: bool, + dry_run: bool, ) -> tuple[int, bytes]: # pragma: win32 no cover cmd = docker_cmd() + lang_base.hook_cmd(entry, args) return lang_base.run_xargs( @@ -29,4 +30,5 @@ def run_hook( file_args, require_serial=require_serial, color=color, + dry_run=dry_run, ) diff --git a/pre_commit/languages/pygrep.py b/pre_commit/languages/pygrep.py index 72a9345f..131373c0 100644 --- a/pre_commit/languages/pygrep.py +++ b/pre_commit/languages/pygrep.py @@ -96,9 +96,10 @@ def run_hook( is_local: bool, require_serial: bool, color: bool, + dry_run: bool, ) -> tuple[int, bytes]: cmd = (sys.executable, '-m', __name__, *args, entry) - return xargs(cmd, file_args, color=color) + return xargs(cmd, file_args, color=color, dry_run=dry_run) def main(argv: Sequence[str] | None = None) -> int: diff --git a/pre_commit/languages/r.py b/pre_commit/languages/r.py index 93b62bd5..bcd11110 100644 --- a/pre_commit/languages/r.py +++ b/pre_commit/languages/r.py @@ -185,6 +185,7 @@ def run_hook( is_local: bool, require_serial: bool, color: bool, + dry_run: bool, ) -> tuple[int, bytes]: cmd = _cmd_from_hook(prefix, entry, args, is_local=is_local) return lang_base.run_xargs( @@ -192,4 +193,5 @@ def run_hook( file_args, require_serial=require_serial, color=color, + dry_run=dry_run, ) diff --git a/pre_commit/languages/script.py b/pre_commit/languages/script.py index 1eaa1e27..ca2d549c 100644 --- a/pre_commit/languages/script.py +++ b/pre_commit/languages/script.py @@ -21,6 +21,7 @@ def run_hook( is_local: bool, require_serial: bool, color: bool, + dry_run: bool, ) -> tuple[int, bytes]: cmd = lang_base.hook_cmd(entry, args) cmd = (prefix.path(cmd[0]), *cmd[1:]) @@ -29,4 +30,5 @@ def run_hook( file_args, require_serial=require_serial, color=color, + dry_run=dry_run, ) diff --git a/pre_commit/main.py b/pre_commit/main.py index 18c978a8..9780e3da 100644 --- a/pre_commit/main.py +++ b/pre_commit/main.py @@ -60,6 +60,12 @@ def _add_hook_type_option(parser: argparse.ArgumentParser) -> None: def _add_run_options(parser: argparse.ArgumentParser) -> None: parser.add_argument('hook', nargs='?', help='A single hook-id to run') parser.add_argument('--verbose', '-v', action='store_true', default=False) + parser.add_argument( + '--dry-run', + action='store_true', + help='Display the hook commands instead of run them (useful for debugging).', + default=False, + ) mutex_group = parser.add_mutually_exclusive_group(required=False) mutex_group.add_argument( '--all-files', '-a', action='store_true', default=False, diff --git a/pre_commit/xargs.py b/pre_commit/xargs.py index 22580f59..1b6e06fb 100644 --- a/pre_commit/xargs.py +++ b/pre_commit/xargs.py @@ -134,6 +134,7 @@ def xargs( varargs: Sequence[str], *, color: bool = False, + dry_run: bool = False, target_concurrency: int = 1, _max_length: int = _get_platform_max_length(), **kwargs: Any, @@ -172,14 +173,19 @@ def xargs( return cmd_fn( *run_cmd, check=False, stderr=subprocess.STDOUT, **kwargs, ) + if dry_run: + for run_cmd in partitions: + print("DRY RUN EXEC: ", " ".join(run_cmd)) + retcode = 0 + stdout = b"" + else: + threads = min(len(partitions), target_concurrency) + with _thread_mapper(threads) as thread_map: + results = thread_map(run_cmd_partition, partitions) - threads = min(len(partitions), target_concurrency) - with _thread_mapper(threads) as thread_map: - results = thread_map(run_cmd_partition, partitions) - - for proc_retcode, proc_out, _ in results: - if abs(proc_retcode) > abs(retcode): - retcode = proc_retcode - stdout += proc_out + for proc_retcode, proc_out, _ in results: + if abs(proc_retcode) > abs(retcode): + retcode = proc_retcode + stdout += proc_out return retcode, stdout diff --git a/testing/language_helpers.py b/testing/language_helpers.py index 05c94ebc..e7039414 100644 --- a/testing/language_helpers.py +++ b/testing/language_helpers.py @@ -18,6 +18,7 @@ def run_language( is_local: bool = False, require_serial: bool = True, color: bool = False, + dry_run: bool = False ) -> tuple[int, bytes]: prefix = Prefix(str(path)) version = version or language.get_default_version() @@ -35,6 +36,7 @@ def run_language( is_local=is_local, require_serial=require_serial, color=color, + dry_run=dry_run, ) out = out.replace(b'\r\n', b'\n') return ret, out