Merge pull request #8820 from katzdm/multi-abis-platforms

This commit is contained in:
Pradyun Gedam 2020-10-28 13:51:20 +05:30 committed by GitHub
commit c4dbc7d814
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 136 additions and 60 deletions

View File

@ -197,3 +197,32 @@ Examples
C:\> dir 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.rst Normal file
View File

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

View File

@ -97,8 +97,8 @@ def check_dist_restriction(options, check_target=False):
"""
dist_restriction_set = any([
options.python_version,
options.platform,
options.abi,
options.platforms,
options.abis,
options.implementation,
])
@ -490,14 +490,16 @@ def only_binary():
)
platform = partial(
platforms = partial(
Option,
'--platform',
dest='platform',
dest='platforms',
metavar='platform',
action='append',
default=None,
help=("Only use wheels compatible with <platform>. "
"Defaults to the platform of the running system."),
help=("Only use wheels compatible with <platform>. Defaults to the "
"platform of the running system. Use this option multiple times to "
"specify multiple platforms supported by the target interpreter."),
) # type: Callable[..., Option]
@ -581,35 +583,36 @@ implementation = partial(
) # type: Callable[..., Option]
abi = partial(
abis = partial(
Option,
'--abi',
dest='abi',
dest='abis',
metavar='abi',
action='append',
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."),
help=("Only use wheels compatible with Python abi <abi>, e.g. 'pypy_41'. "
"If not specified, then the current interpreter abi tag is used. "
"Use this option multiple times to specify multiple abis supported "
"by the target interpreter. Generally you will need to specify "
"--implementation, --platform, and --python-version when using this "
"option."),
) # type: Callable[..., Option]
def add_target_python_options(cmd_opts):
# type: (OptionGroup) -> None
cmd_opts.add_option(platform())
cmd_opts.add_option(platforms())
cmd_opts.add_option(python_version())
cmd_opts.add_option(implementation())
cmd_opts.add_option(abi())
cmd_opts.add_option(abis())
def make_target_python(options):
# type: (Values) -> TargetPython
target_python = TargetPython(
platform=options.platform,
platforms=options.platforms,
py_version_info=options.python_version,
abi=options.abi,
abis=options.abis,
implementation=options.implementation,
)

View File

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

View File

@ -86,6 +86,24 @@ def _get_custom_platforms(arch):
return arches
def _expand_allowed_platforms(platforms):
# type: (Optional[List[str]]) -> Optional[List[str]]
if not platforms:
return None
seen = set()
result = []
for p in platforms:
if p in seen:
continue
additions = [c for c in _get_custom_platforms(p) if c not in seen]
seen.update(additions)
result.extend(additions)
return result
def _get_python_version(version):
# type: (str) -> PythonVersion
if len(version) > 1:
@ -105,9 +123,9 @@ def _get_custom_interpreter(implementation=None, version=None):
def get_supported(
version=None, # type: Optional[str]
platform=None, # type: Optional[str]
platforms=None, # type: Optional[List[str]]
impl=None, # type: Optional[str]
abi=None # type: Optional[str]
abis=None # type: Optional[List[str]]
):
# type: (...) -> List[Tag]
"""Return a list of supported tags for each version specified in
@ -115,11 +133,11 @@ def get_supported(
:param version: a string version, of the form "33" or "32",
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.
:param impl: specify the exact implementation you want valid
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.
"""
supported = [] # type: List[Tag]
@ -130,13 +148,7 @@ def get_supported(
interpreter = _get_custom_interpreter(impl, version)
abis = None # type: Optional[List[str]]
if abi is not None:
abis = [abi]
platforms = None # type: Optional[List[str]]
if platform is not None:
platforms = _get_custom_platforms(platform)
platforms = _expand_allowed_platforms(platforms)
is_cpython = (impl or interpreter_name()) == "cp"
if is_cpython:

View File

@ -309,6 +309,21 @@ def test_download_specify_platform(script, data):
Path('scratch') / 'fake-2.0-py2.py3-none-linux_x86_64.whl'
)
# Test with multiple supported platforms specified.
data.reset()
fake_wheel(data, 'fake-3.0-py2.py3-none-linux_x86_64.whl')
result = script.pip(
'download', '--no-index', '--find-links', data.find_links,
'--only-binary=:all:',
'--dest', '.',
'--platform', 'manylinux1_x86_64', '--platform', 'linux_x86_64',
'--platform', 'any',
'fake==3'
)
result.did_create(
Path('scratch') / 'fake-3.0-py2.py3-none-linux_x86_64.whl'
)
class TestDownloadPlatformManylinuxes(object):
"""
@ -575,6 +590,22 @@ def test_download_specify_abi(script, data):
expect_error=True,
)
data.reset()
fake_wheel(data, 'fake-1.0-fk2-otherabi-fake_platform.whl')
result = script.pip(
'download', '--no-index', '--find-links', data.find_links,
'--only-binary=:all:',
'--dest', '.',
'--python-version', '2',
'--implementation', 'fk',
'--platform', 'fake_platform',
'--abi', 'fakeabi', '--abi', 'otherabi', '--abi', 'none',
'fake'
)
result.did_create(
Path('scratch') / 'fake-1.0-fk2-otherabi-fake_platform.whl'
)
def test_download_specify_implementation(script, data):
"""

View File

@ -76,7 +76,7 @@ class TestWheelFile(object):
Wheels built for macOS 10.6 are supported on 10.9
"""
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')
assert w.supported(tags=tags)
@ -88,7 +88,7 @@ class TestWheelFile(object):
Wheels built for macOS 10.9 are not supported on 10.6
"""
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')
assert not w.supported(tags=tags)
@ -98,22 +98,22 @@ class TestWheelFile(object):
Multi-arch wheels (intel) are supported on components (i386, x86_64)
"""
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(
'27', platform='macosx_10_5_intel', impl='cp'
'27', platforms=['macosx_10_5_intel'], impl='cp'
)
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(
'27', platform='macosx_10_5_i386', impl='cp'
'27', platforms=['macosx_10_5_i386'], impl='cp'
)
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(
'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')
@ -136,10 +136,10 @@ class TestWheelFile(object):
Single-arch wheels (x86_64) are not supported on multi-arch (intel)
"""
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(
'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')

View File

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

View File

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