We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
pip-compile may add unwanted requirements from future versions of package dependencies
pip-compile may add unwanted requirements from future versions of package dependencies.
E.g. when collecting third-level dependences (`celery`->`amqp`->`vine`), when second-level
dependency changes (`ampq` 2.0.2 -> 1.4.9), 3rd-level requirement (`vine`) _might_ remain in resulting output. In fact second compilation gives expected results (don't know why).
##### Steps to replicate

Tried on fresh virtulaenv, OS X with pip==8.1.1 and pip-tools==1.6.5

```
$ echo "celery\nlibrabbitmq" > requirements.in
$ pip-compile -r -v
```
##### Expected result

No vine package in requirements.txt

```
amqp==1.4.9               # via kombu, librabbitmq
anyjson==0.3.3            # via kombu
billiard==3.3.0.23        # via celery
celery==3.1.23
kombu==3.0.35             # via celery
librabbitmq==1.6.1
pytz==2016.4              # via celery
```
##### Actual result (unwanted vine requirement is present)

```
amqp==1.4.9               # via kombu, librabbitmq
anyjson==0.3.3            # via kombu
billiard==3.3.0.23        # via celery
celery==3.1.23
kombu==3.0.35             # via celery
librabbitmq==1.6.1
pytz==2016.4              # via celery
vine==1.1.0
```

Full log (first run, actual result): https://gist.github.com/coagulant/db486bd554bd34d2074d9e2df8e25105
Full log (second run, expected result): https://gist.github.com/coagulant/e66e9ad23e2572757ca710cd0865b0d0


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `piptools/resolver.py`
Content:
```
1 # coding: utf-8
2 from __future__ import (absolute_import, division, print_function,
3                         unicode_literals)
4 
5 import os
6 from functools import partial
7 from itertools import chain, count
8 
9 from first import first
10 from pip.req import InstallRequirement
11 
12 from . import click
13 from .cache import DependencyCache
14 from .exceptions import UnsupportedConstraint
15 from .logging import log
16 from .utils import (format_requirement, format_specifier, full_groupby,
17                     is_pinned_requirement, key_from_req)
18 
19 green = partial(click.style, fg='green')
20 magenta = partial(click.style, fg='magenta')
21 
22 
23 def _dep_key(ireq):
24     if ireq.req is None and ireq.link is not None:
25         return str(ireq.link)
26     else:
27         return key_from_req(ireq.req)
28 
29 
30 class RequirementSummary(object):
31     """
32     Summary of a requirement's properties for comparison purposes.
33     """
34     def __init__(self, req):
35         self.req = req
36         self.key = key_from_req(req)
37         self.extras = str(sorted(req.extras))
38         if hasattr(req, 'specs'):
39             # pip < 8.1.2
40             self.specifier = str(req.specs)
41         else:
42             # pip >= 8.1.2
43             self.specifier = str(req.specifier)
44 
45     def __eq__(self, other):
46         return str(self) == str(other)
47 
48     def __hash__(self):
49         return hash(str(self))
50 
51     def __str__(self):
52         return repr([self.key, self.specifier, self.extras])
53 
54 
55 class Resolver(object):
56     def __init__(self, constraints, repository, cache=None, prereleases=False, clear_caches=False):
57         """
58         This class resolves a given set of constraints (a collection of
59         InstallRequirement objects) by consulting the given Repository and the
60         DependencyCache.
61         """
62         self.our_constraints = set(constraints)
63         self.their_constraints = set()
64         self.repository = repository
65         if cache is None:
66             cache = DependencyCache()  # pragma: no cover
67         self.dependency_cache = cache
68         self.prereleases = prereleases
69         self.clear_caches = clear_caches
70 
71     @property
72     def constraints(self):
73         return set(self._group_constraints(chain(self.our_constraints,
74                                                  self.their_constraints)))
75 
76     def resolve_hashes(self, ireqs):
77         """
78         Finds acceptable hashes for all of the given InstallRequirements.
79         """
80         return {ireq: self.repository.get_hashes(ireq) for ireq in ireqs}
81 
82     def resolve(self, max_rounds=10):
83         """
84         Finds concrete package versions for all the given InstallRequirements
85         and their recursive dependencies.  The end result is a flat list of
86         (name, version) tuples.  (Or an editable package.)
87 
88         Resolves constraints one round at a time, until they don't change
89         anymore.  Protects against infinite loops by breaking out after a max
90         number rounds.
91         """
92         if self.clear_caches:
93             self.dependency_cache.clear()
94             self.repository.clear_caches()
95 
96         self._check_constraints()
97 
98         # Ignore existing packages
99         os.environ[str('PIP_EXISTS_ACTION')] = str('i')  # NOTE: str() wrapping necessary for Python 2/3 compat
100         for current_round in count(start=1):
101             if current_round > max_rounds:
102                 raise RuntimeError('No stable configuration of concrete packages '
103                                    'could be found for the given constraints after '
104                                    '%d rounds of resolving.\n'
105                                    'This is likely a bug.' % max_rounds)
106 
107             log.debug('')
108             log.debug(magenta('{:^60}'.format('ROUND {}'.format(current_round))))
109             has_changed, best_matches = self._resolve_one_round()
110             log.debug('-' * 60)
111             log.debug('Result of round {}: {}'.format(current_round,
112                                                       'not stable' if has_changed else 'stable, done'))
113             if not has_changed:
114                 break
115 
116             # If a package version (foo==2.0) was built in a previous round,
117             # and in this round a different version of foo needs to be built
118             # (i.e. foo==1.0), the directory will exist already, which will
119             # cause a pip build failure.  The trick is to start with a new
120             # build cache dir for every round, so this can never happen.
121             self.repository.freshen_build_caches()
122 
123         del os.environ['PIP_EXISTS_ACTION']
124         return best_matches
125 
126     def _check_constraints(self):
127         for constraint in chain(self.our_constraints, self.their_constraints):
128             if constraint.link is not None and not constraint.editable:
129                 msg = ('pip-compile does not support URLs as packages, unless they are editable. '
130                        'Perhaps add -e option?')
131                 raise UnsupportedConstraint(msg, constraint)
132 
133     def _group_constraints(self, constraints):
134         """
135         Groups constraints (remember, InstallRequirements!) by their key name,
136         and combining their SpecifierSets into a single InstallRequirement per
137         package.  For example, given the following constraints:
138 
139             Django<1.9,>=1.4.2
140             django~=1.5
141             Flask~=0.7
142 
143         This will be combined into a single entry per package:
144 
145             django~=1.5,<1.9,>=1.4.2
146             flask~=0.7
147 
148         """
149         for _, ireqs in full_groupby(constraints, key=_dep_key):
150             ireqs = list(ireqs)
151             editable_ireq = first(ireqs, key=lambda ireq: ireq.editable)
152             if editable_ireq:
153                 yield editable_ireq  # ignore all the other specs: the editable one is the one that counts
154                 continue
155 
156             ireqs = iter(ireqs)
157             combined_ireq = next(ireqs)
158             combined_ireq.comes_from = None
159             for ireq in ireqs:
160                 # NOTE we may be losing some info on dropped reqs here
161                 combined_ireq.req.specifier &= ireq.req.specifier
162                 # Return a sorted, de-duped tuple of extras
163                 combined_ireq.extras = tuple(sorted(set(tuple(combined_ireq.extras) + tuple(ireq.extras))))
164             yield combined_ireq
165 
166     def _resolve_one_round(self):
167         """
168         Resolves one level of the current constraints, by finding the best
169         match for each package in the repository and adding all requirements
170         for those best package versions.  Some of these constraints may be new
171         or updated.
172 
173         Returns whether new constraints appeared in this round.  If no
174         constraints were added or changed, this indicates a stable
175         configuration.
176         """
177         # Sort this list for readability of terminal output
178         constraints = sorted(self.constraints, key=_dep_key)
179         log.debug('Current constraints:')
180         for constraint in constraints:
181             log.debug('  {}'.format(constraint))
182 
183         log.debug('')
184         log.debug('Finding the best candidates:')
185         best_matches = set(self.get_best_match(ireq) for ireq in constraints)
186 
187         # Find the new set of secondary dependencies
188         log.debug('')
189         log.debug('Finding secondary dependencies:')
190         theirs = set(dep
191                      for best_match in best_matches
192                      for dep in self._iter_dependencies(best_match))
193 
194         # NOTE: We need to compare RequirementSummary objects, since
195         # InstallRequirement does not define equality
196         diff = {RequirementSummary(t.req) for t in theirs} - {RequirementSummary(t.req) for t in self.their_constraints}
197         has_changed = len(diff) > 0
198         if has_changed:
199             log.debug('')
200             log.debug('New dependencies found in this round:')
201             for new_dependency in sorted(diff, key=lambda req: key_from_req(req.req)):
202                 log.debug('  adding {}'.format(new_dependency))
203 
204         # Store the last round's results in the their_constraints
205         self.their_constraints |= theirs
206         return has_changed, best_matches
207 
208     def get_best_match(self, ireq):
209         """
210         Returns a (pinned or editable) InstallRequirement, indicating the best
211         match to use for the given InstallRequirement (in the form of an
212         InstallRequirement).
213 
214         Example:
215         Given the constraint Flask>=0.10, may return Flask==0.10.1 at
216         a certain moment in time.
217 
218         Pinned requirements will always return themselves, i.e.
219 
220             Flask==0.10.1 => Flask==0.10.1
221 
222         """
223         if ireq.editable:
224             # NOTE: it's much quicker to immediately return instead of
225             # hitting the index server
226             best_match = ireq
227         elif is_pinned_requirement(ireq):
228             # NOTE: it's much quicker to immediately return instead of
229             # hitting the index server
230             best_match = ireq
231         else:
232             best_match = self.repository.find_best_match(ireq, prereleases=self.prereleases)
233 
234         # Format the best match
235         log.debug('  found candidate {} (constraint was {})'.format(format_requirement(best_match),
236                                                                     format_specifier(ireq)))
237         return best_match
238 
239     def _iter_dependencies(self, ireq):
240         """
241         Given a pinned or editable InstallRequirement, collects all the
242         secondary dependencies for them, either by looking them up in a local
243         cache, or by reaching out to the repository.
244 
245         Editable requirements will never be looked up, as they may have
246         changed at any time.
247         """
248         if ireq.editable:
249             for dependency in self.repository.get_dependencies(ireq):
250                 yield dependency
251             return
252         elif not is_pinned_requirement(ireq):
253             raise TypeError('Expected pinned or editable requirement, got {}'.format(ireq))
254 
255         # Now, either get the dependencies from the dependency cache (for
256         # speed), or reach out to the external repository to
257         # download and inspect the package version and get dependencies
258         # from there
259         if ireq not in self.dependency_cache:
260             log.debug('  {} not in cache, need to check index'.format(format_requirement(ireq)), fg='yellow')
261             dependencies = self.repository.get_dependencies(ireq)
262             self.dependency_cache[ireq] = sorted(str(ireq.req) for ireq in dependencies)
263 
264         # Example: ['Werkzeug>=0.9', 'Jinja2>=2.4']
265         dependency_strings = self.dependency_cache[ireq]
266         log.debug('  {:25} requires {}'.format(format_requirement(ireq),
267                                                ', '.join(sorted(dependency_strings, key=lambda s: s.lower())) or '-'))
268         for dependency_string in dependency_strings:
269             yield InstallRequirement.from_line(dependency_string)
270 
271     def reverse_dependencies(self, ireqs):
272         non_editable = [ireq for ireq in ireqs if not ireq.editable]
273         return self.dependency_cache.reverse_dependencies(non_editable)
274
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/piptools/resolver.py b/piptools/resolver.py
--- a/piptools/resolver.py
+++ b/piptools/resolver.py
@@ -181,22 +181,29 @@
         # Find the new set of secondary dependencies
         log.debug('')
         log.debug('Finding secondary dependencies:')
-        theirs = set(dep
+        # Grouping constraints to make clean diff between rounds
+        theirs = set(self._group_constraints(dep
                      for best_match in best_matches
-                     for dep in self._iter_dependencies(best_match))
+                     for dep in self._iter_dependencies(best_match)))
 
         # NOTE: We need to compare RequirementSummary objects, since
         # InstallRequirement does not define equality
         diff = {RequirementSummary(t.req) for t in theirs} - {RequirementSummary(t.req) for t in self.their_constraints}
-        has_changed = len(diff) > 0
+        removed = ({RequirementSummary(t.req) for t in self.their_constraints} -
+                   {RequirementSummary(t.req) for t in theirs})
+
+        has_changed = len(diff) > 0 or len(removed) > 0
         if has_changed:
             log.debug('')
             log.debug('New dependencies found in this round:')
             for new_dependency in sorted(diff, key=lambda req: key_from_req(req.req)):
                 log.debug('  adding {}'.format(new_dependency))
+            log.debug('Removed dependencies in this round:')
+            for removed_dependency in sorted(removed, key=lambda req: key_from_req(req.req)):
+                log.debug('  removing {}'.format(removed_dependency))
 
         # Store the last round's results in the their_constraints
-        self.their_constraints |= theirs
+        self.their_constraints = theirs
         return has_changed, best_matches
 
     def get_best_match(self, ireq):

{"golden_diff": "diff --git a/piptools/resolver.py b/piptools/resolver.py\n--- a/piptools/resolver.py\n+++ b/piptools/resolver.py\n@@ -181,22 +181,29 @@\n         # Find the new set of secondary dependencies\n         log.debug('')\n         log.debug('Finding secondary dependencies:')\n-        theirs = set(dep\n+        # Grouping constraints to make clean diff between rounds\n+        theirs = set(self._group_constraints(dep\n                      for best_match in best_matches\n-                     for dep in self._iter_dependencies(best_match))\n+                     for dep in self._iter_dependencies(best_match)))\n \n         # NOTE: We need to compare RequirementSummary objects, since\n         # InstallRequirement does not define equality\n         diff = {RequirementSummary(t.req) for t in theirs} - {RequirementSummary(t.req) for t in self.their_constraints}\n-        has_changed = len(diff) > 0\n+        removed = ({RequirementSummary(t.req) for t in self.their_constraints} -\n+                   {RequirementSummary(t.req) for t in theirs})\n+\n+        has_changed = len(diff) > 0 or len(removed) > 0\n         if has_changed:\n             log.debug('')\n             log.debug('New dependencies found in this round:')\n             for new_dependency in sorted(diff, key=lambda req: key_from_req(req.req)):\n                 log.debug('  adding {}'.format(new_dependency))\n+            log.debug('Removed dependencies in this round:')\n+            for removed_dependency in sorted(removed, key=lambda req: key_from_req(req.req)):\n+                log.debug('  removing {}'.format(removed_dependency))\n \n         # Store the last round's results in the their_constraints\n-        self.their_constraints |= theirs\n+        self.their_constraints = theirs\n         return has_changed, best_matches\n \n     def get_best_match(self, ireq):\n", "issue": "pip-compile may add unwanted requirements from future versions of package dependencies\npip-compile may add unwanted requirements from future versions of package dependencies.\nE.g. when collecting third-level dependences (`celery`->`amqp`->`vine`), when second-level\ndependency changes (`ampq` 2.0.2 -> 1.4.9), 3rd-level requirement (`vine`) _might_ remain in resulting output. In fact second compilation gives expected results (don't know why).\n##### Steps to replicate\n\nTried on fresh virtulaenv, OS X with pip==8.1.1 and pip-tools==1.6.5\n\n```\n$ echo \"celery\\nlibrabbitmq\" > requirements.in\n$ pip-compile -r -v\n```\n##### Expected result\n\nNo vine package in requirements.txt\n\n```\namqp==1.4.9               # via kombu, librabbitmq\nanyjson==0.3.3            # via kombu\nbilliard==3.3.0.23        # via celery\ncelery==3.1.23\nkombu==3.0.35             # via celery\nlibrabbitmq==1.6.1\npytz==2016.4              # via celery\n```\n##### Actual result (unwanted vine requirement is present)\n\n```\namqp==1.4.9               # via kombu, librabbitmq\nanyjson==0.3.3            # via kombu\nbilliard==3.3.0.23        # via celery\ncelery==3.1.23\nkombu==3.0.35             # via celery\nlibrabbitmq==1.6.1\npytz==2016.4              # via celery\nvine==1.1.0\n```\n\nFull log (first run, actual result): https://gist.github.com/coagulant/db486bd554bd34d2074d9e2df8e25105\nFull log (second run, expected result): https://gist.github.com/coagulant/e66e9ad23e2572757ca710cd0865b0d0\n\n", "before_files": [{"content": "# coding: utf-8\nfrom __future__ import (absolute_import, division, print_function,\n                        unicode_literals)\n\nimport os\nfrom functools import partial\nfrom itertools import chain, count\n\nfrom first import first\nfrom pip.req import InstallRequirement\n\nfrom . import click\nfrom .cache import DependencyCache\nfrom .exceptions import UnsupportedConstraint\nfrom .logging import log\nfrom .utils import (format_requirement, format_specifier, full_groupby,\n                    is_pinned_requirement, key_from_req)\n\ngreen = partial(click.style, fg='green')\nmagenta = partial(click.style, fg='magenta')\n\n\ndef _dep_key(ireq):\n    if ireq.req is None and ireq.link is not None:\n        return str(ireq.link)\n    else:\n        return key_from_req(ireq.req)\n\n\nclass RequirementSummary(object):\n    \"\"\"\n    Summary of a requirement's properties for comparison purposes.\n    \"\"\"\n    def __init__(self, req):\n        self.req = req\n        self.key = key_from_req(req)\n        self.extras = str(sorted(req.extras))\n        if hasattr(req, 'specs'):\n            # pip < 8.1.2\n            self.specifier = str(req.specs)\n        else:\n            # pip >= 8.1.2\n            self.specifier = str(req.specifier)\n\n    def __eq__(self, other):\n        return str(self) == str(other)\n\n    def __hash__(self):\n        return hash(str(self))\n\n    def __str__(self):\n        return repr([self.key, self.specifier, self.extras])\n\n\nclass Resolver(object):\n    def __init__(self, constraints, repository, cache=None, prereleases=False, clear_caches=False):\n        \"\"\"\n        This class resolves a given set of constraints (a collection of\n        InstallRequirement objects) by consulting the given Repository and the\n        DependencyCache.\n        \"\"\"\n        self.our_constraints = set(constraints)\n        self.their_constraints = set()\n        self.repository = repository\n        if cache is None:\n            cache = DependencyCache()  # pragma: no cover\n        self.dependency_cache = cache\n        self.prereleases = prereleases\n        self.clear_caches = clear_caches\n\n    @property\n    def constraints(self):\n        return set(self._group_constraints(chain(self.our_constraints,\n                                                 self.their_constraints)))\n\n    def resolve_hashes(self, ireqs):\n        \"\"\"\n        Finds acceptable hashes for all of the given InstallRequirements.\n        \"\"\"\n        return {ireq: self.repository.get_hashes(ireq) for ireq in ireqs}\n\n    def resolve(self, max_rounds=10):\n        \"\"\"\n        Finds concrete package versions for all the given InstallRequirements\n        and their recursive dependencies.  The end result is a flat list of\n        (name, version) tuples.  (Or an editable package.)\n\n        Resolves constraints one round at a time, until they don't change\n        anymore.  Protects against infinite loops by breaking out after a max\n        number rounds.\n        \"\"\"\n        if self.clear_caches:\n            self.dependency_cache.clear()\n            self.repository.clear_caches()\n\n        self._check_constraints()\n\n        # Ignore existing packages\n        os.environ[str('PIP_EXISTS_ACTION')] = str('i')  # NOTE: str() wrapping necessary for Python 2/3 compat\n        for current_round in count(start=1):\n            if current_round > max_rounds:\n                raise RuntimeError('No stable configuration of concrete packages '\n                                   'could be found for the given constraints after '\n                                   '%d rounds of resolving.\\n'\n                                   'This is likely a bug.' % max_rounds)\n\n            log.debug('')\n            log.debug(magenta('{:^60}'.format('ROUND {}'.format(current_round))))\n            has_changed, best_matches = self._resolve_one_round()\n            log.debug('-' * 60)\n            log.debug('Result of round {}: {}'.format(current_round,\n                                                      'not stable' if has_changed else 'stable, done'))\n            if not has_changed:\n                break\n\n            # If a package version (foo==2.0) was built in a previous round,\n            # and in this round a different version of foo needs to be built\n            # (i.e. foo==1.0), the directory will exist already, which will\n            # cause a pip build failure.  The trick is to start with a new\n            # build cache dir for every round, so this can never happen.\n            self.repository.freshen_build_caches()\n\n        del os.environ['PIP_EXISTS_ACTION']\n        return best_matches\n\n    def _check_constraints(self):\n        for constraint in chain(self.our_constraints, self.their_constraints):\n            if constraint.link is not None and not constraint.editable:\n                msg = ('pip-compile does not support URLs as packages, unless they are editable. '\n                       'Perhaps add -e option?')\n                raise UnsupportedConstraint(msg, constraint)\n\n    def _group_constraints(self, constraints):\n        \"\"\"\n        Groups constraints (remember, InstallRequirements!) by their key name,\n        and combining their SpecifierSets into a single InstallRequirement per\n        package.  For example, given the following constraints:\n\n            Django<1.9,>=1.4.2\n            django~=1.5\n            Flask~=0.7\n\n        This will be combined into a single entry per package:\n\n            django~=1.5,<1.9,>=1.4.2\n            flask~=0.7\n\n        \"\"\"\n        for _, ireqs in full_groupby(constraints, key=_dep_key):\n            ireqs = list(ireqs)\n            editable_ireq = first(ireqs, key=lambda ireq: ireq.editable)\n            if editable_ireq:\n                yield editable_ireq  # ignore all the other specs: the editable one is the one that counts\n                continue\n\n            ireqs = iter(ireqs)\n            combined_ireq = next(ireqs)\n            combined_ireq.comes_from = None\n            for ireq in ireqs:\n                # NOTE we may be losing some info on dropped reqs here\n                combined_ireq.req.specifier &= ireq.req.specifier\n                # Return a sorted, de-duped tuple of extras\n                combined_ireq.extras = tuple(sorted(set(tuple(combined_ireq.extras) + tuple(ireq.extras))))\n            yield combined_ireq\n\n    def _resolve_one_round(self):\n        \"\"\"\n        Resolves one level of the current constraints, by finding the best\n        match for each package in the repository and adding all requirements\n        for those best package versions.  Some of these constraints may be new\n        or updated.\n\n        Returns whether new constraints appeared in this round.  If no\n        constraints were added or changed, this indicates a stable\n        configuration.\n        \"\"\"\n        # Sort this list for readability of terminal output\n        constraints = sorted(self.constraints, key=_dep_key)\n        log.debug('Current constraints:')\n        for constraint in constraints:\n            log.debug('  {}'.format(constraint))\n\n        log.debug('')\n        log.debug('Finding the best candidates:')\n        best_matches = set(self.get_best_match(ireq) for ireq in constraints)\n\n        # Find the new set of secondary dependencies\n        log.debug('')\n        log.debug('Finding secondary dependencies:')\n        theirs = set(dep\n                     for best_match in best_matches\n                     for dep in self._iter_dependencies(best_match))\n\n        # NOTE: We need to compare RequirementSummary objects, since\n        # InstallRequirement does not define equality\n        diff = {RequirementSummary(t.req) for t in theirs} - {RequirementSummary(t.req) for t in self.their_constraints}\n        has_changed = len(diff) > 0\n        if has_changed:\n            log.debug('')\n            log.debug('New dependencies found in this round:')\n            for new_dependency in sorted(diff, key=lambda req: key_from_req(req.req)):\n                log.debug('  adding {}'.format(new_dependency))\n\n        # Store the last round's results in the their_constraints\n        self.their_constraints |= theirs\n        return has_changed, best_matches\n\n    def get_best_match(self, ireq):\n        \"\"\"\n        Returns a (pinned or editable) InstallRequirement, indicating the best\n        match to use for the given InstallRequirement (in the form of an\n        InstallRequirement).\n\n        Example:\n        Given the constraint Flask>=0.10, may return Flask==0.10.1 at\n        a certain moment in time.\n\n        Pinned requirements will always return themselves, i.e.\n\n            Flask==0.10.1 => Flask==0.10.1\n\n        \"\"\"\n        if ireq.editable:\n            # NOTE: it's much quicker to immediately return instead of\n            # hitting the index server\n            best_match = ireq\n        elif is_pinned_requirement(ireq):\n            # NOTE: it's much quicker to immediately return instead of\n            # hitting the index server\n            best_match = ireq\n        else:\n            best_match = self.repository.find_best_match(ireq, prereleases=self.prereleases)\n\n        # Format the best match\n        log.debug('  found candidate {} (constraint was {})'.format(format_requirement(best_match),\n                                                                    format_specifier(ireq)))\n        return best_match\n\n    def _iter_dependencies(self, ireq):\n        \"\"\"\n        Given a pinned or editable InstallRequirement, collects all the\n        secondary dependencies for them, either by looking them up in a local\n        cache, or by reaching out to the repository.\n\n        Editable requirements will never be looked up, as they may have\n        changed at any time.\n        \"\"\"\n        if ireq.editable:\n            for dependency in self.repository.get_dependencies(ireq):\n                yield dependency\n            return\n        elif not is_pinned_requirement(ireq):\n            raise TypeError('Expected pinned or editable requirement, got {}'.format(ireq))\n\n        # Now, either get the dependencies from the dependency cache (for\n        # speed), or reach out to the external repository to\n        # download and inspect the package version and get dependencies\n        # from there\n        if ireq not in self.dependency_cache:\n            log.debug('  {} not in cache, need to check index'.format(format_requirement(ireq)), fg='yellow')\n            dependencies = self.repository.get_dependencies(ireq)\n            self.dependency_cache[ireq] = sorted(str(ireq.req) for ireq in dependencies)\n\n        # Example: ['Werkzeug>=0.9', 'Jinja2>=2.4']\n        dependency_strings = self.dependency_cache[ireq]\n        log.debug('  {:25} requires {}'.format(format_requirement(ireq),\n                                               ', '.join(sorted(dependency_strings, key=lambda s: s.lower())) or '-'))\n        for dependency_string in dependency_strings:\n            yield InstallRequirement.from_line(dependency_string)\n\n    def reverse_dependencies(self, ireqs):\n        non_editable = [ireq for ireq in ireqs if not ireq.editable]\n        return self.dependency_cache.reverse_dependencies(non_editable)\n", "path": "piptools/resolver.py"}], "after_files": [{"content": "# coding: utf-8\nfrom __future__ import (absolute_import, division, print_function,\n                        unicode_literals)\n\nimport os\nfrom functools import partial\nfrom itertools import chain, count\n\nfrom first import first\nfrom pip.req import InstallRequirement\n\nfrom . import click\nfrom .cache import DependencyCache\nfrom .exceptions import UnsupportedConstraint\nfrom .logging import log\nfrom .utils import (format_requirement, format_specifier, full_groupby,\n                    is_pinned_requirement, key_from_req)\n\ngreen = partial(click.style, fg='green')\nmagenta = partial(click.style, fg='magenta')\n\n\ndef _dep_key(ireq):\n    if ireq.req is None and ireq.link is not None:\n        return str(ireq.link)\n    else:\n        return key_from_req(ireq.req)\n\n\nclass RequirementSummary(object):\n    \"\"\"\n    Summary of a requirement's properties for comparison purposes.\n    \"\"\"\n    def __init__(self, req):\n        self.req = req\n        self.key = key_from_req(req)\n        self.extras = str(sorted(req.extras))\n        if hasattr(req, 'specs'):\n            # pip < 8.1.2\n            self.specifier = str(req.specs)\n        else:\n            # pip >= 8.1.2\n            self.specifier = str(req.specifier)\n\n    def __eq__(self, other):\n        return str(self) == str(other)\n\n    def __hash__(self):\n        return hash(str(self))\n\n    def __str__(self):\n        return repr([self.key, self.specifier, self.extras])\n\n\nclass Resolver(object):\n    def __init__(self, constraints, repository, cache=None, prereleases=False, clear_caches=False):\n        \"\"\"\n        This class resolves a given set of constraints (a collection of\n        InstallRequirement objects) by consulting the given Repository and the\n        DependencyCache.\n        \"\"\"\n        self.our_constraints = set(constraints)\n        self.their_constraints = set()\n        self.repository = repository\n        if cache is None:\n            cache = DependencyCache()  # pragma: no cover\n        self.dependency_cache = cache\n        self.prereleases = prereleases\n        self.clear_caches = clear_caches\n\n    @property\n    def constraints(self):\n        return set(self._group_constraints(chain(self.our_constraints,\n                                                 self.their_constraints)))\n\n    def resolve(self, max_rounds=10):\n        \"\"\"\n        Finds concrete package versions for all the given InstallRequirements\n        and their recursive dependencies.  The end result is a flat list of\n        (name, version) tuples.  (Or an editable package.)\n\n        Resolves constraints one round at a time, until they don't change\n        anymore.  Protects against infinite loops by breaking out after a max\n        number rounds.\n        \"\"\"\n        if self.clear_caches:\n            self.dependency_cache.clear()\n            self.repository.clear_caches()\n\n        self._check_constraints()\n\n        # Ignore existing packages\n        os.environ[str('PIP_EXISTS_ACTION')] = str('i')  # NOTE: str() wrapping necessary for Python 2/3 compat\n        for current_round in count(start=1):\n            if current_round > max_rounds:\n                raise RuntimeError('No stable configuration of concrete packages '\n                                   'could be found for the given constraints after '\n                                   '%d rounds of resolving.\\n'\n                                   'This is likely a bug.' % max_rounds)\n\n            log.debug('')\n            log.debug(magenta('{:^60}'.format('ROUND {}'.format(current_round))))\n            has_changed, best_matches = self._resolve_one_round()\n            log.debug('-' * 60)\n            log.debug('Result of round {}: {}'.format(current_round,\n                                                      'not stable' if has_changed else 'stable, done'))\n            if not has_changed:\n                break\n\n            # If a package version (foo==2.0) was built in a previous round,\n            # and in this round a different version of foo needs to be built\n            # (i.e. foo==1.0), the directory will exist already, which will\n            # cause a pip build failure.  The trick is to start with a new\n            # build cache dir for every round, so this can never happen.\n            self.repository.freshen_build_caches()\n\n        del os.environ['PIP_EXISTS_ACTION']\n        return best_matches\n\n    def _check_constraints(self):\n        for constraint in chain(self.our_constraints, self.their_constraints):\n            if constraint.link is not None and not constraint.editable:\n                msg = ('pip-compile does not support URLs as packages, unless they are editable. '\n                       'Perhaps add -e option?')\n                raise UnsupportedConstraint(msg, constraint)\n\n    def _group_constraints(self, constraints):\n        \"\"\"\n        Groups constraints (remember, InstallRequirements!) by their key name,\n        and combining their SpecifierSets into a single InstallRequirement per\n        package.  For example, given the following constraints:\n\n            Django<1.9,>=1.4.2\n            django~=1.5\n            Flask~=0.7\n\n        This will be combined into a single entry per package:\n\n            django~=1.5,<1.9,>=1.4.2\n            flask~=0.7\n\n        \"\"\"\n        for _, ireqs in full_groupby(constraints, key=_dep_key):\n            ireqs = list(ireqs)\n            editable_ireq = first(ireqs, key=lambda ireq: ireq.editable)\n            if editable_ireq:\n                yield editable_ireq  # ignore all the other specs: the editable one is the one that counts\n                continue\n\n            ireqs = iter(ireqs)\n            combined_ireq = next(ireqs)\n            combined_ireq.comes_from = None\n            for ireq in ireqs:\n                # NOTE we may be losing some info on dropped reqs here\n                combined_ireq.req.specifier &= ireq.req.specifier\n                # Return a sorted, de-duped tuple of extras\n                combined_ireq.extras = tuple(sorted(set(tuple(combined_ireq.extras) + tuple(ireq.extras))))\n            yield combined_ireq\n\n    def _resolve_one_round(self):\n        \"\"\"\n        Resolves one level of the current constraints, by finding the best\n        match for each package in the repository and adding all requirements\n        for those best package versions.  Some of these constraints may be new\n        or updated.\n\n        Returns whether new constraints appeared in this round.  If no\n        constraints were added or changed, this indicates a stable\n        configuration.\n        \"\"\"\n        # Sort this list for readability of terminal output\n        constraints = sorted(self.constraints, key=_dep_key)\n        log.debug('Current constraints:')\n        for constraint in constraints:\n            log.debug('  {}'.format(constraint))\n\n        log.debug('')\n        log.debug('Finding the best candidates:')\n        best_matches = set(self.get_best_match(ireq) for ireq in constraints)\n\n        # Find the new set of secondary dependencies\n        log.debug('')\n        log.debug('Finding secondary dependencies:')\n        # Grouping constraints to make clean diff between rounds\n        theirs = set(self._group_constraints(dep\n                     for best_match in best_matches\n                     for dep in self._iter_dependencies(best_match)))\n\n        # NOTE: We need to compare RequirementSummary objects, since\n        # InstallRequirement does not define equality\n        diff = {RequirementSummary(t.req) for t in theirs} - {RequirementSummary(t.req) for t in self.their_constraints}\n        removed = ({RequirementSummary(t.req) for t in self.their_constraints} -\n                   {RequirementSummary(t.req) for t in theirs})\n\n        has_changed = len(diff) > 0 or len(removed) > 0\n        if has_changed:\n            log.debug('')\n            log.debug('New dependencies found in this round:')\n            for new_dependency in sorted(diff, key=lambda req: key_from_req(req.req)):\n                log.debug('  adding {}'.format(new_dependency))\n            log.debug('Removed dependencies in this round:')\n            for removed_dependency in sorted(removed, key=lambda req: key_from_req(req.req)):\n                log.debug('  removing {}'.format(removed_dependency))\n\n        # Store the last round's results in the their_constraints\n        self.their_constraints = theirs\n        return has_changed, best_matches\n\n    def get_best_match(self, ireq):\n        \"\"\"\n        Returns a (pinned or editable) InstallRequirement, indicating the best\n        match to use for the given InstallRequirement (in the form of an\n        InstallRequirement).\n\n        Example:\n        Given the constraint Flask>=0.10, may return Flask==0.10.1 at\n        a certain moment in time.\n\n        Pinned requirements will always return themselves, i.e.\n\n            Flask==0.10.1 => Flask==0.10.1\n\n        \"\"\"\n        if ireq.editable:\n            # NOTE: it's much quicker to immediately return instead of\n            # hitting the index server\n            best_match = ireq\n        elif is_pinned_requirement(ireq):\n            # NOTE: it's much quicker to immediately return instead of\n            # hitting the index server\n            best_match = ireq\n        else:\n            best_match = self.repository.find_best_match(ireq, prereleases=self.prereleases)\n\n        # Format the best match\n        log.debug('  found candidate {} (constraint was {})'.format(format_requirement(best_match),\n                                                                    format_specifier(ireq)))\n        return best_match\n\n    def _iter_dependencies(self, ireq):\n        \"\"\"\n        Given a pinned or editable InstallRequirement, collects all the\n        secondary dependencies for them, either by looking them up in a local\n        cache, or by reaching out to the repository.\n\n        Editable requirements will never be looked up, as they may have\n        changed at any time.\n        \"\"\"\n        if ireq.editable:\n            for dependency in self.repository.get_dependencies(ireq):\n                yield dependency\n            return\n        elif not is_pinned_requirement(ireq):\n            raise TypeError('Expected pinned or editable requirement, got {}'.format(ireq))\n\n        # Now, either get the dependencies from the dependency cache (for\n        # speed), or reach out to the external repository to\n        # download and inspect the package version and get dependencies\n        # from there\n        if ireq not in self.dependency_cache:\n            log.debug('  {} not in cache, need to check index'.format(format_requirement(ireq)), fg='yellow')\n            dependencies = self.repository.get_dependencies(ireq)\n            self.dependency_cache[ireq] = sorted(str(ireq.req) for ireq in dependencies)\n\n        # Example: ['Werkzeug>=0.9', 'Jinja2>=2.4']\n        dependency_strings = self.dependency_cache[ireq]\n        log.debug('  {:25} requires {}'.format(format_requirement(ireq),\n                                               ', '.join(sorted(dependency_strings, key=lambda s: s.lower())) or '-'))\n        for dependency_string in dependency_strings:\n            yield InstallRequirement.from_line(dependency_string)\n\n    def reverse_dependencies(self, ireqs):\n        non_editable = [ireq for ireq in ireqs if not ireq.editable]\n        return self.dependency_cache.reverse_dependencies(non_editable)\n", "path": "piptools/resolver.py"}]}