Skip to content

Commit

Permalink
Issue #2675: Granular control over wheels/sdists
Browse files Browse the repository at this point in the history
With wheel autobuilding in place a release blocker is some granular
way to opt-out of wheels for known-bad packages. This patch introduces
two new options: --no-binary and --only-binary to control what
archives we are willing to use on both a global and per-package basis.

This also closes #2084
  • Loading branch information
rbtcollins committed Apr 20, 2015
1 parent a1a0147 commit c889d84
Show file tree
Hide file tree
Showing 19 changed files with 285 additions and 38 deletions.
3 changes: 3 additions & 0 deletions CHANGES.txt
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@
* Build Wheels prior to installing from sdist, caching them in the pip cache
directory to speed up subsequent installs. (:pull:`2618`)

* Allow fine grained control over the use of wheels and source builds.
(:pull:`2699)

**6.1.1 (2015-04-07)**

* No longer ignore dependencies which have been added to the standard library,
Expand Down
3 changes: 2 additions & 1 deletion docs/reference/pip_install.rst
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,8 @@ Additionally, the following Package Index Options are supported:
* :ref:`--allow-external <--allow-external>`
* :ref:`--allow-all-external <--allow-external>`
* :ref:`--allow-unverified <--allow-unverified>`
* :ref:`--no-use-wheel <install_--no-use-wheel>`
* :ref:`--no-binary <install_--no-binary>`
* :ref:`--only-binary <install_--only-binary>`

For example, to specify :ref:`--no-index <--no-index>` and 2 :ref:`--find-links <--find-links>` locations:

Expand Down
2 changes: 1 addition & 1 deletion docs/user_guide.rst
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ to building and installing from source archives. For more information, see the
`PEP425 <http://www.python.org/dev/peps/pep-0425>`_

Pip prefers Wheels where they are available. To disable this, use the
:ref:`--no-use-wheel <install_--no-use-wheel>` flag for :ref:`pip install`.
:ref:`--no-binary <install_--no-binary>` flag for :ref:`pip install`.

If no satisfactory wheels are found, pip will default to finding source archives.

Expand Down
57 changes: 55 additions & 2 deletions pip/cmdoptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@

from functools import partial
from optparse import OptionGroup, SUPPRESS_HELP, Option
from pip.index import PyPI

from pip.index import (
PyPI, FormatControl, fmt_ctl_handle_mutual_exclude, fmt_ctl_no_use_wheel)
from pip.locations import CA_BUNDLE_PATH, USER_CACHE_DIR, src_prefix


Expand All @@ -27,6 +29,12 @@ def make_option_group(group, parser):
return option_group


def resolve_wheel_no_use_binary(options):
if not options.use_wheel:
control = options.format_control
fmt_ctl_no_use_wheel(control)


###########
# options #
###########
Expand Down Expand Up @@ -339,6 +347,7 @@ def editable():
'The default for global installs is "<current dir>/src".'
)

# XXX: deprecated, remove in 9.0
use_wheel = partial(
Option,
'--use-wheel',
Expand All @@ -354,9 +363,53 @@ def editable():
action='store_false',
default=True,
help=('Do not Find and prefer wheel archives when searching indexes and '
'find-links locations.'),
'find-links locations. DEPRECATED in favour of --no-binary.'),
)


def _get_format_control(values, option):
"""Get a format_control object."""
return getattr(values, option.dest)


def _handle_no_binary(option, opt_str, value, parser):
existing = getattr(parser.values, option.dest)
fmt_ctl_handle_mutual_exclude(
value, existing.no_binary, existing.only_binary)


def _handle_only_binary(option, opt_str, value, parser):
existing = getattr(parser.values, option.dest)
fmt_ctl_handle_mutual_exclude(
value, existing.only_binary, existing.no_binary)


def no_binary():
return Option(
"--no-binary", dest="format_control", action="callback",
callback=_handle_no_binary, type="str",
default=FormatControl(set(), set()),
help="Do not use binary packages. Can be supplied multiple times, and "
"each time adds to the existing value. Accepts either :all: to "
"disable all binary packages, :none: to empty the set, or one or "
"more package names with commas between them. Note that some "
"packages are tricky to compile and may fail to install when "
"this option is used on them.")


def only_binary():
return Option(
"--only-binary", dest="format_control", action="callback",
callback=_handle_only_binary, type="str",
default=FormatControl(set(), set()),
help="Do not use source packages. Can be supplied multiple times, and "
"each time adds to the existing value. Accepts either :all: to "
"disable all source packages, :none: to empty the set, or one or "
"more package names with commas between them. Packages without "
"binary distributions will fail to install when this option is "
"used on them.")


