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

Respect platform's wheels for dependencies (Fixes #558) #571

Merged
Merged
Show file tree
Hide file tree
Changes from all 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
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
# 1.10.2 (UNRELEASED)

Bug Fixes:
- Fixed bug causing dependencies from invalid wheels for the current platform to be included ([#571](https://github.com/jazzband/pip-tools/pull/571)).

# 1.10.1 (2017-09-27)

Bug Fixes:
Expand Down
8 changes: 8 additions & 0 deletions piptools/repositories/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
unicode_literals)

from abc import ABCMeta, abstractmethod
from contextlib import contextmanager

from six import add_metaclass

Expand Down Expand Up @@ -38,3 +39,10 @@ def get_hashes(self, ireq):
all of the files for a given requirement. It is not acceptable for an
editable or unpinned requirement to be passed to this function.
"""

@abstractmethod
@contextmanager
def allow_all_wheels(self):
"""
Monkey patches pip.Wheel to allow wheels from all platforms and Python versions.
"""
7 changes: 7 additions & 0 deletions piptools/repositories/local.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
from __future__ import (absolute_import, division, print_function,
unicode_literals)

from contextlib import contextmanager

from piptools.utils import as_tuple, key_from_req, make_install_requirement
from .base import BaseRepository

Expand Down Expand Up @@ -63,3 +65,8 @@ def get_dependencies(self, ireq):

def get_hashes(self, ireq):
return self.repository.get_hashes(ireq)

@contextmanager
def allow_all_wheels(self):
with self.repository.allow_all_wheels():
yield
51 changes: 32 additions & 19 deletions piptools/repositories/pypi.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,24 +28,6 @@
from .._compat import TemporaryDirectory


# Monkey patch pip's Wheel class to support all platform tags. This allows
# pip-tools to generate hashes for all available distributions, not only the
# one for the current platform.

def _wheel_supported(self, tags=None):
# Ignore current platform. Support everything.
return True


def _wheel_support_index_min(self, tags=None):
# All wheels are equal priority for sorting.
return 0


Wheel.supported = _wheel_supported
Wheel.support_index_min = _wheel_support_index_min


class PyPIRepository(BaseRepository):
DEFAULT_INDEX_URL = 'https://pypi.python.org/simple'

Expand Down Expand Up @@ -182,7 +164,7 @@ def get_hashes(self, ireq):

# We need to get all of the candidates that match our current version
# pin, these will represent all of the files that could possibly
# satisify this constraint.
# satisfy this constraint.
all_candidates = self.find_all_candidates(ireq.name)
candidates_by_version = lookup_table(all_candidates, key=lambda c: c.version)
matching_versions = list(
Expand All @@ -201,6 +183,37 @@ def _get_file_hash(self, location):
h.update(chunk)
return ":".join([FAVORITE_HASH, h.hexdigest()])

@contextmanager
def allow_all_wheels(self):
"""
Monkey patches pip.Wheel to allow wheels from all platforms and Python versions.

This also saves the candidate cache and set a new one, or else the results from the
previous non-patched calls will interfere.
"""
def _wheel_supported(self, tags=None):
# Ignore current platform. Support everything.
return True

def _wheel_support_index_min(self, tags=None):
# All wheels are equal priority for sorting.
return 0

original_wheel_supported = Wheel.supported
original_support_index_min = Wheel.support_index_min
original_cache = self._available_candidates_cache

Wheel.supported = _wheel_supported
Wheel.support_index_min = _wheel_support_index_min
self._available_candidates_cache = {}

try:
yield
finally:
Wheel.supported = original_wheel_supported
Wheel.support_index_min = original_support_index_min
self._available_candidates_cache = original_cache


@contextmanager
def open_local_or_remote_file(link, session):
Expand Down
3 changes: 2 additions & 1 deletion piptools/resolver.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,8 @@ def resolve_hashes(self, ireqs):
"""
Finds acceptable hashes for all of the given InstallRequirements.
"""
return {ireq: self.repository.get_hashes(ireq) for ireq in ireqs}
with self.repository.allow_all_wheels():
return {ireq: self.repository.get_hashes(ireq) for ireq in ireqs}

def resolve(self, max_rounds=10):
"""
Expand Down
6 changes: 6 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import json
from contextlib import contextmanager
from functools import partial

from pip._vendor.packaging.version import Version
Expand Down Expand Up @@ -49,6 +50,11 @@ def get_dependencies(self, ireq):
dependencies = [dep for extra in extras for dep in self.index[name][version][extra]]
return [InstallRequirement.from_line(dep, constraint=ireq.constraint) for dep in dependencies]

@contextmanager
def allow_all_wheels(self):
# No need to do an actual pip.Wheel mock here.
yield


class FakeInstalledDistribution(object):
def __init__(self, line, deps=None):
Expand Down
3 changes: 2 additions & 1 deletion tests/test_repository_pypi.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,8 @@ def test_generate_hashes_all_platforms(from_line):
session = pip_command._build_session(pip_options)
repository = PyPIRepository(pip_options, session)
ireq = from_line('cffi==1.9.1')
assert repository.get_hashes(ireq) == expected
with repository.allow_all_wheels():
assert repository.get_hashes(ireq) == expected


def test_generate_hashes_without_interfering_with_each_other(from_line):
Expand Down