From b42eeb11e92687dc0329ec786a004917fb5d5cad Mon Sep 17 00:00:00 2001 From: anatoly techtonik Date: Thu, 5 Sep 2019 15:17:23 +0300 Subject: [PATCH] Support indented requirements (#114) * Support indented requirements (fixes #112) * Refactor amend_requirements_content() Remove duplicate code from is_different_lines(). * Indent only non-empty lines * Refactor and make linter happy Rename new_lines to new_text where it is not list * Add test for indented spec --- hashin.py | 46 ++++++++++++++++++++++++---------------------- tests/test_cli.py | 40 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+), 22 deletions(-) diff --git a/hashin.py b/hashin.py index a053ca1..515e037 100755 --- a/hashin.py +++ b/hashin.py @@ -387,43 +387,45 @@ def amend_requirements_content(requirements, 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 + def is_different_lines(old_lines, new_lines, indent): + # This assumes that the package is already mentioned in the old + # requirements. Now we just need to double-check that its lines 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, old_name, new_lines in all_new_lines: + old = set([l.strip(" \\") for l in old_lines]) + new = set([indent + x.strip(" \\") for x in new_lines]) + return old != new + + for package, old_name, new_text in all_new_lines: regex = re.compile( - r"^{0}(\[.*\])?==".format(re.escape(old_name)), re.IGNORECASE | re.MULTILINE + r"^(?P[ \t]*){0}(\[.*\])?==".format(re.escape(old_name)), + re.IGNORECASE | re.MULTILINE, ) # if the package wasn't already there, add it to the bottom - if not regex.search(requirements): + match = regex.search(requirements) + if not match: # easy peasy if requirements: requirements = requirements.strip() + "\n" - requirements += new_lines.strip() + "\n" - elif is_different_lines(package, new_lines): - # need to replace the existing + requirements += new_text.strip() + "\n" + else: + indent = match.group("indent") lines = [] for line in requirements.splitlines(): if regex.search(line): lines.append(line) - elif lines and line.startswith(padding): + elif lines and line.startswith(indent + padding): lines.append(line) elif lines: break - combined = "\n".join(lines + [""]) - requirements = requirements.replace(combined, new_lines) + if is_different_lines(lines, new_text.splitlines(), indent): + # need to replace the existing + combined = "\n".join(lines + [""]) + # indent non-empty lines + indented = re.sub( + r"^(.+)$", r"{0}\1".format(indent), new_text, flags=re.MULTILINE + ) + requirements = requirements.replace(combined, indented) return requirements diff --git a/tests/test_cli.py b/tests/test_cli.py index 8c60ba0..6bf9cc6 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -579,6 +579,46 @@ def test_amend_requirements_content_new_similar_name(): assert new_lines[2] in result +def test_amend_requirements_content_indented(): + """This test came from https://github.com/peterbe/hashin/issues/112""" + requirements = ( + """ +boto3==1.9.85 \\ + --hash=sha256:acfd27967cf1ba7f9d83ad6fc2011764541e4c295fe0d896ea7b495cc2f03336 \\ + --hash=sha256:96296871863e0245b04931df7dd5c583e53cadbe1d54197829b34b03b0d048a8 + + botocore==1.12.85 \\ + --hash=sha256:af727d4af0cf1ddbf84eaf1cc9d0160ff066eac7f9e6a2fe6a75ccbed4452c98 \\ + --hash=sha256:c381fd05b777f41a608ea0846a8d8ecc32077a83e456d05e824cce8d6b213e32 + """.strip() + + "\n" + ) + expect = ( + """ +boto3==1.9.85 \\ + --hash=sha256:acfd27967cf1ba7f9d83ad6fc2011764541e4c295fe0d896ea7b495cc2f03336 \\ + --hash=sha256:96296871863e0245b04931df7dd5c583e53cadbe1d54197829b34b03b0d048a8 + + botocore==1.12.221 \\ + --hash=sha256:6d49deff062d2ae0f03fc26b56df8b1bb9e8b136657bcd8d84c986a4068fb784 \\ + --hash=sha256:bbee3fdcbe56ca53e2c32c6c12d174fa9b4ffe27b633183c29bd5aec9e200bae + """.strip() + + "\n" + ) + new_lines = ( + "botocore", + "botocore", + """ +botocore==1.12.221 \\ + --hash=sha256:6d49deff062d2ae0f03fc26b56df8b1bb9e8b136657bcd8d84c986a4068fb784 \\ + --hash=sha256:bbee3fdcbe56ca53e2c32c6c12d174fa9b4ffe27b633183c29bd5aec9e200bae + """.strip() + + "\n", + ) + result = hashin.amend_requirements_content(requirements, [new_lines]) + assert result == expect + + def test_run(murlopen, tmpfile, capsys): def mocked_get(url, **options): if url == "https://pypi.org/pypi/hashin/json":