Skip to content

Commit

Permalink
Add support for pip 8.1.2
Browse files Browse the repository at this point in the history
  • Loading branch information
jmbowman committed Jul 6, 2016
1 parent f1ea213 commit b8043be
Show file tree
Hide file tree
Showing 7 changed files with 90 additions and 35 deletions.
21 changes: 19 additions & 2 deletions piptools/repositories/local.py
Expand Up @@ -2,9 +2,25 @@
from __future__ import (absolute_import, division, print_function,
unicode_literals)

from piptools.utils import key_from_req
from .base import BaseRepository


def ireq_satisfied_by_existing_pin(ireq, existing_pin):
"""
Return True if the given InstallationRequirement is satisfied by the
previously encountered version pin.
"""
if hasattr(existing_pin.req, 'specs'):
# pip < 8.1.2
version = existing_pin.req.specs[0][1]
return version in ireq.req
else:
# pip >= 8.1.2
version = next(iter(existing_pin.req.specifier)).version
return version in ireq.req.specifier


class LocalRequirementsRepository(BaseRepository):
"""
The LocalRequirementsRepository proxied the _real_ repository by first
Expand Down Expand Up @@ -38,8 +54,9 @@ def freshen_build_caches(self):
self.repository.freshen_build_caches()

def find_best_match(self, ireq, prereleases=None):
existing_pin = self.existing_pins.get(ireq.req.project_name.lower())
if existing_pin and existing_pin.req.specs[0][1] in ireq.req:
key = key_from_req(ireq.req)
existing_pin = self.existing_pins.get(key)
if existing_pin and ireq_satisfied_by_existing_pin(ireq, existing_pin):
return existing_pin
else:
return self.repository.find_best_match(ireq, prereleases)
Expand Down
37 changes: 31 additions & 6 deletions piptools/resolver.py
Expand Up @@ -14,7 +14,7 @@
from .exceptions import UnsupportedConstraint
from .logging import log
from .utils import (format_requirement, format_specifier, full_groupby,
is_pinned_requirement)
is_pinned_requirement, key_from_req)

green = partial(click.style, fg='green')
magenta = partial(click.style, fg='magenta')
Expand All @@ -24,7 +24,32 @@ def _dep_key(ireq):
if ireq.req is None and ireq.link is not None:
return str(ireq.link)
else:
return ireq.req.key
return key_from_req(ireq.req)


class RequirementSummary(object):
"""
Summary of a requirement's properties for comparison purposes.
"""
def __init__(self, req):
self.req = req
self.key = key_from_req(req)
self.extras = str(sorted(req.extras))
if hasattr(req, 'specs'):
# pip < 8.1.2
self.specifier = str(req.specs)
else:
# pip >= 8.1.2
self.specifier = str(req.specifier)

def __eq__(self, other):
return str(self) == str(other)

def __hash__(self):
return hash(str(self))

def __str__(self):
return repr([self.key, self.specifier, self.extras])


class Resolver(object):
Expand Down Expand Up @@ -129,7 +154,7 @@ def _group_constraints(self, constraints):
# NOTE we may be losing some info on dropped reqs here
combined_ireq.req.specifier &= ireq.req.specifier
# Return a sorted, de-duped tuple of extras
combined_ireq.extras = tuple(sorted(set(combined_ireq.extras + ireq.extras)))
combined_ireq.extras = tuple(sorted(set(tuple(combined_ireq.extras) + tuple(ireq.extras))))
yield combined_ireq

def _resolve_one_round(self):
Expand Down Expand Up @@ -160,14 +185,14 @@ def _resolve_one_round(self):
for best_match in best_matches
for dep in self._iter_dependencies(best_match))

# NOTE: We need to compare the underlying Requirement objects, since
# NOTE: We need to compare RequirementSummary objects, since
# InstallRequirement does not define equality
diff = {t.req for t in theirs} - {t.req for t in self.their_constraints}
diff = {RequirementSummary(t.req) for t in theirs} - {RequirementSummary(t.req) for t in self.their_constraints}
has_changed = len(diff) > 0
if has_changed:
log.debug('')
log.debug('New dependencies found in this round:')
for new_dependency in sorted(diff, key=lambda req: req.key):
for new_dependency in sorted(diff, key=lambda req: key_from_req(req.req)):
log.debug(' adding {}'.format(new_dependency))

# Store the last round's results in the their_constraints
Expand Down
19 changes: 10 additions & 9 deletions piptools/sync.py
Expand Up @@ -5,7 +5,7 @@

from . import click
from .exceptions import IncompatibleRequirements, UnsupportedConstraint
from .utils import flat_map
from .utils import flat_map, key_from_req

PACKAGES_TO_IGNORE = [
'pip',
Expand Down Expand Up @@ -34,13 +34,14 @@ def dependency_tree(installed_keys, root_key):

while queue:
v = queue.popleft()
if v.key in dependencies:
key = key_from_req(v)
if key in dependencies:
continue

dependencies.add(v.key)
dependencies.add(key)

for dep_specifier in v.requires():
dep_name = dep_specifier.key
dep_name = key_from_req(dep_specifier)
if dep_name in installed_keys:
dep = installed_keys[dep_name]

Expand All @@ -59,7 +60,7 @@ def get_dists_to_ignore(installed):
locally, click should also be installed/uninstalled depending on the given
requirements.
"""
installed_keys = {r.key: r for r in installed}
installed_keys = {key_from_req(r): r for r in installed}
return list(flat_map(lambda req: dependency_tree(installed_keys, req), PACKAGES_TO_IGNORE))


Expand All @@ -72,7 +73,7 @@ def merge(requirements, ignore_conflicts):
'Perhaps add -e option?')
raise UnsupportedConstraint(msg, ireq)

