1
1
Fork 0
mirror of https://github.com/pypa/pip synced 2023-12-13 21:30:23 +01:00

Support multiple abi and platform values for pip download.

This commit is contained in:
Daniel Katz 2020-08-28 12:55:17 -04:00
parent a0ec4be98b
commit cea9f32dae
8 changed files with 84 additions and 46 deletions

View file

@ -197,3 +197,32 @@ Examples
C:\> dir pip-8.1.1-py2.py3-none-any.whl C:\> dir pip-8.1.1-py2.py3-none-any.whl
pip-8.1.1-py2.py3-none-any.whl pip-8.1.1-py2.py3-none-any.whl
#. Download a package supporting one of several ABIs and platforms.
This is useful when fetching wheels for a well-defined interpreter, whose
supported ABIs and platforms are known and fixed, different than the one pip is
running under.
.. tab:: Unix/macOS
.. code-block:: console
$ python -m pip download \
--only-binary=:all: \
--platform manylinux1_x86_64 --platform linux_x86_64 --platform any \
--python-version 36 \
--implementation cp \
--abi cp36m --abi cp36 --abi abi3 --abi none \
SomePackage
.. tab:: Windows
.. code-block:: console
C:> py -m pip download ^
--only-binary=:all: ^
--platform manylinux1_x86_64 --platform linux_x86_64 --platform any ^
--python-version 36 ^
--implementation cp ^
--abi cp36m --abi cp36 --abi abi3 --abi none ^
SomePackage

1
news/6121.feature Normal file
View file

@ -0,0 +1 @@
Allow comma-separated values for --abi and --platform.

View file

