diff --git a/pre_commit/commands/run.py b/pre_commit/commands/run.py index dd30c7e59..fe0350490 100644 --- a/pre_commit/commands/run.py +++ b/pre_commit/commands/run.py @@ -110,16 +110,25 @@ def _run_single_hook(classifier, hook, args, skips, cols, use_color): # Print the hook and the dots first in case the hook takes hella long to # run. - output.write( - get_hook_message( - _hook_msg_start(hook, args.verbose), end_len=6, cols=cols, - ), + msg, num_dots = get_hook_message( + _hook_msg_start(hook, args.verbose), end_len=6, cols=cols, ) + output.write(msg) sys.stdout.flush() + dots_printed = [0] + + def progress(perc): + dots_to_print = int(round(num_dots * perc)) + + sys.stdout.write('.' * (dots_to_print - dots_printed[0])) + sys.stdout.flush() + + dots_printed[0] = dots_to_print + diff_before = cmd_output_b('git', 'diff', '--no-ext-diff', retcode=None) filenames = tuple(filenames) if hook.pass_filenames else () - retcode, out = hook.run(filenames, use_color) + retcode, out = hook.run(filenames, use_color, progress) diff_after = cmd_output_b('git', 'diff', '--no-ext-diff', retcode=None) file_modifications = diff_before != diff_after diff --git a/pre_commit/languages/all.py b/pre_commit/languages/all.py index 051656b7d..99d57bf77 100644 --- a/pre_commit/languages/all.py +++ b/pre_commit/languages/all.py @@ -38,7 +38,7 @@ # version - A version specified in the hook configuration or 'default'. # """ # -# def run_hook(hook, file_args, color): +# def run_hook(hook, file_args, color, progress): # """Runs a hook and returns the returncode and output of running that # hook. # @@ -46,6 +46,7 @@ # hook - `Hook` # file_args - The files to be run # color - whether the hook should be given a pty (when supported) +# progress - report progress as a float percentage # # Returns: # (returncode, output) diff --git a/pre_commit/languages/docker.py b/pre_commit/languages/docker.py index b8cc5d07a..db8f0ff62 100644 --- a/pre_commit/languages/docker.py +++ b/pre_commit/languages/docker.py @@ -95,7 +95,7 @@ def docker_cmd(): # pragma: windows no cover ) -def run_hook(hook, file_args, color): # pragma: windows no cover +def run_hook(hook, file_args, color, progress): # pragma: windows no cover assert_docker_available() # Rebuild the docker image in case it has gone missing, as many people do # automated cleanup of docker images. @@ -106,4 +106,6 @@ def run_hook(hook, file_args, color): # pragma: windows no cover entry_tag = ('--entrypoint', entry_exe, docker_tag(hook.prefix)) cmd = docker_cmd() + entry_tag + cmd_rest - return helpers.run_xargs(hook, cmd, file_args, color=color) + return helpers.run_xargs( + hook, cmd, file_args, color=color, progress=progress, + ) diff --git a/pre_commit/languages/docker_image.py b/pre_commit/languages/docker_image.py index 7bd5c3140..030c03b24 100644 --- a/pre_commit/languages/docker_image.py +++ b/pre_commit/languages/docker_image.py @@ -12,7 +12,9 @@ install_environment = helpers.no_install -def run_hook(hook, file_args, color): # pragma: windows no cover +def run_hook(hook, file_args, color, progress): # pragma: windows no cover assert_docker_available() cmd = docker_cmd() + hook.cmd - return helpers.run_xargs(hook, cmd, file_args, color=color) + return helpers.run_xargs( + hook, cmd, file_args, color=color, progress=progress, + ) diff --git a/pre_commit/languages/golang.py b/pre_commit/languages/golang.py index d85a55c67..3238f2e92 100644 --- a/pre_commit/languages/golang.py +++ b/pre_commit/languages/golang.py @@ -81,6 +81,8 @@ def install_environment(prefix, version, additional_dependencies): rmtree(pkgdir) -def run_hook(hook, file_args, color): +def run_hook(hook, file_args, color, progress): with in_env(hook.prefix): - return helpers.run_xargs(hook, hook.cmd, file_args, color=color) + return helpers.run_xargs( + hook, hook.cmd, file_args, color=color, progress=progress, + ) diff --git a/pre_commit/languages/node.py b/pre_commit/languages/node.py index f5bc9bfaa..1ca93bf01 100644 --- a/pre_commit/languages/node.py +++ b/pre_commit/languages/node.py @@ -78,6 +78,8 @@ def install_environment( ) -def run_hook(hook, file_args, color): # pragma: windows no cover +def run_hook(hook, file_args, color, progress): # pragma: windows no cover with in_env(hook.prefix, hook.language_version): - return helpers.run_xargs(hook, hook.cmd, file_args, color=color) + return helpers.run_xargs( + hook, hook.cmd, file_args, color=color, progress=progress, + ) diff --git a/pre_commit/languages/python.py b/pre_commit/languages/python.py index c9bedb68c..5424efded 100644 --- a/pre_commit/languages/python.py +++ b/pre_commit/languages/python.py @@ -151,9 +151,11 @@ def healthy(prefix, language_version): ) return retcode == 0 - def run_hook(hook, file_args, color): + def run_hook(hook, file_args, color, progress): with in_env(hook.prefix, hook.language_version): - return helpers.run_xargs(hook, hook.cmd, file_args, color=color) + return helpers.run_xargs( + hook, hook.cmd, file_args, color=color, progress=progress, + ) def install_environment(prefix, version, additional_dependencies): additional_dependencies = tuple(additional_dependencies) diff --git a/pre_commit/languages/ruby.py b/pre_commit/languages/ruby.py index 83e2a6faf..dadff8802 100644 --- a/pre_commit/languages/ruby.py +++ b/pre_commit/languages/ruby.py @@ -124,6 +124,8 @@ def install_environment( ) -def run_hook(hook, file_args, color): # pragma: windows no cover +def run_hook(hook, file_args, color, progress): # pragma: windows no cover with in_env(hook.prefix, hook.language_version): - return helpers.run_xargs(hook, hook.cmd, file_args, color=color) + return helpers.run_xargs( + hook, hook.cmd, file_args, color=color, progress=progress, + ) diff --git a/pre_commit/languages/rust.py b/pre_commit/languages/rust.py index 91291fb34..351ab18cb 100644 --- a/pre_commit/languages/rust.py +++ b/pre_commit/languages/rust.py @@ -89,6 +89,8 @@ def install_environment(prefix, version, additional_dependencies): ) -def run_hook(hook, file_args, color): +def run_hook(hook, file_args, color, progress): with in_env(hook.prefix): - return helpers.run_xargs(hook, hook.cmd, file_args, color=color) + return helpers.run_xargs( + hook, hook.cmd, file_args, color=color, progress=progress, + ) diff --git a/pre_commit/languages/script.py b/pre_commit/languages/script.py index 96b8aeb6f..48178e068 100644 --- a/pre_commit/languages/script.py +++ b/pre_commit/languages/script.py @@ -9,7 +9,9 @@ install_environment = helpers.no_install -def run_hook(hook, file_args, color): +def run_hook(hook, file_args, color, progress): cmd = hook.cmd cmd = (hook.prefix.path(cmd[0]),) + cmd[1:] - return helpers.run_xargs(hook, cmd, file_args, color=color) + return helpers.run_xargs( + hook, cmd, file_args, color=color, progress=progress, + ) diff --git a/pre_commit/languages/swift.py b/pre_commit/languages/swift.py index 014349596..df55ef9c4 100644 --- a/pre_commit/languages/swift.py +++ b/pre_commit/languages/swift.py @@ -51,6 +51,8 @@ def install_environment( ) -def run_hook(hook, file_args, color): # pragma: windows no cover +def run_hook(hook, file_args, color, progress): # pragma: windows no cover with in_env(hook.prefix): - return helpers.run_xargs(hook, hook.cmd, file_args, color=color) + return helpers.run_xargs( + hook, hook.cmd, file_args, color=color, progress=progress, + ) diff --git a/pre_commit/languages/system.py b/pre_commit/languages/system.py index b412b368c..164b995df 100644 --- a/pre_commit/languages/system.py +++ b/pre_commit/languages/system.py @@ -9,5 +9,7 @@ install_environment = helpers.no_install -def run_hook(hook, file_args, color): - return helpers.run_xargs(hook, hook.cmd, file_args, color=color) +def run_hook(hook, file_args, color, progress): + return helpers.run_xargs( + hook, hook.cmd, file_args, color=color, progress=progress, + ) diff --git a/pre_commit/output.py b/pre_commit/output.py index 478ad5e65..30e8cb2ea 100644 --- a/pre_commit/output.py +++ b/pre_commit/output.py @@ -54,7 +54,10 @@ def get_hook_message( ) if end_len: - return start + '.' * (cols - len(start) - end_len - 1) + num_dots = cols - len(start) - end_len - 1 + if end_msg is None: + return start, num_dots + return start + '.' * num_dots else: return '{}{}{}{}\n'.format( start, diff --git a/pre_commit/repository.py b/pre_commit/repository.py index 3042f12dc..249b8cbb3 100644 --- a/pre_commit/repository.py +++ b/pre_commit/repository.py @@ -100,9 +100,9 @@ def install(self): # Write our state to indicate we're installed _write_state(self.prefix, venv, _state(self.additional_dependencies)) - def run(self, file_args, color): + def run(self, file_args, color, progress): lang = languages[self.language] - return lang.run_hook(self, file_args, color) + return lang.run_hook(self, file_args, color, progress) @classmethod def create(cls, src, prefix, dct): diff --git a/pre_commit/xargs.py b/pre_commit/xargs.py index 4c3ddacfc..3b72be0bb 100644 --- a/pre_commit/xargs.py +++ b/pre_commit/xargs.py @@ -112,6 +112,7 @@ def xargs(cmd, varargs, **kwargs): color = kwargs.pop('color', False) negate = kwargs.pop('negate', False) target_concurrency = kwargs.pop('target_concurrency', 1) + progress = kwargs.pop('progress', lambda _: None) max_length = kwargs.pop('_max_length', _get_platform_max_length()) cmd_fn = cmd_output_p if color else cmd_output_b retcode = 0 @@ -125,15 +126,17 @@ def xargs(cmd, varargs, **kwargs): partitions = partition(cmd, varargs, target_concurrency, max_length) def run_cmd_partition(run_cmd): - return cmd_fn( + ret = cmd_fn( *run_cmd, retcode=None, stderr=subprocess.STDOUT, **kwargs ) + return ret, len(run_cmd) - len(cmd) 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: + total_processed = 0 + for (proc_retcode, proc_out, _), processed in results: # This is *slightly* too clever so I'll explain it. # First the xor boolean table: # T | F | @@ -146,5 +149,7 @@ def run_cmd_partition(run_cmd): # code. Otherwise, the returncode is unchanged. retcode |= bool(proc_retcode) ^ negate stdout += proc_out + total_processed += processed + progress(float(total_processed) / len(varargs)) return retcode, stdout