diff --git a/pre_commit_hooks/requirements_txt_fixer.py b/pre_commit_hooks/requirements_txt_fixer.py index 8ce8ec64..459a8c82 100644 --- a/pre_commit_hooks/requirements_txt_fixer.py +++ b/pre_commit_hooks/requirements_txt_fixer.py @@ -45,6 +45,14 @@ def __lt__(self, requirement: Requirement) -> bool: elif requirement.value == b'\n': return False else: + # if both are pip options (start with --), maintain original + # order to avoid breaking semantic ordering + # (e.g. --index-url must come before --extra-index-url) + if ( + self.name.startswith(b'--') and + requirement.name.startswith(b'--') + ): + return False # if 2 requirements have the same name, the one with comments # needs to go first (so that when removing duplicates, the one # with comments is kept) diff --git a/tests/requirements_txt_fixer_test.py b/tests/requirements_txt_fixer_test.py index c0d2c65d..fd943db2 100644 --- a/tests/requirements_txt_fixer_test.py +++ b/tests/requirements_txt_fixer_test.py @@ -107,6 +107,39 @@ PASS, b'a=2.0.0 \\\n --hash=sha256:abcd\nb==1.0.0\n', ), + # --index-url and --extra-index-url should maintain original order + # see: https://github.com/pre-commit/pre-commit-hooks/issues/612 + ( + b'--index-url https://example.com/simple\n' + b'--extra-index-url https://example.com/extra\n' + b'foo\n', + PASS, + b'--index-url https://example.com/simple\n' + b'--extra-index-url https://example.com/extra\n' + b'foo\n', + ), + # pip options should not be reordered even if out of alpha order + ( + b'--extra-index-url https://example.com/extra\n' + b'--index-url https://example.com/simple\n' + b'foo\n', + PASS, + b'--extra-index-url https://example.com/extra\n' + b'--index-url https://example.com/simple\n' + b'foo\n', + ), + # packages should still be sorted while pip options stay in place + ( + b'--index-url https://example.com/simple\n' + b'--extra-index-url https://example.com/extra\n' + b'foo\n' + b'bar\n', + FAIL, + b'--index-url https://example.com/simple\n' + b'--extra-index-url https://example.com/extra\n' + b'bar\n' + b'foo\n', + ), ), ) def test_integration(input_s, expected_retval, output, tmpdir):