mirror of https://github.com/pypa/pip
Merge pull request #8820 from katzdm/multi-abis-platforms
This commit is contained in:
commit
c4dbc7d814
|
@ -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
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
Allow multiple values for --abi and --platform.
|
|
@ -97,8 +97,8 @@ def check_dist_restriction(options, check_target=False):
|
||||||
"""
|
"""
|
||||||
dist_restriction_set = any([
|
dist_restriction_set = any([
|
||||||
options.python_version,
|
options.python_version,
|
||||||
options.platform,
|
options.platforms,
|
||||||
options.abi,
|
options.abis,
|
||||||
options.implementation,
|
options.implementation,
|
||||||
])
|
])
|
||||||
|
|
||||||
|
@ -490,14 +490,16 @@ def only_binary():
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
platform = partial(
|
platforms = partial(
|
||||||
Option,
|
Option,
|
||||||
'--platform',
|
'--platform',
|
||||||
dest='platform',
|
dest='platforms',
|
||||||
metavar='platform',
|
metavar='platform',
|
||||||
|
action='append',
|
||||||
default=None,
|
default=None,
|
||||||
help=("Only use wheels compatible with <platform>. "
|
help=("Only use wheels compatible with <platform>. Defaults to the "
|
||||||
"Defaults to the platform of the running system."),
|
"platform of the running system. Use this option multiple times to "
|
||||||
|
"specify multiple platforms supported by the target interpreter."),
|
||||||
) # type: Callable[..., Option]
|
) # type: Callable[..., Option]
|
||||||
|
|
||||||
|
|
||||||
|
@ -581,35 +583,36 @@ implementation = partial(
|
||||||
) # type: Callable[..., Option]
|
) # type: Callable[..., Option]
|
||||||
|
|
||||||
|
|
||||||
abi = partial(
|
abis = partial(
|
||||||
Option,
|
Option,
|
||||||
'--abi',
|
'--abi',
|
||||||
dest='abi',
|
dest='abis',
|
||||||
metavar='abi',
|
metavar='abi',
|
||||||
|
action='append',
|
||||||
default=None,
|
default=None,
|
||||||
help=("Only use wheels compatible with Python "
|
help=("Only use wheels compatible with Python abi <abi>, e.g. 'pypy_41'. "
|
||||||
"abi <abi>, e.g. 'pypy_41'. If not specified, then the "
|
"If not specified, then the current interpreter abi tag is used. "
|
||||||
"current interpreter abi tag is used. Generally "
|
"Use this option multiple times to specify multiple abis supported "
|
||||||
"you will need to specify --implementation, "
|
"by the target interpreter. Generally you will need to specify "
|
||||||
"--platform, and --python-version when using "
|
"--implementation, --platform, and --python-version when using this "
|
||||||
"this option."),
|
"option."),
|
||||||
) # type: Callable[..., Option]
|
) # type: Callable[..., Option]
|
||||||
|
|
||||||
|
|
||||||
def add_target_python_options(cmd_opts):
|
def add_target_python_options(cmd_opts):
|
||||||
# type: (OptionGroup) -> None
|
# type: (OptionGroup) -> None
|
||||||
cmd_opts.add_option(platform())
|
cmd_opts.add_option(platforms())
|
||||||
cmd_opts.add_option(python_version())
|
cmd_opts.add_option(python_version())
|
||||||
cmd_opts.add_option(implementation())
|
cmd_opts.add_option(implementation())
|
||||||
cmd_opts.add_option(abi())
|
cmd_opts.add_option(abis())
|
||||||
|
|
||||||
|
|
||||||
def make_target_python(options):
|
def make_target_python(options):
|
||||||
# type: (Values) -> TargetPython
|
# type: (Values) -> TargetPython
|
||||||
target_python = TargetPython(
|
target_python = TargetPython(
|
||||||
platform=options.platform,
|
platforms=options.platforms,
|
||||||
py_version_info=options.python_version,
|
py_version_info=options.python_version,
|
||||||
abi=options.abi,
|
abis=options.abis,
|
||||||
implementation=options.implementation,
|
implementation=options.implementation,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -86,6 +86,24 @@ def _get_custom_platforms(arch):
|
||||||
return arches
|
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):
|
def _get_python_version(version):
|
||||||
# type: (str) -> PythonVersion
|
# type: (str) -> PythonVersion
|
||||||
if len(version) > 1:
|
if len(version) > 1:
|
||||||
|
@ -105,9 +123,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 +133,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 +148,7 @@ def get_supported(
|
||||||
|
|
||||||
interpreter = _get_custom_interpreter(impl, version)
|
interpreter = _get_custom_interpreter(impl, version)
|
||||||
|
|
||||||
abis = None # type: Optional[List[str]]
|
platforms = _expand_allowed_platforms(platforms)
|
||||||
if abi is not None:
|
|
||||||
abis = [abi]
|
|
||||||
|
|
||||||
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:
|
||||||
|
|
|
@ -309,6 +309,21 @@ def test_download_specify_platform(script, data):
|
||||||
Path('scratch') / 'fake-2.0-py2.py3-none-linux_x86_64.whl'
|
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):
|
class TestDownloadPlatformManylinuxes(object):
|
||||||
"""
|
"""
|
||||||
|
@ -575,6 +590,22 @@ def test_download_specify_abi(script, data):
|
||||||
expect_error=True,
|
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):
|
def test_download_specify_implementation(script, data):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -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')
|
||||||
|
|
|
@ -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'"
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
@ -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), []
|
||||||
|
|
Loading…
Reference in New Issue