From 45e3dab00ddbd1763543438381ebf184f19319c9 Mon Sep 17 00:00:00 2001 From: Chris Kuehl Date: Mon, 19 Nov 2018 17:36:57 -0800 Subject: [PATCH] Shuffle arguments before running hooks --- pre_commit/languages/helpers.py | 22 ++++++++++++++++++++++ tests/languages/helpers_test.py | 4 ++++ 2 files changed, 26 insertions(+) diff --git a/pre_commit/languages/helpers.py b/pre_commit/languages/helpers.py index aa5a5d13..7ab117bf 100644 --- a/pre_commit/languages/helpers.py +++ b/pre_commit/languages/helpers.py @@ -2,12 +2,18 @@ from __future__ import unicode_literals import multiprocessing import os +import random import shlex +import six + from pre_commit.util import cmd_output from pre_commit.xargs import xargs +FIXED_RANDOM_SEED = 1542676186 + + def run_setup_cmd(prefix, cmd): cmd_output(*cmd, cwd=prefix.prefix_dir, encoding=None) @@ -64,5 +70,21 @@ def target_concurrency(hook): return 1 +def _shuffled(seq): + """Deterministically shuffle identically under both py2 + py3.""" + fixed_random = random.Random() + if six.PY2: # pragma: no cover (py2) + fixed_random.seed(FIXED_RANDOM_SEED) + else: + fixed_random.seed(FIXED_RANDOM_SEED, version=1) + + seq = list(seq) + random.shuffle(seq, random=fixed_random.random) + return seq + + def run_xargs(hook, cmd, file_args): + # Shuffle the files so that they more evenly fill out the xargs partitions, + # but do it deterministically in case a hook cares about ordering. + file_args = _shuffled(file_args) return xargs(cmd, file_args, target_concurrency=target_concurrency(hook)) diff --git a/tests/languages/helpers_test.py b/tests/languages/helpers_test.py index e7bd4702..f77c3053 100644 --- a/tests/languages/helpers_test.py +++ b/tests/languages/helpers_test.py @@ -62,3 +62,7 @@ def test_target_concurrency_cpu_count_not_implemented(): ): with mock.patch.dict(os.environ, {}, clear=True): assert helpers.target_concurrency({'require_serial': False}) == 1 + + +def test_shuffled_is_deterministic(): + assert helpers._shuffled(range(10)) == [3, 7, 8, 2, 4, 6, 5, 1, 0, 9]