key = ireq.link or ireq.req.key
key = ireq.link or key_from_req(ireq.req)

if not ignore_conflicts:
existing_ireq = by_key.get(key)
Expand All @@ -93,17 +94,17 @@ def diff(compiled_requirements, installed_dists):
Calculate which packages should be installed or uninstalled, given a set
of compiled requirements and a list of currently installed modules.
"""
requirements_lut = {r.link or r.req.key: r for r in compiled_requirements}
requirements_lut = {r.link or key_from_req(r.req): r for r in compiled_requirements}

satisfied = set() # holds keys
to_install = set() # holds keys-and-versions
to_uninstall = set() # holds keys

pkgs_to_ignore = get_dists_to_ignore(installed_dists)
for dist in installed_dists:
key = dist.key
key = key_from_req(dist)
if key not in requirements_lut:
to_uninstall.add(dist.key)
to_uninstall.add(key)
elif requirements_lut[key].specifier.contains(dist.version):
satisfied.add(key)

Expand Down
34 changes: 22 additions & 12 deletions piptools/utils.py
Expand Up @@ -29,15 +29,25 @@ def assert_compatible_pip_version():
'perhaps run `pip install --upgrade pip`?'.format(pip.__version__))
sys.exit(4)

if pip_version_info >= (8, 1, 2):
print('ERROR:')
print('You are using pip>=8.1.2, which changed some internal data structures pip-tools')
print('depends on. Support for this is scheduled for pip-tools>=1.7. Until then,')
print('consider downgrading your pip:')
print('')
print(' $ pip install --upgrade pip==8.1.1')
print('')
sys.exit(4)

def key_from_req(req):
"""Get an all-lowercase version of the requirement's name."""
if hasattr(req, 'key'):
# pip 8.1.1 or below, using pkg_resources
return req.key
else:
# pip 8.1.2 or above, using packaging
return req.name.lower()


def name_from_req(req):
"""Get the name of the requirement"""
if hasattr(req, 'project_name'):
# pip 8.1.1 or below, using pkg_resources
return req.project_name
else:
# pip 8.1.2 or above, using packaging
return req.name


def comment(text):
Expand All @@ -64,7 +74,7 @@ def format_requirement(ireq, include_specifier=True):
elif include_specifier:
line = str(ireq.req)
else:
line = ireq.req.project_name
line = name_from_req(ireq.req)
return line


Expand Down Expand Up @@ -113,9 +123,9 @@ def as_tuple(ireq):
if not is_pinned_requirement(ireq):
raise TypeError('Expected a pinned InstallRequirement, got {}'.format(ireq))

name = ireq.req.key
name = key_from_req(ireq.req)
version = first(ireq.specifier._specs)._spec[1]
extras = ireq.extras
extras = tuple(sorted(ireq.extras))
return name, version, extras


Expand Down
10 changes: 5 additions & 5 deletions tests/conftest.py
Expand Up @@ -9,7 +9,7 @@
from piptools.cache import DependencyCache
from piptools.repositories.base import BaseRepository
from piptools.resolver import Resolver
from piptools.utils import as_tuple, make_install_requirement
from piptools.utils import as_tuple, key_from_req, make_install_requirement


class FakeRepository(BaseRepository):
Expand All @@ -24,17 +24,17 @@ def find_best_match(self, ireq, prereleases=False):
if ireq.editable:
return ireq

versions = ireq.specifier.filter(self.index[ireq.req.key], prereleases=prereleases)
versions = ireq.specifier.filter(self.index[key_from_req(ireq.req)], prereleases=prereleases)
best_version = max(versions, key=Version)
return make_install_requirement(ireq.req.key, best_version, ireq.extras)
return make_install_requirement(key_from_req(ireq.req), best_version, ireq.extras)

def get_dependencies(self, ireq):
if ireq.editable:
return self.editables[str(ireq.link)]

name, version, extras = as_tuple(ireq)
# Store non-extra dependencies under the empty string
extras = ireq.extras + ("",)
extras += ("",)
dependencies = [dep for extra in extras for dep in self.index[name][version][extra]]
return [InstallRequirement.from_line(dep) for dep in dependencies]

Expand All @@ -47,7 +47,7 @@ def __init__(self, line, deps=None):

self.req = Requirement.parse(line)

self.key = self.req.key
self.key = key_from_req(self.req)
self.specifier = self.req.specifier

self.version = line.split("==")[1]
Expand Down
1 change: 1 addition & 0 deletions tests/test_cli.py
Expand Up @@ -90,6 +90,7 @@ def test_extra_index_option(pip_conf):
' http://extraindex1.com\n'
' http://extraindex2.com' in out.output)


def test_trusted_host(pip_conf):

assert os.path.exists(pip_conf)
Expand Down
3 changes: 2 additions & 1 deletion tests/test_minimal_upgrade.py
@@ -1,5 +1,6 @@
import pytest
from piptools.repositories import LocalRequirementsRepository
from piptools.utils import name_from_req


@pytest.mark.parametrize(
Expand Down Expand Up @@ -30,7 +31,7 @@ def test_no_upgrades(base_resolver, repository, from_line, input, pins, expected
existing_pins = dict()
for line in pins:
ireq = from_line(line)
existing_pins[ireq.req.project_name] = ireq
existing_pins[name_from_req(ireq.req)] = ireq
local_repository = LocalRequirementsRepository(existing_pins, repository)
output = base_resolver(input, prereleases=False, repository=local_repository).resolve()
output = {str(line) for line in output}
Expand Down

0 comments on commit b8043be

Please sign in to comment.