diff --git a/news/7693.feature b/news/7693.feature new file mode 100644 index 00000000000..4e458559110 --- /dev/null +++ b/news/7693.feature @@ -0,0 +1 @@ +Allow specifying ``--prefer-binary`` option in a requirements file diff --git a/src/pip/_internal/build_env.py b/src/pip/_internal/build_env.py index b8f005f5ca9..089a523b725 100644 --- a/src/pip/_internal/build_env.py +++ b/src/pip/_internal/build_env.py @@ -193,6 +193,8 @@ def install_requirements( args.extend(['--trusted-host', host]) if finder.allow_all_prereleases: args.append('--pre') + if finder.prefer_binary: + args.append('--prefer-binary') args.append('--') args.extend(requirements) with open_spinner(message) as spinner: diff --git a/src/pip/_internal/index/package_finder.py b/src/pip/_internal/index/package_finder.py index 441992b92b3..731e4981d72 100644 --- a/src/pip/_internal/index/package_finder.py +++ b/src/pip/_internal/index/package_finder.py @@ -693,6 +693,15 @@ def set_allow_all_prereleases(self): # type: () -> None self._candidate_prefs.allow_all_prereleases = True + @property + def prefer_binary(self): + # type: () -> bool + return self._candidate_prefs.prefer_binary + + def set_prefer_binary(self): + # type: () -> None + self._candidate_prefs.prefer_binary = True + def make_link_evaluator(self, project_name): # type: (str) -> LinkEvaluator canonical_name = canonicalize_name(project_name) diff --git a/src/pip/_internal/req/req_file.py b/src/pip/_internal/req/req_file.py index 63cab76f6f2..cde0b08d6dd 100644 --- a/src/pip/_internal/req/req_file.py +++ b/src/pip/_internal/req/req_file.py @@ -60,6 +60,7 @@ cmdoptions.find_links, cmdoptions.no_binary, cmdoptions.only_binary, + cmdoptions.prefer_binary, cmdoptions.require_hashes, cmdoptions.pre, cmdoptions.trusted_host, @@ -260,6 +261,9 @@ def handle_option_line( if opts.pre: finder.set_allow_all_prereleases() + if opts.prefer_binary: + finder.set_prefer_binary() + if session: for host in opts.trusted_hosts or []: source = 'line {} of {}'.format(lineno, filename) diff --git a/tests/functional/test_download.py b/tests/functional/test_download.py index 05b97ab3aec..e5bb89b7be5 100644 --- a/tests/functional/test_download.py +++ b/tests/functional/test_download.py @@ -688,6 +688,30 @@ def test_download_prefer_binary_when_tarball_higher_than_wheel(script, data): ) +def test_prefer_binary_tarball_higher_than_wheel_req_file(script, data): + fake_wheel(data, 'source-0.8-py2.py3-none-any.whl') + script.scratch_path.joinpath("test-req.txt").write_text(textwrap.dedent(""" + --prefer-binary + source + """)) + result = script.pip( + 'download', + '-r', script.scratch_path / 'test-req.txt', + '--no-index', + '-f', data.packages, + '-d', '.' + ) + + assert ( + Path('scratch') / 'source-0.8-py2.py3-none-any.whl' + in result.files_created + ) + assert ( + Path('scratch') / 'source-1.0.tar.gz' + not in result.files_created + ) + + def test_download_prefer_binary_when_wheel_doesnt_satisfy_req(script, data): fake_wheel(data, 'source-0.8-py2.py3-none-any.whl') script.scratch_path.joinpath("test-req.txt").write_text(textwrap.dedent(""" @@ -712,6 +736,30 @@ def test_download_prefer_binary_when_wheel_doesnt_satisfy_req(script, data): ) +def test_prefer_binary_when_wheel_doesnt_satisfy_req_req_file(script, data): + fake_wheel(data, 'source-0.8-py2.py3-none-any.whl') + script.scratch_path.joinpath("test-req.txt").write_text(textwrap.dedent(""" + --prefer-binary + source>0.9 + """)) + + result = script.pip( + 'download', + '--no-index', + '-f', data.packages, + '-d', '.', + '-r', script.scratch_path / 'test-req.txt' + ) + assert ( + Path('scratch') / 'source-1.0.tar.gz' + in result.files_created + ) + assert ( + Path('scratch') / 'source-0.8-py2.py3-none-any.whl' + not in result.files_created + ) + + def test_download_prefer_binary_when_only_tarball_exists(script, data): result = script.pip( 'download', @@ -726,6 +774,24 @@ def test_download_prefer_binary_when_only_tarball_exists(script, data): ) +def test_prefer_binary_when_only_tarball_exists_req_file(script, data): + script.scratch_path.joinpath("test-req.txt").write_text(textwrap.dedent(""" + --prefer-binary + source + """)) + result = script.pip( + 'download', + '--no-index', + '-f', data.packages, + '-d', '.', + '-r', script.scratch_path / 'test-req.txt' + ) + assert ( + Path('scratch') / 'source-1.0.tar.gz' + in result.files_created + ) + + @pytest.fixture(scope="session") def shared_script(tmpdir_factory, script_factory): tmpdir = Path(str(tmpdir_factory.mktemp("download_shared_script")))