Skip to content

Commit

Permalink
Add always-install-via-wheel feature
Browse files Browse the repository at this point in the history
When enabled this feature does the following:

- the presence of --build-option, --install-option, --global-option
  no longer imply --no-binary :all:
- --no-binary does not disable wheel building anymore, so a wheel
  is built during the install process and setup.py install is
  not used anymore in this case
  • Loading branch information
sbidoul committed Apr 5, 2021
1 parent ec8b383 commit 22436af
Show file tree
Hide file tree
Showing 5 changed files with 73 additions and 5 deletions.
12 changes: 12 additions & 0 deletions news/9778.feature.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
Add an experimental ``always-install-via-wheel`` feature flag. When enabled, it has the
following effects:

- Installation of source distributions is always done by first building a wheel
then installing from it (assuming the ``wheel`` package is installed).
- ``--no-binary`` now only means "do not download wheels", whereas before it also
implied using the legacy ``setup.py install`` and disabling the wheel cache.
- The use of ``--install-option``, ``--build-option``, ``--global-option`` no longer
imply ``--no-binary :all:``.
- ``--build-option`` and ``--global-option`` are passed to ``setup.py bdist_wheel``
during installation, as it was already done when using ``pip wheel``.
- ``--install-option`` is ignored (because there is no ``setup.py install`` involved)
11 changes: 10 additions & 1 deletion src/pip/_internal/cli/cmdoptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,15 @@ def getname(n):
# type: (str) -> Optional[Any]
return getattr(check_options, n, None)

if "always-install-via-wheel" in options.features_enabled:
if getname("install_options"):
warnings.warn(
"Ignoring --install-option because 'always-install-via-wheel' "
"is enabled.",
stacklevel=2,
)
return

names = ["build_options", "global_options", "install_options"]
if any(map(getname, names)):
control = options.format_control
Expand Down Expand Up @@ -965,7 +974,7 @@ def check_list_path_option(options):
metavar="feature",
action="append",
default=[],
choices=["2020-resolver", "fast-deps", "in-tree-build"],
choices=["2020-resolver", "fast-deps", "in-tree-build", "always-install-via-wheel"],
help="Enable new functionality, that may be backward incompatible.",
) # type: Callable[..., Option]

Expand Down
8 changes: 5 additions & 3 deletions src/pip/_internal/commands/install.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,12 @@
logger = logging.getLogger(__name__)


def get_check_binary_allowed(format_control):
# type: (FormatControl) -> BinaryAllowedPredicate
def get_check_binary_allowed(format_control, features_enabled):
# type: (FormatControl, List[str]) -> BinaryAllowedPredicate
def check_binary_allowed(req):
# type: (InstallRequirement) -> bool
if "always-install-via-wheel" in features_enabled:
return True
canonical_name = canonicalize_name(req.name or "")
allowed_formats = format_control.get_allowed_formats(canonical_name)
return "binary" in allowed_formats
Expand Down Expand Up @@ -330,7 +332,7 @@ def run(self, options, args):
)

check_binary_allowed = get_check_binary_allowed(
finder.format_control
finder.format_control, options.features_enabled
)

reqs_to_build = [
Expand Down
2 changes: 1 addition & 1 deletion src/pip/_internal/wheel_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ def _should_build(

if not check_binary_allowed(req):
logger.info(
"Skipping wheel build for %s, due to binaries "
"Using legacy 'setup.py install' for %s, due to binaries "
"being disabled for it.", req.name,
)
return False
Expand Down
45 changes: 45 additions & 0 deletions tests/functional/test_install.py
Original file line number Diff line number Diff line change
Expand Up @@ -724,6 +724,17 @@ def test_install_global_option(script):
assert not result.files_created


def test_install_global_option_does_not_disable_wheel_building(
script, data, with_wheel
):
res = script.pip(
'install', '--no-index', '--global-option=-v', '-f', data.find_links,
'--use-feature=always-install-via-wheel',
'upper', expect_stderr=True)
assert "Successfully installed upper-2.0" in str(res), str(res)
assert "Building wheel for upper" in str(res), str(res)


def test_install_with_hacked_egg_info(script, data):
"""
test installing a package which defines its own egg_info class
Expand Down Expand Up @@ -1439,6 +1450,17 @@ def test_install_no_binary_disables_building_wheels(script, data, with_wheel):
assert "Running setup.py install for upper" in str(res), str(res)


def test_install_no_binary_does_not_disable_building_wheels(
script, data, with_wheel
):
res = script.pip(
'install', '--no-index', '--no-binary=upper', '-f', data.find_links,
'--use-feature=always-install-via-wheel',
'upper', expect_stderr=True)
assert "Successfully installed upper-2.0" in str(res), str(res)
assert "Building wheel for upper" in str(res), str(res)


@pytest.mark.network
def test_install_no_binary_builds_pep_517_wheel(script, data, with_wheel):
to_install = data.packages.joinpath('pep517_setup_and_pyproject')
Expand Down Expand Up @@ -1484,6 +1506,29 @@ def test_install_no_binary_disables_cached_wheels(script, data, with_wheel):
assert "Running setup.py install for upper" in str(res), str(res)


def test_install_no_binary_does_not_disables_cached_wheels(script, data, with_wheel):
"""When using always-install-via-wheel, --no-binary does not disable
the wheel cache"""
# Seed the cache
res = script.pip(
'install', '--no-index',
'--use-feature=always-install-via-wheel',
'--no-binary=:all:', '-f', data.find_links,
'upper')
# wheel building occured for upper
assert "Building wheel for upper" in str(res), str(res)
script.pip('uninstall', 'upper', '-y')
res = script.pip(
'install', '--no-index',
'--use-feature=always-install-via-wheel',
'--no-binary=:all:', '-f', data.find_links,
'upper', expect_stderr=True)
assert "Successfully installed upper-2.0" in str(res), str(res)
# Must have used cached wheel.
assert "Building wheel for upper" not in str(res), str(res)
assert "Using cached Upper" in str(res), str(res)


def test_install_editable_with_wrong_egg_name(script, resolver_variant):
script.scratch_path.joinpath("pkga").mkdir()
pkga_path = script.scratch_path / 'pkga'
Expand Down

0 comments on commit 22436af

Please sign in to comment.