forked from pyodide/micropip
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
144851e
commit 86fd799
Showing
5 changed files
with
287 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
"""copied and modified from pip/.../resolvelib/base.py""" | ||
from collections.abc import Iterable | ||
|
||
# from pip._internal.models.link import Link, links_equivalent | ||
# from pip._internal.req.req_install import InstallRequirement | ||
# from pip._internal.utils.hashes import Hashes | ||
from .._vendored.packaging.src.packaging.utils import NormalizedName | ||
from .._vendored.packaging.src.packaging.version import Version | ||
|
||
# CandidateLookup = tuple["Candidate" | None, InstallRequirement | None] | ||
|
||
|
||
def format_name(project: NormalizedName, extras: frozenset[NormalizedName]) -> str: | ||
if not extras: | ||
return project | ||
extras_expr = ",".join(sorted(extras)) | ||
return f"{project}[{extras_expr}]" | ||
|
||
|
||
class Requirement: | ||
@property | ||
def project_name(self) -> NormalizedName: | ||
"""The "project name" of a requirement. | ||
This is different from ``name`` if this requirement contains extras, | ||
in which case ``name`` would contain the ``[...]`` part, while this | ||
refers to the name of the project. | ||
""" | ||
raise NotImplementedError("Subclass should override") | ||
|
||
@property | ||
def name(self) -> str: | ||
"""The name identifying this requirement in the resolver. | ||
This is different from ``project_name`` if this requirement contains | ||
extras, where ``project_name`` would not contain the ``[...]`` part. | ||
""" | ||
raise NotImplementedError("Subclass should override") | ||
|
||
def is_satisfied_by(self, candidate: "Candidate") -> bool: | ||
return False | ||
|
||
# def get_candidate_lookup(self) -> CandidateLookup: | ||
# raise NotImplementedError("Subclass should override") | ||
|
||
def format_for_error(self) -> str: | ||
raise NotImplementedError("Subclass should override") | ||
|
||
|
||
class Candidate: | ||
@property | ||
def project_name(self) -> NormalizedName: | ||
"""The "project name" of the candidate. | ||
This is different from ``name`` if this candidate contains extras, | ||
in which case ``name`` would contain the ``[...]`` part, while this | ||
refers to the name of the project. | ||
""" | ||
raise NotImplementedError("Override in subclass") | ||
|
||
@property | ||
def name(self) -> str: | ||
"""The name identifying this candidate in the resolver. | ||
This is different from ``project_name`` if this candidate contains | ||
extras, where ``project_name`` would not contain the ``[...]`` part. | ||
""" | ||
raise NotImplementedError("Override in subclass") | ||
|
||
@property | ||
def version(self) -> Version: | ||
raise NotImplementedError("Override in subclass") | ||
|
||
@property | ||
def is_installed(self) -> bool: | ||
raise NotImplementedError("Override in subclass") | ||
|
||
@property | ||
def is_editable(self) -> bool: | ||
raise NotImplementedError("Override in subclass") | ||
|
||
@property | ||
# def source_link(self) -> Link | None: | ||
# raise NotImplementedError("Override in subclass") | ||
|
||
def iter_dependencies(self, with_requires: bool) -> Iterable[Requirement | None]: | ||
raise NotImplementedError("Override in subclass") | ||
|
||
# def get_install_requirement(self) -> InstallRequirement | None: | ||
# raise NotImplementedError("Override in subclass") | ||
|
||
def format_for_error(self) -> str: | ||
raise NotImplementedError("Subclass should override") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,135 @@ | ||
from collections.abc import Sequence | ||
from functools import lru_cache | ||
from typing import Iterable, Iterator, Mapping, Union, override | ||
from .._vendored.resolvelib.src.resolvelib import AbstractProvider | ||
from .base import Requirement, Candidate | ||
|
||
|
||
class MicropipProvider(AbstractProvider): | ||
"""Provider Implementation for resolvlib""" | ||
|
||
def __init__( | ||
self, | ||
ignore_dependencies: bool, | ||
user_requested: dict[str, int], | ||
) -> None: | ||
self._ignore_dependencies = ignore_dependencies | ||
self._user_requested = user_requested | ||
|
||
@override | ||
def identify(self, requirement_or_candidate: Requirement | Candidate) -> str: | ||
return requirement_or_candidate.name | ||
|
||
@override | ||
def get_preference( | ||
self, | ||
identifier: str, | ||
resolutions: Mapping[str, Candidate], | ||
candidates: Mapping[str, Iterator[Candidate]], | ||
information: Mapping[str, Iterable["PreferenceInformation"]], | ||
backtrack_causes: Sequence["PreferenceInformation"], | ||
) -> "Preference": | ||
"""Produce a sort key for given requirement based on preference. | ||
The lower the return value is, the more preferred this group of | ||
arguments is. | ||
Currently micropip considers the following in order: | ||
* if the requirement exists in the lockfile, and the index url is the default index url, | ||
then prefer the lockfile candidate. | ||
* otherwise, lookup teh candidate from the index. | ||
""" | ||
# try: | ||
# next(iter(information[identifier])) | ||
# except StopIteration: | ||
# # There is no information for this identifier, so there's no known | ||
# # candidates. | ||
# has_information = False | ||
# else: | ||
# has_information = True | ||
|
||
# if has_information: | ||
# lookups = (r.get_candidate_lookup() for r, _ in information[identifier]) | ||
# candidate, ireqs = zip(*lookups) | ||
# else: | ||
# candidate, ireqs = None, () | ||
|
||
# operators = [ | ||
# specifier.operator | ||
# for specifier_set in (ireq.specifier for ireq in ireqs if ireq) | ||
# for specifier in specifier_set | ||
# ] | ||
|
||
# direct = candidate is not None | ||
# pinned = any(op[:2] == "==" for op in operators) | ||
# unfree = bool(operators) | ||
|
||
# try: | ||
# requested_order: Union[int, float] = self._user_requested[identifier] | ||
# except KeyError: | ||
# requested_order = math.inf | ||
# if has_information: | ||
# parent_depths = ( | ||
# self._known_depths[parent.name] if parent is not None else 0.0 | ||
# for _, parent in information[identifier] | ||
# ) | ||
# inferred_depth = min(d for d in parent_depths) + 1.0 | ||
# else: | ||
# inferred_depth = math.inf | ||
# else: | ||
# inferred_depth = 1.0 | ||
# self._known_depths[identifier] = inferred_depth | ||
|
||
# requested_order = self._user_requested.get(identifier, math.inf) | ||
|
||
# # Requires-Python has only one candidate and the check is basically | ||
# # free, so we always do it first to avoid needless work if it fails. | ||
# requires_python = identifier == REQUIRES_PYTHON_IDENTIFIER | ||
|
||
# # Prefer the causes of backtracking on the assumption that the problem | ||
# # resolving the dependency tree is related to the failures that caused | ||
# # the backtracking | ||
# backtrack_cause = self.is_backtrack_cause(identifier, backtrack_causes) | ||
|
||
# return ( | ||
# not requires_python, | ||
# not direct, | ||
# not pinned, | ||
# not backtrack_cause, | ||
# inferred_depth, | ||
# requested_order, | ||
# not unfree, | ||
# identifier, | ||
# ) | ||
|
||
@override | ||
def find_matches( | ||
self, | ||
identifier: str, | ||
requirements: Mapping[str, Iterator[Requirement]], | ||
incompatibilities: Mapping[str, Iterator[Candidate]], | ||
) -> Iterable[Candidate]: | ||
# constraint = _get_with_identifier( | ||
# self._constraints, | ||
# identifier, | ||
# default=Constraint.empty(), | ||
# ) | ||
return self._factory.find_candidates( | ||
identifier=identifier, | ||
requirements=requirements, | ||
# constraint=constraint, | ||
prefers_installed=False, | ||
incompatibilities=incompatibilities, | ||
is_satisfied_by=self.is_satisfied_by, | ||
) | ||
|
||
@override | ||
@lru_cache(maxsize=None) | ||
def is_satisfied_by(self, requirement: Requirement, candidate: Candidate) -> bool: | ||
return requirement.is_satisfied_by(candidate) | ||
|
||
@override | ||
def get_dependencies(self, candidate: Candidate) -> Sequence[Requirement]: | ||
with_requires = not self._ignore_dependencies | ||
return [r for r in candidate.iter_dependencies(with_requires) if r is not None] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
""" | ||
Reporter class implementation for resolvelib. | ||
""" | ||
from typing import Any | ||
from ..logging import setup_logging | ||
from .._vendored.resolvelib.src.resolvelib import BaseReporter | ||
from .base import Requirement, Candidate | ||
|
||
class DebuggingReporter(BaseReporter): | ||
""" | ||
Adapted from pip. | ||
A reporter that does an info log for every event it sees. | ||
""" | ||
def __init__(self, verbose) -> None: | ||
self._ctx = setup_logging().ctx_level(verbose) | ||
self._logger = self._ctx.__enter__() | ||
|
||
def __del__(self) -> None: | ||
self._ctx.__exit__(None, None, None) | ||
|
||
def starting(self) -> None: | ||
self.logger.info("Reporter.starting()") | ||
|
||
def starting_round(self, index: int) -> None: | ||
self.logger.info("Reporter.starting_round(%r)", index) | ||
|
||
def ending_round(self, index: int, state: Any) -> None: | ||
self.logger.info("Reporter.ending_round(%r, state)", index) | ||
self.logger.debug("Reporter.ending_round(%r, %r)", index, state) | ||
|
||
def ending(self, state: Any) -> None: | ||
self.logger.info("Reporter.ending(%r)", state) | ||
|
||
def adding_requirement(self, requirement: Requirement, parent: Candidate) -> None: | ||
self.logger.info("Reporter.adding_requirement(%r, %r)", requirement, parent) | ||
|
||
def rejecting_candidate(self, criterion: Any, candidate: Candidate) -> None: | ||
self.logger.info("Reporter.rejecting_candidate(%r, %r)", criterion, candidate) | ||
|
||
def pinning(self, candidate: Candidate) -> None: | ||
self.logger.info("Reporter.pinning(%r)", candidate) |