@ -31,7 +31,7 @@ from pip._internal.utils.typing import MYPY_CHECK_RUNNING
if MYPY_CHECK_RUNNING: if MYPY_CHECK_RUNNING:
from optparse import OptionParser, Values from optparse import OptionParser, Values
from typing import Any, Callable, Dict, Optional, Tuple from typing import Any, Callable, Dict, List, Optional, Tuple
from pip._internal.cli.parser import ConfigOptionParser from pip._internal.cli.parser import ConfigOptionParser
@ -588,7 +588,7 @@ abi = partial(
metavar='abi', metavar='abi',
default=None, default=None,
help=("Only use wheels compatible with Python " help=("Only use wheels compatible with Python "
"abi <abi>, e.g. 'pypy_41'. If not specified, then the " "abi <abi>, e.g. 'pypy_41,none'. If not specified, then the "
"current interpreter abi tag is used. Generally " "current interpreter abi tag is used. Generally "
"you will need to specify --implementation, " "you will need to specify --implementation, "
"--platform, and --python-version when using " "--platform, and --python-version when using "
@ -606,10 +606,21 @@ def add_target_python_options(cmd_opts):
def make_target_python(options): def make_target_python(options):
# type: (Values) -> TargetPython # type: (Values) -> TargetPython
# abi can be a comma-separated list of values.
abis = options.abi # type: Optional[List[str]]
if options.abi:
abis = options.abi.split(',')
# platform can also be a comma-separated list of values.
platforms = options.platform # type: Optional[List[str]]
if options.platform:
platforms = options.platform.split(',')
target_python = TargetPython( target_python = TargetPython(
platform=options.platform, platforms=platforms,
py_version_info=options.python_version, py_version_info=options.python_version,
abi=options.abi, abis=abis,
implementation=options.implementation, implementation=options.implementation,
) )

View file

@ -19,9 +19,9 @@ class TargetPython(object):
__slots__ = [ __slots__ = [
"_given_py_version_info", "_given_py_version_info",
"abi", "abis",
"implementation", "implementation",
"platform", "platforms",
"py_version", "py_version",
"py_version_info", "py_version_info",
"_valid_tags", "_valid_tags",
@ -29,23 +29,23 @@ class TargetPython(object):
def __init__( def __init__(
self, self,
platform=None, # type: Optional[str] platforms=None, # type: Optional[List[str]]
py_version_info=None, # type: Optional[Tuple[int, ...]] py_version_info=None, # type: Optional[Tuple[int, ...]]
abi=None, # type: Optional[str] abis=None, # type: Optional[List[str]]
implementation=None, # type: Optional[str] implementation=None, # type: Optional[str]
): ):
# type: (...) -> None # type: (...) -> None
""" """
:param platform: A string or None. If None, searches for packages :param platforms: A list of strings or None. If None, searches for
that are supported by the current system. Otherwise, will find packages that are supported by the current system. Otherwise, will
packages that can be built on the platform passed in. These find packages that can be built on the platforms passed in. These
packages will only be downloaded for distribution: they will packages will only be downloaded for distribution: they will
not be built locally. not be built locally.
:param py_version_info: An optional tuple of ints representing the :param py_version_info: An optional tuple of ints representing the
Python version information to use (e.g. `sys.version_info[:3]`). Python version information to use (e.g. `sys.version_info[:3]`).
This can have length 1, 2, or 3 when provided. This can have length 1, 2, or 3 when provided.
:param abi: A string or None. This is passed to compatibility_tags.py's :param abis: A list of strings or None. This is passed to
get_supported() function as is. compatibility_tags.py's get_supported() function as is.
:param implementation: A string or None. This is passed to :param implementation: A string or None. This is passed to
compatibility_tags.py's get_supported() function as is. compatibility_tags.py's get_supported() function as is.
""" """
@ -59,9 +59,9 @@ class TargetPython(object):
py_version = '.'.join(map(str, py_version_info[:2])) py_version = '.'.join(map(str, py_version_info[:2]))
self.abi = abi self.abis = abis
self.implementation = implementation self.implementation = implementation
self.platform = platform self.platforms = platforms
self.py_version = py_version self.py_version = py_version
self.py_version_info = py_version_info self.py_version_info = py_version_info
@ -80,9 +80,9 @@ class TargetPython(object):
) )
key_values = [ key_values = [
('platform', self.platform), ('platforms', self.platforms),
('version_info', display_version), ('version_info', display_version),
('abi', self.abi), ('abis', self.abis),
('implementation', self.implementation), ('implementation', self.implementation),
] ]
return ' '.join( return ' '.join(
@ -108,8 +108,8 @@ class TargetPython(object):
tags = get_supported( tags = get_supported(
version=version, version=version,
platform=self.platform, platforms=self.platforms,
abi=self.abi, abis=self.abis,
impl=self.implementation, impl=self.implementation,
) )
self._valid_tags = tags self._valid_tags = tags

View file

@ -105,9 +105,9 @@ def _get_custom_interpreter(implementation=None, version=None):
def get_supported( def get_supported(
version=None, # type: Optional[str] version=None, # type: Optional[str]
platform=None, # type: Optional[str] platforms=None, # type: Optional[List[str]]
impl=None, # type: Optional[str] impl=None, # type: Optional[str]
abi=None # type: Optional[str] abis=None # type: Optional[List[str]]
): ):
# type: (...) -> List[Tag] # type: (...) -> List[Tag]
"""Return a list of supported tags for each version specified in """Return a list of supported tags for each version specified in
@ -115,11 +115,11 @@ def get_supported(
:param version: a string version, of the form "33" or "32", :param version: a string version, of the form "33" or "32",
or None. The version will be assumed to support our ABI. or None. The version will be assumed to support our ABI.
:param platform: specify the exact platform you want valid :param platform: specify a list of platforms you want valid
tags for, or None. If None, use the local system platform. tags for, or None. If None, use the local system platform.
:param impl: specify the exact implementation you want valid :param impl: specify the exact implementation you want valid
tags for, or None. If None, use the local interpreter impl. tags for, or None. If None, use the local interpreter impl.
:param abi: specify the exact abi you want valid :param abis: specify a list of abis you want valid
tags for, or None. If None, use the local interpreter abi. tags for, or None. If None, use the local interpreter abi.
""" """
supported = [] # type: List[Tag] supported = [] # type: List[Tag]
@ -130,13 +130,10 @@ def get_supported(
interpreter = _get_custom_interpreter(impl, version) interpreter = _get_custom_interpreter(impl, version)
abis = None # type: Optional[List[str]] if platforms and len(platforms) == 1:
if abi is not None: # Only expand list of platforms if a single platform was provided.
abis = [abi] # Otherwise, assume that the list provided is comprehensive.
platforms = _get_custom_platforms(platforms[0])
platforms = None # type: Optional[List[str]]
if platform is not None:
platforms = _get_custom_platforms(platform)
is_cpython = (impl or interpreter_name()) == "cp" is_cpython = (impl or interpreter_name()) == "cp"
if is_cpython: if is_cpython:

View file

@ -76,7 +76,7 @@ class TestWheelFile(object):
Wheels built for macOS 10.6 are supported on 10.9 Wheels built for macOS 10.6 are supported on 10.9
""" """
tags = compatibility_tags.get_supported( tags = compatibility_tags.get_supported(
'27', platform='macosx_10_9_intel', impl='cp' '27', platforms=['macosx_10_9_intel'], impl='cp'
) )
w = Wheel('simple-0.1-cp27-none-macosx_10_6_intel.whl') w = Wheel('simple-0.1-cp27-none-macosx_10_6_intel.whl')
assert w.supported(tags=tags) assert w.supported(tags=tags)
@ -88,7 +88,7 @@ class TestWheelFile(object):
Wheels built for macOS 10.9 are not supported on 10.6 Wheels built for macOS 10.9 are not supported on 10.6
""" """
tags = compatibility_tags.get_supported( tags = compatibility_tags.get_supported(
'27', platform='macosx_10_6_intel', impl='cp' '27', platforms=['macosx_10_6_intel'], impl='cp'
) )
w = Wheel('simple-0.1-cp27-none-macosx_10_9_intel.whl') w = Wheel('simple-0.1-cp27-none-macosx_10_9_intel.whl')
assert not w.supported(tags=tags) assert not w.supported(tags=tags)
@ -98,22 +98,22 @@ class TestWheelFile(object):
Multi-arch wheels (intel) are supported on components (i386, x86_64) Multi-arch wheels (intel) are supported on components (i386, x86_64)
""" """
universal = compatibility_tags.get_supported( universal = compatibility_tags.get_supported(
'27', platform='macosx_10_5_universal', impl='cp' '27', platforms=['macosx_10_5_universal'], impl='cp'
) )
intel = compatibility_tags.get_supported( intel = compatibility_tags.get_supported(
'27', platform='macosx_10_5_intel', impl='cp' '27', platforms=['macosx_10_5_intel'], impl='cp'
) )
x64 = compatibility_tags.get_supported( x64 = compatibility_tags.get_supported(
'27', platform='macosx_10_5_x86_64', impl='cp' '27', platforms=['macosx_10_5_x86_64'], impl='cp'
) )
i386 = compatibility_tags.get_supported( i386 = compatibility_tags.get_supported(
'27', platform='macosx_10_5_i386', impl='cp' '27', platforms=['macosx_10_5_i386'], impl='cp'
) )
ppc = compatibility_tags.get_supported( ppc = compatibility_tags.get_supported(
'27', platform='macosx_10_5_ppc', impl='cp' '27', platforms=['macosx_10_5_ppc'], impl='cp'
) )
ppc64 = compatibility_tags.get_supported( ppc64 = compatibility_tags.get_supported(
'27', platform='macosx_10_5_ppc64', impl='cp' '27', platforms=['macosx_10_5_ppc64'], impl='cp'
) )
w = Wheel('simple-0.1-cp27-none-macosx_10_5_intel.whl') w = Wheel('simple-0.1-cp27-none-macosx_10_5_intel.whl')
@ -136,10 +136,10 @@ class TestWheelFile(object):
Single-arch wheels (x86_64) are not supported on multi-arch (intel) Single-arch wheels (x86_64) are not supported on multi-arch (intel)
""" """
universal = compatibility_tags.get_supported( universal = compatibility_tags.get_supported(
'27', platform='macosx_10_5_universal', impl='cp' '27', platforms=['macosx_10_5_universal'], impl='cp'
) )
intel = compatibility_tags.get_supported( intel = compatibility_tags.get_supported(
'27', platform='macosx_10_5_intel', impl='cp' '27', platforms=['macosx_10_5_intel'], impl='cp'
) )
w = Wheel('simple-0.1-cp27-none-macosx_10_5_i386.whl') w = Wheel('simple-0.1-cp27-none-macosx_10_5_i386.whl')

View file

@ -45,16 +45,16 @@ class TestTargetPython:
({}, ''), ({}, ''),
(dict(py_version_info=(3, 6)), "version_info='3.6'"), (dict(py_version_info=(3, 6)), "version_info='3.6'"),
( (
dict(platform='darwin', py_version_info=(3, 6)), dict(platforms=['darwin'], py_version_info=(3, 6)),
"platform='darwin' version_info='3.6'", "platforms=['darwin'] version_info='3.6'",
), ),
( (
dict( dict(
platform='darwin', py_version_info=(3, 6), abi='cp36m', platforms=['darwin'], py_version_info=(3, 6), abis=['cp36m'],
implementation='cp' implementation='cp'
), ),
( (
"platform='darwin' version_info='3.6' abi='cp36m' " "platforms=['darwin'] version_info='3.6' abis=['cp36m'] "
"implementation='cp'" "implementation='cp'"
), ),
), ),

View file

@ -63,7 +63,7 @@ class TestManylinux2010Tags(object):
Specifying manylinux2010 implies manylinux1. Specifying manylinux2010 implies manylinux1.
""" """
groups = {} groups = {}
supported = compatibility_tags.get_supported(platform=manylinux2010) supported = compatibility_tags.get_supported(platforms=[manylinux2010])
for tag in supported: for tag in supported:
groups.setdefault( groups.setdefault(
(tag.interpreter, tag.abi), [] (tag.interpreter, tag.abi), []
@ -87,7 +87,7 @@ class TestManylinux2014Tags(object):
Specifying manylinux2014 implies manylinux2010/manylinux1. Specifying manylinux2014 implies manylinux2010/manylinux1.
""" """
groups = {} groups = {}
supported = compatibility_tags.get_supported(platform=manylinuxA) supported = compatibility_tags.get_supported(platforms=[manylinuxA])
for tag in supported: for tag in supported:
groups.setdefault( groups.setdefault(
(tag.interpreter, tag.abi), [] (tag.interpreter, tag.abi), []