cache_dir = partial(
Option,
"--cache-dir",
Expand Down
4 changes: 3 additions & 1 deletion pip/commands/freeze.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import sys

import pip
from pip.basecommand import Command
from pip.operations.freeze import freeze
from pip.wheel import Cache
Expand Down Expand Up @@ -55,7 +56,8 @@ def __init__(self, *args, **kw):
self.parser.insert_option_group(0, self.cmd_opts)

def run(self, options, args):
wheel_cache = Cache(options.cache_dir)
wheel_cache = Cache(
options.cache_dir, pip.index.FormatControl(set(), set()))
freeze_kwargs = dict(
requirement=options.requirement,
find_links=options.find_links,
Expand Down
7 changes: 5 additions & 2 deletions pip/commands/install.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,8 @@ def __init__(self, *args, **kw):

cmd_opts.add_option(cmdoptions.use_wheel())
cmd_opts.add_option(cmdoptions.no_use_wheel())
cmd_opts.add_option(cmdoptions.no_binary())
cmd_opts.add_option(cmdoptions.only_binary())

cmd_opts.add_option(
'--pre',
Expand All @@ -179,8 +181,8 @@ def _build_package_finder(self, options, index_urls, session):
"""
return PackageFinder(
find_links=options.find_links,
format_control=options.format_control,
index_urls=index_urls,
use_wheel=options.use_wheel,
allow_external=options.allow_external,
allow_unverified=options.allow_unverified,
allow_all_external=options.allow_all_external,
Expand All @@ -191,6 +193,7 @@ def _build_package_finder(self, options, index_urls, session):
)

def run(self, options, args):
cmdoptions.resolve_wheel_no_use_binary(options)

if options.download_dir:
options.ignore_installed = True
Expand Down Expand Up @@ -241,7 +244,7 @@ def run(self, options, args):
build_delete = (not (options.no_clean or options.build_dir))
with BuildDirectory(options.build_dir,
delete=build_delete) as build_dir:
wheel_cache = Cache(options.cache_dir)
wheel_cache = Cache(options.cache_dir, options.format_control)
requirement_set = RequirementSet(
build_dir=build_dir,
src_dir=options.src_dir,
Expand Down
5 changes: 3 additions & 2 deletions pip/commands/list.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

from pip.basecommand import Command
from pip.exceptions import DistributionNotFound
from pip.index import PackageFinder
from pip.index import FormatControl, PackageFinder
from pip.req import InstallRequirement
from pip.utils import get_installed_distributions, dist_is_editable
from pip.wheel import Cache
Expand Down Expand Up @@ -129,7 +129,8 @@ def find_packages_latest_versions(self, options):
user_only=options.user,
include_editables=False,
)
wheel_cache = Cache(options.cache_dir)
wheel_cache = Cache(
options.cache_dir, FormatControl(set(), set()))
for dist in installed_packages:
req = InstallRequirement.from_line(
dist.key, None, isolated=options.isolated_mode,
Expand Down
4 changes: 3 additions & 1 deletion pip/commands/uninstall.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from __future__ import absolute_import

import pip
from pip.wheel import Cache
from pip.req import InstallRequirement, RequirementSet, parse_requirements
from pip.basecommand import Command
Expand Down Expand Up @@ -43,7 +44,8 @@ def __init__(self, *args, **kw):

def run(self, options, args):
with self._build_session(options) as session:
wheel_cache = Cache(options.cache_dir)
wheel_cache = Cache(
options.cache_dir, pip.index.FormatControl(set(), set()))
requirement_set = RequirementSet(
build_dir=None,
src_dir=None,
Expand Down
7 changes: 5 additions & 2 deletions pip/commands/wheel.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ def __init__(self, *args, **kw):
)
cmd_opts.add_option(cmdoptions.use_wheel())
cmd_opts.add_option(cmdoptions.no_use_wheel())
cmd_opts.add_option(cmdoptions.no_binary())
cmd_opts.add_option(cmdoptions.only_binary())
cmd_opts.add_option(
'--build-option',
dest='build_options',
Expand Down Expand Up @@ -122,6 +124,7 @@ def check_required_packages(self):

def run(self, options, args):
self.check_required_packages()
cmdoptions.resolve_wheel_no_use_binary(options)

index_urls = [options.index_url] + options.extra_index_urls
if options.no_index:
Expand All @@ -143,8 +146,8 @@ def run(self, options, args):

finder = PackageFinder(
find_links=options.find_links,
format_control=options.format_control,
index_urls=index_urls,
use_wheel=options.use_wheel,
allow_external=options.allow_external,
allow_unverified=options.allow_unverified,
allow_all_external=options.allow_all_external,
Expand All @@ -157,7 +160,7 @@ def run(self, options, args):
build_delete = (not (options.no_clean or options.build_dir))
with BuildDirectory(options.build_dir,
delete=build_delete) as build_dir:
wheel_cache = Cache(options.cache_dir)
wheel_cache = Cache(options.cache_dir, options.format_control)
requirement_set = RequirementSet(
build_dir=build_dir,
src_dir=options.src_dir,
Expand Down
Loading

0 comments on commit c889d84

Please sign in to comment.