Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Restore legacy PyPy wheel interpreter tags #7655

Closed
Closed
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions news/7629.bugfix
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Restore compatibility with legacy non-CPython interpreter wheel tags that use
the interpreter implementation version in the filename instead of the nominal
Python language version (for example, the expected PyPy-specific interpreter tag
is ``pp35`` for versions of PyPy that target Python 3.5 compatibility, but older
versions of pip instead looked for tags like ``pp372``, incorporating the
first two digits of the implementation version).
27 changes: 26 additions & 1 deletion src/pip/_internal/pep425tags.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,31 @@ def _get_custom_interpreter(implementation=None, version=None):
return "{}{}".format(implementation, version)


def _with_legacy_tags(standard_tags, interpreter, impl):
# type: (List[Tag], str, Optional[str]) -> List[Tag]
"""For backwards compatibilty, add legacy tags that pip used to accept

Note: returns a reference to the given list if no changes are needed,
but returns a new list otherwise.
"""
all_supported_tags = standard_tags # Default to not making any changes
ncoghlan marked this conversation as resolved.
Show resolved Hide resolved

# pip used to calculate incorrect implementation tags for alternate
# implementations like PyPy, appending part of the implementation version,
# rather than using the nominal Python language version
legacy_interpreter = _get_custom_interpreter(impl)
if interpreter != legacy_interpreter:
# Build a new list with extra tags inserted
all_supported_tags = []
for tag in standard_tags:
all_supported_tags.append(tag)
if tag.interpreter == interpreter:
legacy_tag = Tag(legacy_interpreter, tag.abi, tag.platform)
all_supported_tags.append(legacy_tag)

return all_supported_tags


def get_supported(
version=None, # type: Optional[str]
platform=None, # type: Optional[str]
Expand Down Expand Up @@ -164,4 +189,4 @@ def get_supported(
)
)

return supported
return _with_legacy_tags(supported, interpreter, impl)
19 changes: 19 additions & 0 deletions tests/functional/test_download.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@

import pytest

from pip._vendor.packaging.tags import interpreter_name

from pip._internal.cli.status_codes import ERROR
from pip._internal.utils.urls import path_to_url
from tests.lib import create_really_basic_wheel
Expand Down Expand Up @@ -61,6 +63,23 @@ def test_download_wheel(script, data):
assert script.site_packages / 'piptestpackage' not in result.files_created


def test_download_platform_specific_wheel(script, data):
"""
Test using "pip download" to download a platform specific *.whl archive
"""
assert False, "Test TBD"


@pytest.mark.skipif(
interpreter_name() == 'pp',
reason="Only test PyPy custom wheel logic on PyPy")
def test_download_pypy_version_specific_wheel(script, data):
"""
Test using "pip download" to download a PyPy version specific *.whl archive
"""
assert False, "Test TBD"


@pytest.mark.network
def test_single_download_from_requirements_file(script):
"""
Expand Down
55 changes: 55 additions & 0 deletions tests/unit/test_pep425tags.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import pytest
from mock import patch
from pip._vendor.packaging.tags import Tag

from pip._internal import pep425tags

Expand Down Expand Up @@ -99,3 +100,57 @@ def test_manylinuxA_implies_manylinuxB(self, manylinuxA, manylinuxB):
if arches == ['any']:
continue
assert arches[:3] == expected_arches


class TestLegacyTags(object):

def test_extra_tags_for_interpreter_version_mismatch(self):
# For backwards compatibility with legacy PyPy wheel tags, each
# occurrence of "ppXY" (where "XY" is taken from the language version)
# as an interpreter tag gets supplemented with another tag, "ppABC",
# where "ABC" is taken from the interpreter implementation version
# This is done in a generic way, as it potentially affects all
# non-CPython implementations
impl = "ex" # Example implementation for test purposes
legacy_interpreter = pep425tags._get_custom_interpreter(impl)

version = legacy_interpreter[2:] + "0" # Force version mismatch
interpreter = impl + version
platform = "example_platform"
abi = "example_abi"
supported = pep425tags.get_supported(version, platform, impl, abi)

unique_tags = set(supported)
assert len(unique_tags) == len(supported)

# Check every standard interpreter tag is followed by a legacy one
interpreter_tags = 0
legacy_interpreter_tags = 0
expected_tag = None
for tag in supported:
if expected_tag is not None:
assert tag == expected_tag
expected_tag = None
if tag.interpreter == interpreter:
interpreter_tags += 1
expected_tag = Tag(legacy_interpreter, tag.abi, tag.platform)
elif tag.interpreter == legacy_interpreter:
legacy_interpreter_tags += 1

# Check the total numbers of interpreter tags match as expected
assert interpreter_tags
assert interpreter_tags == legacy_interpreter_tags

def test_no_extra_tags_when_interpreter_version_matches(self):
# When the language version and the interpreter version are the same,
# duplicate tags should not be generated
impl = "ex" # Example implementation for test purposes
interpreter = pep425tags._get_custom_interpreter(impl)
ncoghlan marked this conversation as resolved.
Show resolved Hide resolved

version = interpreter[2:] # Ensure version arg matches default tag
platform = "example_platform"
abi = "example_abi"
supported = pep425tags.get_supported(version, platform, impl, abi)
ncoghlan marked this conversation as resolved.
Show resolved Hide resolved

unique_tags = set(supported)
assert len(unique_tags) == len(supported)