mirror of
https://gitlab.com/MoonTestUse1/AdministrationItDepartmens.git
synced 2025-08-14 00:25:46 +02:00
483 lines
17 KiB
Python
483 lines
17 KiB
Python
import collections
|
|
import operator
|
|
|
|
from .providers import AbstractResolver
|
|
from .structs import DirectedGraph, IteratorMapping, build_iter_view
|
|
|
|
RequirementInformation = collections.namedtuple(
|
|
"RequirementInformation", ["requirement", "parent"]
|
|
)
|
|
|
|
|
|
class ResolverException(Exception):
|
|
"""A base class for all exceptions raised by this module.
|
|
|
|
Exceptions derived by this class should all be handled in this module. Any
|
|
bubbling pass the resolver should be treated as a bug.
|
|
"""
|
|
|
|
|
|
class RequirementsConflicted(ResolverException):
|
|
def __init__(self, criterion):
|
|
super(RequirementsConflicted, self).__init__(criterion)
|
|
self.criterion = criterion
|
|
|
|
def __str__(self):
|
|
return "Requirements conflict: {}".format(
|
|
", ".join(repr(r) for r in self.criterion.iter_requirement()),
|
|
)
|
|
|
|
|
|
class InconsistentCandidate(ResolverException):
|
|
def __init__(self, candidate, criterion):
|
|
super(InconsistentCandidate, self).__init__(candidate, criterion)
|
|
self.candidate = candidate
|
|
self.criterion = criterion
|
|
|
|
def __str__(self):
|
|
return "Provided candidate {!r} does not satisfy {}".format(
|
|
self.candidate,
|
|
", ".join(repr(r) for r in self.criterion.iter_requirement()),
|
|
)
|
|
|
|
|
|
class Criterion(object):
|
|
"""Representation of possible resolution results of a package.
|
|
|
|
This holds three attributes:
|
|
|
|
* `information` is a collection of `RequirementInformation` pairs.
|
|
Each pair is a requirement contributing to this criterion, and the
|
|
candidate that provides the requirement.
|
|
* `incompatibilities` is a collection of all known not-to-work candidates
|
|
to exclude from consideration.
|
|
* `candidates` is a collection containing all possible candidates deducted
|
|
from the union of contributing requirements and known incompatibilities.
|
|
It should never be empty, except when the criterion is an attribute of a
|
|
raised `RequirementsConflicted` (in which case it is always empty).
|
|
|
|
.. note::
|
|
This class is intended to be externally immutable. **Do not** mutate
|
|
any of its attribute containers.
|
|
"""
|
|
|
|
def __init__(self, candidates, information, incompatibilities):
|
|
self.candidates = candidates
|
|
self.information = information
|
|
self.incompatibilities = incompatibilities
|
|
|
|
def __repr__(self):
|
|
requirements = ", ".join(
|
|
"({!r}, via={!r})".format(req, parent)
|
|
for req, parent in self.information
|
|
)
|
|
return "Criterion({})".format(requirements)
|
|
|
|
def iter_requirement(self):
|
|
return (i.requirement for i in self.information)
|
|
|
|
def iter_parent(self):
|
|
return (i.parent for i in self.information)
|
|
|
|
|
|
class ResolutionError(ResolverException):
|
|
pass
|
|
|
|
|
|
class ResolutionImpossible(ResolutionError):
|
|
def __init__(self, causes):
|
|
super(ResolutionImpossible, self).__init__(causes)
|
|
# causes is a list of RequirementInformation objects
|
|
self.causes = causes
|
|
|
|
|
|
class ResolutionTooDeep(ResolutionError):
|
|
def __init__(self, round_count):
|
|
super(ResolutionTooDeep, self).__init__(round_count)
|
|
self.round_count = round_count
|
|
|
|
|
|
# Resolution state in a round.
|
|
State = collections.namedtuple("State", "mapping criteria backtrack_causes")
|
|
|
|
|
|
class Resolution(object):
|
|
"""Stateful resolution object.
|
|
|
|
This is designed as a one-off object that holds information to kick start
|
|
the resolution process, and holds the results afterwards.
|
|
"""
|
|
|
|
def __init__(self, provider, reporter):
|
|
self._p = provider
|
|
self._r = reporter
|
|
self._states = []
|
|
|
|
@property
|
|
def state(self):
|
|
try:
|
|
return self._states[-1]
|
|
except IndexError:
|
|
raise AttributeError("state")
|
|
|
|
def _push_new_state(self):
|
|
"""Push a new state into history.
|
|
|
|
This new state will be used to hold resolution results of the next
|
|
coming round.
|
|
"""
|
|
base = self._states[-1]
|
|
state = State(
|
|
mapping=base.mapping.copy(),
|
|
criteria=base.criteria.copy(),
|
|
backtrack_causes=base.backtrack_causes[:],
|
|
)
|
|
self._states.append(state)
|
|
|
|
def _add_to_criteria(self, criteria, requirement, parent):
|
|
self._r.adding_requirement(requirement=requirement, parent=parent)
|
|
|
|
identifier = self._p.identify(requirement_or_candidate=requirement)
|
|
criterion = criteria.get |