Repository: jazzband/pip-tools

Issue:
ignore current hashes using `--no-reuse-hashes`
it might be worth always getting the hashes now rather than skipping them if the hashes are already in the requirements.txt (this behaviour causes me problems, eg when new wheels are uploaded to an old version I don't get the hash of the new wheel)


We could ignore current hashes using `--rebuild`. What do you think?

_Originally posted by @atugushev in https://github.com/jazzband/pip-tools/pull/1109#issuecomment-616114776_

see also https://github.com/jazzband/pip-tools/pull/641/files



Discussion:

Resolving patch:
diff --git a/piptools/repositories/local.py b/piptools/repositories/local.py
--- a/piptools/repositories/local.py
+++ b/piptools/repositories/local.py
@@ -33,7 +33,8 @@ class LocalRequirementsRepository(BaseRepository):
     PyPI.  This keeps updates to the requirements.txt down to a minimum.
     """
 
-    def __init__(self, existing_pins, proxied_repository):
+    def __init__(self, existing_pins, proxied_repository, reuse_hashes=True):
+        self._reuse_hashes = reuse_hashes
         self.repository = proxied_repository
         self.existing_pins = existing_pins
 
@@ -74,8 +75,9 @@ def get_dependencies(self, ireq):
         return self.repository.get_dependencies(ireq)
 
     def get_hashes(self, ireq):
-        key = key_from_ireq(ireq)
-        existing_pin = self.existing_pins.get(key)
+        existing_pin = self._reuse_hashes and self.existing_pins.get(
+            key_from_ireq(ireq)
+        )
         if existing_pin and ireq_satisfied_by_existing_pin(ireq, existing_pin):
             if PIP_VERSION[:2] <= (20, 0):
                 hashes = existing_pin.options.get("hashes", {})
diff --git a/piptools/scripts/compile.py b/piptools/scripts/compile.py
--- a/piptools/scripts/compile.py
+++ b/piptools/scripts/compile.py
@@ -183,6 +183,15 @@ def has_arg(self, arg_name):
     default=False,
     help="Generate pip 8 style hashes in the resulting requirements file.",
 )
+@click.option(
+    "--reuse-hashes/--no-reuse-hashes",
+    is_flag=True,
+    default=True,
+    help=(
+        "Improve the speed of --generate-hashes by reusing the hashes from an "
+        "existing output file."
+    ),
+)
 @click.option(
     "--max-rounds",
     default=10,
@@ -241,6 +250,7 @@ def cli(
     output_file,
     allow_unsafe,
     generate_hashes,
+    reuse_hashes,
     src_files,
     max_rounds,
     build_isolation,
@@ -360,7 +370,9 @@ def cli(
                 existing_pins_to_upgrade.add(key)
             else:
                 existing_pins[key] = ireq
-        repository = LocalRequirementsRepository(existing_pins, repository)
+        repository = LocalRequirementsRepository(
+            existing_pins, repository, reuse_hashes=reuse_hashes
+        )
 
     ###
     # Parsing/collecting initial requirements


Test patch:
diff --git a/tests/test_repository_local.py b/tests/test_repository_local.py
--- a/tests/test_repository_local.py
+++ b/tests/test_repository_local.py
@@ -1,5 +1,7 @@
 import copy
 
+import pytest
+
 from tests.conftest import FakeRepository
 
 from piptools.repositories.local import LocalRequirementsRepository
@@ -29,6 +31,27 @@ def test_get_hashes_local_repository_cache_hit(from_line, repository):
         assert hashes == EXPECTED
 
 
+NONSENSE = {"sha256:NONSENSE"}
+
+
+@pytest.mark.parametrize(
+    ("reuse_hashes", "expected"), ((True, NONSENSE), (False, EXPECTED))
+)
+def test_toggle_reuse_hashes_local_repository(
+    pip_conf, from_line, pypi_repository, reuse_hashes, expected
+):
+    # Create an install requirement with the hashes included in its options
+    options = {"hashes": {"sha256": [entry.split(":")[1] for entry in NONSENSE]}}
+    req = from_line("small-fake-a==0.1", options=options)
+    existing_pins = {name_from_req(req): req}
+
+    local_repository = LocalRequirementsRepository(
+        existing_pins, pypi_repository, reuse_hashes=reuse_hashes
+    )
+    with local_repository.allow_all_wheels():
+        assert local_repository.get_hashes(from_line("small-fake-a==0.1")) == expected
+
+
 class FakeRepositoryChecksForCopy(FakeRepository):
     def __init__(self):
         super(FakeRepositoryChecksForCopy, self).__init__()