mirror of https://github.com/pypa/pip
Merge pull request #5404 from sixninetynine/feature/platforms_for_target
Open up plat/abi/impl options to `install --target`
This commit is contained in:
commit
20127bfe98
|
@ -0,0 +1 @@
|
|||
Allows dist options (--abi, --python-version, --platform, --implementation) when installing with --target
|
|
@ -13,6 +13,7 @@ import warnings
|
|||
from functools import partial
|
||||
from optparse import SUPPRESS_HELP, Option, OptionGroup
|
||||
|
||||
from pip._internal.exceptions import CommandError
|
||||
from pip._internal.index import (
|
||||
FormatControl, fmt_ctl_handle_mutual_exclude, fmt_ctl_no_binary,
|
||||
)
|
||||
|
@ -60,6 +61,45 @@ def check_install_build_global(options, check_options=None):
|
|||
)
|
||||
|
||||
|
||||
def check_dist_restriction(options, check_target=False):
|
||||
"""Function for determining if custom platform options are allowed.
|
||||
|
||||
:param options: The OptionParser options.
|
||||
:param check_target: Whether or not to check if --target is being used.
|
||||
"""
|
||||
dist_restriction_set = any([
|
||||
options.python_version,
|
||||
options.platform,
|
||||
options.abi,
|
||||
options.implementation,
|
||||
])
|
||||
|
||||
binary_only = FormatControl(set(), {':all:'})
|
||||
sdist_dependencies_allowed = (
|
||||
options.format_control != binary_only and
|
||||
not options.ignore_dependencies
|
||||
)
|
||||
|
||||
# Installations or downloads using dist restrictions must not combine
|
||||
# source distributions and dist-specific wheels, as they are not
|
||||
# gauranteed to be locally compatible.
|
||||
if dist_restriction_set and sdist_dependencies_allowed:
|
||||
raise CommandError(
|
||||
"When restricting platform and interpreter constraints using "
|
||||
"--python-version, --platform, --abi, or --implementation, "
|
||||
"either --no-deps must be set, or --only-binary=:all: must be "
|
||||
"set and --no-binary must not be set (or must be set to "
|
||||
":none:)."
|
||||
)
|
||||
|
||||
if check_target:
|
||||
if dist_restriction_set and not options.target_dir:
|
||||
raise CommandError(
|
||||
"Can not use any platform or abi specific options unless "
|
||||
"installing via '--target'"
|
||||
)
|
||||
|
||||
|
||||
###########
|
||||
# options #
|
||||
###########
|
||||
|
@ -406,6 +446,61 @@ def only_binary():
|
|||
)
|
||||
|
||||
|
||||
platform = partial(
|
||||
Option,
|
||||
'--platform',
|
||||
dest='platform',
|
||||
metavar='platform',
|
||||
default=None,
|
||||
help=("Only use wheels compatible with <platform>. "
|
||||
"Defaults to the platform of the running system."),
|
||||
)
|
||||
|
||||
|
||||
python_version = partial(
|
||||
Option,
|
||||
'--python-version',
|
||||
dest='python_version',
|
||||
metavar='python_version',
|
||||
default=None,
|
||||
help=("Only use wheels compatible with Python "
|
||||
"interpreter version <version>. If not specified, then the "
|
||||
"current system interpreter minor version is used. A major "
|
||||
"version (e.g. '2') can be specified to match all "
|
||||
"minor revs of that major version. A minor version "
|
||||
"(e.g. '34') can also be specified."),
|
||||
)
|
||||
|
||||
|
||||
implementation = partial(
|
||||
Option,
|
||||
'--implementation',
|
||||
dest='implementation',
|
||||
metavar='implementation',
|
||||
default=None,
|
||||
help=("Only use wheels compatible with Python "
|
||||
"implementation <implementation>, e.g. 'pp', 'jy', 'cp', "
|
||||
" or 'ip'. If not specified, then the current "
|
||||
"interpreter implementation is used. Use 'py' to force "
|
||||
"implementation-agnostic wheels."),
|
||||
)
|
||||
|
||||
|
||||
abi = partial(
|
||||
Option,
|
||||
'--abi',
|
||||
dest='abi',
|
||||
metavar='abi',
|
||||
default=None,
|
||||
help=("Only use wheels compatible with Python "
|
||||
"abi <abi>, e.g. 'pypy_41'. If not specified, then the "
|
||||
"current interpreter abi tag is used. Generally "
|
||||
"you will need to specify --implementation, "
|
||||
"--platform, and --python-version when using "
|
||||
"this option."),
|
||||
)
|
||||
|
||||
|
||||
def prefer_binary():
|
||||
return Option(
|
||||
"--prefer-binary",
|
||||
|
|
|
@ -5,8 +5,6 @@ import os
|
|||
|
||||
from pip._internal.cli import cmdoptions
|
||||
from pip._internal.cli.base_command import RequirementCommand
|
||||
from pip._internal.exceptions import CommandError
|
||||
from pip._internal.index import FormatControl
|
||||
from pip._internal.operations.prepare import RequirementPreparer
|
||||
from pip._internal.req import RequirementSet
|
||||
from pip._internal.req.req_tracker import RequirementTracker
|
||||
|
@ -69,52 +67,10 @@ class DownloadCommand(RequirementCommand):
|
|||
help=("Download packages into <dir>."),
|
||||
)
|
||||
|
||||
cmd_opts.add_option(
|
||||
'--platform',
|
||||
dest='platform',
|
||||
metavar='platform',
|
||||
default=None,
|
||||
help=("Only download wheels compatible with <platform>. "
|
||||
"Defaults to the platform of the running system."),
|
||||
)
|
||||
|
||||
cmd_opts.add_option(
|
||||
'--python-version',
|
||||
dest='python_version',
|
||||
metavar='python_version',
|
||||
default=None,
|
||||
help=("Only download wheels compatible with Python "
|
||||
"interpreter version <version>. If not specified, then the "
|
||||
"current system interpreter minor version is used. A major "
|
||||
"version (e.g. '2') can be specified to match all "
|
||||
"minor revs of that major version. A minor version "
|
||||
"(e.g. '34') can also be specified."),
|
||||
)
|
||||
|
||||
cmd_opts.add_option(
|
||||
'--implementation',
|
||||
dest='implementation',
|
||||
metavar='implementation',
|
||||
default=None,
|
||||
help=("Only download wheels compatible with Python "
|
||||
"implementation <implementation>, e.g. 'pp', 'jy', 'cp', "
|
||||
" or 'ip'. If not specified, then the current "
|
||||
"interpreter implementation is used. Use 'py' to force "
|
||||
"implementation-agnostic wheels."),
|
||||
)
|
||||
|
||||
cmd_opts.add_option(
|
||||
'--abi',
|
||||
dest='abi',
|
||||
metavar='abi',
|
||||
default=None,
|
||||
help=("Only download wheels compatible with Python "
|
||||
"abi <abi>, e.g. 'pypy_41'. If not specified, then the "
|
||||
"current interpreter abi tag is used. Generally "
|
||||
"you will need to specify --implementation, "
|
||||
"--platform, and --python-version when using "
|
||||
"this option."),
|
||||
)
|
||||
cmd_opts.add_option(cmdoptions.platform())
|
||||
cmd_opts.add_option(cmdoptions.python_version())
|
||||
cmd_opts.add_option(cmdoptions.implementation())
|
||||
cmd_opts.add_option(cmdoptions.abi())
|
||||
|
||||
index_opts = cmdoptions.make_option_group(
|
||||
cmdoptions.index_group,
|
||||
|
@ -135,25 +91,7 @@ class DownloadCommand(RequirementCommand):
|
|||
else:
|
||||
python_versions = None
|
||||
|
||||
dist_restriction_set = any([
|
||||
options.python_version,
|
||||
options.platform,
|
||||
options.abi,
|
||||
options.implementation,
|
||||
])
|
||||
binary_only = FormatControl(set(), {':all:'})
|
||||
no_sdist_dependencies = (
|
||||
options.format_control != binary_only and
|
||||
not options.ignore_dependencies
|
||||
)
|
||||
if dist_restriction_set and no_sdist_dependencies:
|
||||
raise CommandError(
|
||||
"When restricting platform and interpreter constraints using "
|
||||
"--python-version, --platform, --abi, or --implementation, "
|
||||
"either --no-deps must be set, or --only-binary=:all: must be "
|
||||
"set and --no-binary must not be set (or must be set to "
|
||||
":none:)."
|
||||
)
|
||||
cmdoptions.check_dist_restriction(options)
|
||||
|
||||
options.src_dir = os.path.abspath(options.src_dir)
|
||||
options.download_dir = normalize_path(options.download_dir)
|
||||
|
|
|
@ -83,6 +83,11 @@ class InstallCommand(RequirementCommand):
|
|||
'<dir>. Use --upgrade to replace existing packages in <dir> '
|
||||
'with new versions.'
|
||||
)
|
||||
cmd_opts.add_option(cmdoptions.platform())
|
||||
cmd_opts.add_option(cmdoptions.python_version())
|
||||
cmd_opts.add_option(cmdoptions.implementation())
|
||||
cmd_opts.add_option(cmdoptions.abi())
|
||||
|
||||
cmd_opts.add_option(
|
||||
'--user',
|
||||
dest='use_user_site',
|
||||
|
@ -204,7 +209,6 @@ class InstallCommand(RequirementCommand):
|
|||
|
||||
def run(self, options, args):
|
||||
cmdoptions.check_install_build_global(options)
|
||||
|
||||
upgrade_strategy = "to-satisfy-only"
|
||||
if options.upgrade:
|
||||
upgrade_strategy = options.upgrade_strategy
|
||||
|
@ -212,6 +216,13 @@ class InstallCommand(RequirementCommand):
|
|||
if options.build_dir:
|
||||
options.build_dir = os.path.abspath(options.build_dir)
|
||||
|
||||
cmdoptions.check_dist_restriction(options, check_target=True)
|
||||
|
||||
if options.python_version:
|
||||
python_versions = [options.python_version]
|
||||
else:
|
||||
python_versions = None
|
||||
|
||||
options.src_dir = os.path.abspath(options.src_dir)
|
||||
install_options = options.install_options or []
|
||||
if options.use_user_site:
|
||||
|
@ -246,7 +257,14 @@ class InstallCommand(RequirementCommand):
|
|||
global_options = options.global_options or []
|
||||
|
||||
with self._build_session(options) as session:
|
||||
finder = self._build_package_finder(options, session)
|
||||
finder = self._build_package_finder(
|
||||
options=options,
|
||||
session=session,
|
||||
platform=options.platform,
|
||||
python_versions=python_versions,
|
||||
abi=options.abi,
|
||||
implementation=options.implementation,
|
||||
)
|
||||
build_delete = (not (options.no_clean or options.build_dir))
|
||||
wheel_cache = WheelCache(options.cache_dir, options.format_control)
|
||||
|
||||
|
@ -266,6 +284,7 @@ class InstallCommand(RequirementCommand):
|
|||
) as directory:
|
||||
requirement_set = RequirementSet(
|
||||
require_hashes=options.require_hashes,
|
||||
check_supported_wheels=not options.target_dir,
|
||||
)
|
||||
|
||||
try:
|
||||
|
|
|
@ -12,12 +12,13 @@ logger = logging.getLogger(__name__)
|
|||
|
||||
class RequirementSet(object):
|
||||
|
||||
def __init__(self, require_hashes=False):
|
||||
def __init__(self, require_hashes=False, check_supported_wheels=True):
|
||||
"""Create a RequirementSet.
|
||||
"""
|
||||
|
||||
self.requirements = OrderedDict()
|
||||
self.require_hashes = require_hashes
|
||||
self.check_supported_wheels = check_supported_wheels
|
||||
|
||||
# Mapping of alias: real_name
|
||||
self.requirement_aliases = {}
|
||||
|
@ -65,7 +66,7 @@ class RequirementSet(object):
|
|||
# environment markers.
|
||||
if install_req.link and install_req.link.is_wheel:
|
||||
wheel = Wheel(install_req.link.filename)
|
||||
if not wheel.supported():
|
||||
if self.check_supported_wheels and not wheel.supported():
|
||||
raise InstallationError(
|
||||
"%s is not a supported wheel on this platform." %
|
||||
wheel.filename
|
||||
|
|
Binary file not shown.
|
@ -8,7 +8,7 @@ from os.path import curdir, join, pardir
|
|||
import pytest
|
||||
|
||||
from pip._internal import pep425tags
|
||||
from pip._internal.cli.status_codes import ERROR
|
||||
from pip._internal.cli.status_codes import ERROR, SUCCESS
|
||||
from pip._internal.models.index import PyPI, TestPyPI
|
||||
from pip._internal.utils.misc import rmtree
|
||||
from tests.lib import (
|
||||
|
@ -679,6 +679,66 @@ def test_install_package_with_target(script):
|
|||
assert singlemodule_py in result.files_updated, str(result)
|
||||
|
||||
|
||||
def test_install_nonlocal_compatible_wheel(script, data):
|
||||
target_dir = script.scratch_path / 'target'
|
||||
|
||||
# Test install with --target
|
||||
result = script.pip(
|
||||
'install',
|
||||
'-t', target_dir,
|
||||
'--no-index', '--find-links', data.find_links,
|
||||
'--only-binary=:all:',
|
||||
'--python', '3',
|
||||
'--platform', 'fakeplat',
|
||||
'--abi', 'fakeabi',
|
||||
'simplewheel',
|
||||
)
|
||||
assert result.returncode == SUCCESS
|
||||
|
||||
distinfo = Path('scratch') / 'target' / 'simplewheel-2.0-1.dist-info'
|
||||
assert distinfo in result.files_created
|
||||
|
||||
# Test install without --target
|
||||
result = script.pip(
|
||||
'install',
|
||||
'--no-index', '--find-links', data.find_links,
|
||||
'--only-binary=:all:',
|
||||
'--python', '3',
|
||||
'--platform', 'fakeplat',
|
||||
'--abi', 'fakeabi',
|
||||
'simplewheel',
|
||||
expect_error=True
|
||||
)
|
||||
assert result.returncode == ERROR
|
||||
|
||||
|
||||
def test_install_nonlocal_compatible_wheel_path(script, data):
|
||||
target_dir = script.scratch_path / 'target'
|
||||
|
||||
# Test a full path requirement
|
||||
result = script.pip(
|
||||
'install',
|
||||
'-t', target_dir,
|
||||
'--no-index',
|
||||
'--only-binary=:all:',
|
||||
Path(data.packages) / 'simplewheel-2.0-py3-fakeabi-fakeplat.whl'
|
||||
)
|
||||
assert result.returncode == SUCCESS
|
||||
|
||||
distinfo = Path('scratch') / 'target' / 'simplewheel-2.0.dist-info'
|
||||
assert distinfo in result.files_created
|
||||
|
||||
# Test a full path requirement (without --target)
|
||||
result = script.pip(
|
||||
'install',
|
||||
'--no-index',
|
||||
'--only-binary=:all:',
|
||||
Path(data.packages) / 'simplewheel-2.0-py3-fakeabi-fakeplat.whl',
|
||||
expect_error=True
|
||||
)
|
||||
assert result.returncode == ERROR
|
||||
|
||||
|
||||
def test_install_with_target_and_scripts_no_warning(script, common_wheels):
|
||||
"""
|
||||
Test that installing with --target does not trigger the "script not
|
||||
|
|
Loading…
Reference in New Issue