diff --git a/piptools/writer.py b/piptools/writer.py index bcb70f02..da603f1f 100644 --- a/piptools/writer.py +++ b/piptools/writer.py @@ -148,9 +148,27 @@ def write_trusted_hosts(self) -> Iterator[str]: yield f"--trusted-host {trusted_host}" def write_format_controls(self) -> Iterator[str]: - for nb in dedup(sorted(self.format_control.no_binary)): + # The ordering of output needs to preserve the behavior of pip's + # FormatControl.get_allowed_formats(). The behavior is the following: + # + # * Parsing of CLI options happens first to last. + # * --only-binary takes precedence over --no-binary + # * Package names take precedence over :all: + # * We'll never see :all: in both due to mutual exclusion. + # + # So in summary, we want to emit :all: first and then package names later. + no_binary = self.format_control.no_binary.copy() + only_binary = self.format_control.only_binary.copy() + + if ":all:" in no_binary: + yield "--no-binary :all:" + no_binary.remove(":all:") + if ":all:" in only_binary: + yield "--only-binary :all:" + only_binary.remove(":all:") + for nb in dedup(sorted(no_binary)): yield f"--no-binary {nb}" - for ob in dedup(sorted(self.format_control.only_binary)): + for ob in dedup(sorted(only_binary)): yield f"--only-binary {ob}" def write_find_links(self) -> Iterator[str]: diff --git a/tests/test_writer.py b/tests/test_writer.py index 8a59269c..dd435448 100644 --- a/tests/test_writer.py +++ b/tests/test_writer.py @@ -301,6 +301,41 @@ def test_write_format_controls(writer): assert lines == expected_lines +@pytest.mark.parametrize( + ("no_binary", "only_binary", "expected_lines"), + ( + ( + [":all:"], + ["django"], + [ + "--no-binary :all:", + "--only-binary django", + ], + ), + ( + ["django"], + [":all:"], + [ + "--only-binary :all:", + "--no-binary django", + ], + ), + ), +) +def test_write_format_controls_all(writer, no_binary, only_binary, expected_lines): + """ + Tests --no-binary/--only-binary options + with the value of :all:. We want to preserve + the FormatControl behavior so we emit :all: + first before packages. + """ + + writer.format_control = FormatControl(no_binary=no_binary, only_binary=only_binary) + lines = list(writer.write_format_controls()) + + assert lines == expected_lines + + @pytest.mark.parametrize( ("index_urls", "expected_lines"), (