From dfac6b321e76ab3cb23b987775859dde44468c09 Mon Sep 17 00:00:00 2001 From: Bahtya Date: Fri, 10 Apr 2026 00:13:44 +0800 Subject: [PATCH] Fix requirements-txt-fixer reordering pip options like --index-url The requirements-txt-fixer was alphabetically sorting all lines, including pip options like --index-url and --extra-index-url. This caused --extra-index-url to be sorted above --index-url, breaking pip's index resolution order since --index-url must come first. Fix by preserving the original relative order of lines that start with `--` (pip options), while still sorting regular package requirements alphabetically. Closes #612 Bahtya --- pre_commit_hooks/requirements_txt_fixer.py | 8 ++++++ tests/requirements_txt_fixer_test.py | 33 ++++++++++++++++++++++ 2 files changed, 41 insertions(+) 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):