Skip to content

Commit

Permalink
Vendor ResolveLib from Git
Browse files Browse the repository at this point in the history
We are vendoring from the Git source for now, so the bug fix turnover
can be quicker if there's anything wrong in the resolution logic.

HEAD up-to-date as of 2020-03-12.
  • Loading branch information
uranusjr committed Mar 12, 2020
1 parent 4f6bef6 commit 36065cf
Show file tree
Hide file tree
Showing 11 changed files with 632 additions and 0 deletions.
1 change: 1 addition & 0 deletions news/7850.vendor
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add ResolveLib as a vendored dependency.
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -56,4 +56,5 @@ msgpack-python = "msgpack"

[tool.vendoring.license.fallback-urls]
pytoml = "https://github.com/avakar/pytoml/raw/master/LICENSE"
resolvelib = "https://github.com/sarugaku/resolvelib/raw/master/LICENSE"
webencodings = "https://github.com/SimonSapin/python-webencodings/raw/master/LICENSE"
1 change: 1 addition & 0 deletions src/pip/_vendor/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,4 +107,5 @@ def vendored(modulename):
vendored("requests.packages.urllib3.util.ssl_")
vendored("requests.packages.urllib3.util.timeout")
vendored("requests.packages.urllib3.util.url")
vendored("resolvelib")
vendored("urllib3")
1 change: 1 addition & 0 deletions src/pip/_vendor/resolvelib.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from resolvelib import *
13 changes: 13 additions & 0 deletions src/pip/_vendor/resolvelib/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
Copyright (c) 2018, Tzu-ping Chung <uranusjr@gmail.com>

Permission to use, copy, modify, and distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.

THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
24 changes: 24 additions & 0 deletions src/pip/_vendor/resolvelib/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
__all__ = [
"__version__",
"AbstractProvider",
"AbstractResolver",
"BaseReporter",
"Resolver",
"RequirementsConflicted",
"ResolutionError",
"ResolutionImpossible",
"ResolutionTooDeep",
]

__version__ = "0.2.3.dev0"


from .providers import AbstractProvider, AbstractResolver
from .reporters import BaseReporter
from .resolvers import (
RequirementsConflicted,
Resolver,
ResolutionError,
ResolutionImpossible,
ResolutionTooDeep,
)
121 changes: 121 additions & 0 deletions src/pip/_vendor/resolvelib/providers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
class AbstractProvider(object):
"""Delegate class to provide requirement interface for the resolver.
"""

def identify(self, dependency):
"""Given a dependency, return an identifier for it.
This is used in many places to identify the dependency, e.g. whether
two requirements should have their specifier parts merged, whether
two specifications would conflict with each other (because they the
same name but different versions).
"""
raise NotImplementedError

def get_preference(self, resolution, candidates, information):
"""Produce a sort key for given specification based on preference.
The preference is defined as "I think this requirement should be
resolved first". The lower the return value is, the more preferred
this group of arguments is.
:param resolution: Currently pinned candidate, or `None`.
:param candidates: A list of possible candidates.
:param information: A list of requirement information.
Each information instance is a named tuple with two entries:
* `requirement` specifies a requirement contributing to the current
candidate list
* `parent` specifies the candidate that provids (dependend on) the
requirement, or `None` to indicate a root requirement.
The preference could depend on a various of issues, including (not
necessarily in this order):
* Is this package pinned in the current resolution result?
* How relaxed is the requirement? Stricter ones should probably be
worked on first? (I don't know, actually.)
* How many possibilities are there to satisfy this requirement? Those
with few left should likely be worked on first, I guess?
* Are there any known conflicts for this requirement? We should
probably work on those with the most known conflicts.
A sortable value should be returned (this will be used as the `key`
parameter of the built-in sorting function). The smaller the value is,
the more preferred this specification is (i.e. the sorting function
is called with `reverse=False`).
"""
raise NotImplementedError

def find_matches(self, requirement):
"""Find all possible candidates that satisfy a requirement.
This should try to get candidates based on the requirement's type.
For VCS, local, and archive requirements, the one-and-only match is
returned, and for a "named" requirement, the index(es) should be
consulted to find concrete candidates for this requirement.
The returned candidates should be sorted by reversed preference, e.g.
the most preferred should be LAST. This is done so list-popping can be
as efficient as possible.
"""
raise NotImplementedError

def is_satisfied_by(self, requirement, candidate):
"""Whether the given requirement can be satisfied by a candidate.
A boolean should be returned to indicate whether `candidate` is a
viable solution to the requirement.
"""
raise NotImplementedError

def get_dependencies(self, candidate):
"""Get dependencies of a candidate.
This should return a collection of requirements that `candidate`
specifies as its dependencies.
"""
raise NotImplementedError


class AbstractResolver(object):
"""The thing that performs the actual resolution work.
"""

base_exception = Exception

def __init__(self, provider, reporter):
self.provider = provider
self.reporter = reporter

def resolve(self, requirements, **kwargs):
"""Take a collection of constraints, spit out the resolution result.
Parameters
----------
requirements : Collection
A collection of constraints
kwargs : optional
Additional keyword arguments that subclasses may accept.
Raises
------
self.base_exception
Any raised exception is guaranteed to be a subclass of
self.base_exception. The string representation of an exception
should be human readable and provide context for why it occurred.
Returns
-------
retval : object
A representation of the final resolution state. It can be any object
with a `mapping` attribute that is a Mapping. Other attributes can
be used to provide resolver-specific information.
The `mapping` attribute MUST be key-value pair is an identifier of a
requirement (as returned by the provider's `identify` method) mapped
to the resolved candidate (chosen from the return value of the
provider's `find_matches` method).
"""
raise NotImplementedError
24 changes: 24 additions & 0 deletions src/pip/_vendor/resolvelib/reporters.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
class BaseReporter(object):
"""Delegate class to provider progress reporting for the resolver.
"""

def starting(self):
"""Called before the resolution actually starts.
"""

def starting_round(self, index):
"""Called before each round of resolution starts.
The index is zero-based.
"""

def ending_round(self, index, state):
"""Called before each round of resolution ends.
This is NOT called if the resolution ends at this round. Use `ending`
if you want to report finalization. The index is zero-based.
"""

def ending(self, state):
"""Called before the resolution ends successfully.
"""
Loading

0 comments on commit 36065cf

Please sign in to comment.