1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
|
"""A provider that handles packages with "extras".
Python package dependencies can include "extras", which are additional
dependencies that are installed "on demand". For instance, project X could
have an additional set of dependencies if PDF generation features are needed.
These can be defined for an extra "pdf" and requested on install as X[pdf].
The basic resolvelib algorithm cannot handle extras, as it builds a dependency
graph which needs to be static - the edges (dependencies) from a node
(candidate) must be fixed. Extras break this assumption.
To model projects with extras, we define a candidate as being a project with a
specific set of dependencies. This introduces a problem, as the resolver could
produce a solution that demands version 1.0 of X[foo] and version 2.0 of
X[bar]. This is impossible, as there is actually only one project X to be
installed. To address this, we inject an additional dependency for every
candidate with an extra - X[foo] version v depends on X version v. By doing
this, we constrain the solution to require a unique version of X.
"""
from resolvelib.providers import AbstractProvider
class ExtrasProvider(AbstractProvider):
"""A provider that handles extras."""
def get_extras_for(self, requirement_or_candidate):
"""Given a requirement or candidate, return its extras.
The extras should be a hashable value.
"""
raise NotImplementedError
def get_base_requirement(self, candidate):
"""Given a candidate, return a requirement that specifies that
project/version.
"""
raise NotImplementedError
def identify(self, requirement_or_candidate):
base = super().identify(requirement_or_candidate)
extras = self.get_extras_for(requirement_or_candidate)
if extras:
return (base, extras)
else:
return base
def get_dependencies(self, candidate):
deps = super().get_dependencies(candidate)
if candidate.extras:
req = self.get_base_requirement(candidate)
deps.append(req)
return deps
|