diff --git a/README.rst b/README.rst index 7cddf5e..45e29eb 100644 --- a/README.rst +++ b/README.rst @@ -254,6 +254,10 @@ Version History next + * Order of hashes should not affect if a package in the requirements file + should be replaced or not. + See https://github.com/peterbe/hashin/issues/93 + * (Internal) All tests have been rewritten as plain pytest functions. * In Python 3, if the package can't be found you get a more explicit exception diff --git a/hashin.py b/hashin.py index 3ed9677..bbec469 100755 --- a/hashin.py +++ b/hashin.py @@ -230,10 +230,26 @@ def run_packages( def amend_requirements_content(requirements, all_new_lines): - # I wish we had types! assert isinstance(all_new_lines, list), type(all_new_lines) + padding = " " * 4 + + def is_different_lines(package, new_lines): + # This assumes that for sure the package is already mentioned in the old + # requirements. Now we just need to double-check that they really are + # different. + # The 'new_lines` is what we might intend to replace it with. + lines = set() + for line in requirements.splitlines(): + if regex.search(line): + lines.add(line.strip(" \\")) + elif lines and line.startswith(padding): + lines.add(line.strip(" \\")) + elif lines: + break + return lines != set([x.strip(" \\") for x in new_lines.splitlines()]) + for package, new_lines in all_new_lines: regex = re.compile( r"(^|\n|\n\r){0}==|(^|\n|\n\r){0}\[.*\]==".format(re.escape(package)), @@ -245,10 +261,9 @@ def amend_requirements_content(requirements, all_new_lines): if requirements: requirements = requirements.strip() + "\n" requirements += new_lines.strip() + "\n" - else: + elif is_different_lines(package, new_lines): # need to replace the existing lines = [] - padding = " " * 4 for line in requirements.splitlines(): if regex.search(line): lines.append(line) @@ -509,11 +524,8 @@ def get_package_hashes( else: raise PackageError("No releases could be found for {0}".format(version)) - # Sorting them helps make sure the results are more predictable - # when running more than once for the same version - hashes = sorted( - get_releases_hashes(releases=releases, algorithm=algorithm, verbose=verbose), - key=lambda x: x["hash"], + hashes = list( + get_releases_hashes(releases=releases, algorithm=algorithm, verbose=verbose) ) return {"package": package, "version": version, "hashes": hashes} diff --git a/tests/test_cli.py b/tests/test_cli.py index f48b3de..3615e37 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -304,6 +304,55 @@ def test_amend_requirements_content_replacement(): assert result == new_lines[1] +def test_amend_requirements_content_actually_not_replacement(): + requirements = ( + """ +autocompeter==1.2.2 + --hash=sha256:33a5d0145e82326e781ddee1ad375f92cb84f8cfafea56e9504682adff64a5ee + --hash=sha256:4d64ed1b9e0e73095f5cfa87f0e97ddb4c840049e8efeb7e63b46118ba1d623a + """.strip() + + "\n" + ) + + new_lines = ( + "autocompeter", + """ +autocompeter==1.2.2 + --hash=sha256:4d64ed1b9e0e73095f5cfa87f0e97ddb4c840049e8efeb7e63b46118ba1d623a + --hash=sha256:33a5d0145e82326e781ddee1ad375f92cb84f8cfafea56e9504682adff64a5ee + """.strip() + + "\n", + ) + + result = hashin.amend_requirements_content(requirements, [new_lines]) + # It should be unchanged because the only thing that changed was the + # order of the --hash lines. + assert result == requirements + + +def test_amend_requirements_content_replacement_addition(): + requirements = ( + """ +autocompeter==1.2.2 + --hash=sha256:33a5d0145e82326e781ddee1ad375f92cb84f8cfafea56e9504682adff64a5ee + """.strip() + + "\n" + ) + + new_lines = ( + "autocompeter", + """ +autocompeter==1.2.2 + --hash=sha256:4d64ed1b9e0e73095f5cfa87f0e97ddb4c840049e8efeb7e63b46118ba1d623a + --hash=sha256:33a5d0145e82326e781ddee1ad375f92cb84f8cfafea56e9504682adff64a5ee + """.strip() + + "\n", + ) + + result = hashin.amend_requirements_content(requirements, [new_lines]) + assert result == new_lines[1] + + def test_amend_requirements_content_replacement_single_to_multi(): """Change from autocompeter==1.2.2 to autocompeter==1.2.3 when it was previously written as a single line and now @@ -512,21 +561,21 @@ def mocked_get(url, **options): assert output.endswith("\n") lines = output.splitlines() assert lines[0] == "hashin==0.10 \\" - assert lines[1] == ( + assert ( " --hash=sha512:0d63bf4c115154781846ecf573049324f06b021a1" "d4b92da4fae2bf491da2b83a13096b14d73e73cefad36855f4fa936bac4" "b2357dabf05a2b1e7329ff1e5455 \\" - ) - assert lines[2] == ( + ) in lines + assert ( " --hash=sha512:45d1c5d2237a3b4f78b4198709fb2ecf1f781c823" "4ce3d94356f2100a36739433952c6c13b2843952f608949e6baa9f95055" "a314487cd8fb3f9d76522d8edb50 \\" - ) - assert lines[3] == ( + ) in lines + assert ( " --hash=sha512:c32e6d9fb09dc36ab9222c4606a1f43a2dcc183a8" "c64bdd9199421ef779072c174fa044b155babb12860cf000e36bc4d3586" "94fa22420c997b1dd75b623d4daa" - ) + ) in lines def test_run_atomic_not_write_with_error_on_last_package(murlopen, tmpfile): @@ -1449,10 +1498,10 @@ def mocked_get(url, **options): "version": "0.10", "hashes": [ { - "hash": "0d63bf4c115154781846ecf573049324f06b021a1d4b92da4fae2bf491da2b83a13096b14d73e73cefad36855f4fa936bac4b2357dabf05a2b1e7329ff1e5455" + "hash": "45d1c5d2237a3b4f78b4198709fb2ecf1f781c8234ce3d94356f2100a36739433952c6c13b2843952f608949e6baa9f95055a314487cd8fb3f9d76522d8edb50" }, { - "hash": "45d1c5d2237a3b4f78b4198709fb2ecf1f781c8234ce3d94356f2100a36739433952c6c13b2843952f608949e6baa9f95055a314487cd8fb3f9d76522d8edb50" + "hash": "0d63bf4c115154781846ecf573049324f06b021a1d4b92da4fae2bf491da2b83a13096b14d73e73cefad36855f4fa936bac4b2357dabf05a2b1e7329ff1e5455" }, { "hash": "c32e6d9fb09dc36ab9222c4606a1f43a2dcc183a8c64bdd9199421ef779072c174fa044b155babb12860cf000e36bc4d358694fa22420c997b1dd75b623d4daa"