Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
ryanking13 committed Feb 1, 2025
1 parent 144851e commit 86fd799
Show file tree
Hide file tree
Showing 5 changed files with 287 additions and 0 deletions.
17 changes: 17 additions & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,20 @@ include micropip/_vendored/packaging/LICENSE.APACHE
include micropip/_vendored/packaging/LICENSE.BSD

include micropip/_vendored/packaging/README.rst

exclude micropip/_vendored/resolvelib/.github/*
exclude micropip/_vendored/resolvelib/examples/*
exclude micropip/_vendored/resolvelib/news/*
exclude micropip/_vendored/resolvelib/tests/*
exclude micropip/_vendored/resolvelib/.editorconfig
exclude micropip/_vendored/resolvelib/.gitignore
exclude micropip/_vendored/resolvelib/CHANGELOG.rst
exclude micropip/_vendored/resolvelib/CODE_OF_CONDUCT.md
exclude micropip/_vendored/resolvelib/DEVELOPMENT.rst
exclude micropip/_vendored/resolvelib/MANIFEST.in
exclude micropip/_vendored/resolvelib/noxfile.py
exclude micropip/_vendored/resolvelib/pyproject.toml
exclude micropip/_vendored/resolvelib/README.rst

include micropip/_vendored/resolvelib/LICENSE

Empty file added micropip/resolvelib/__init__.py
Empty file.
93 changes: 93 additions & 0 deletions micropip/resolvelib/base.py
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")
135 changes: 135 additions & 0 deletions micropip/resolvelib/provider.py
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]
42 changes: 42 additions & 0 deletions micropip/resolvelib/reporter.py
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)

0 comments on commit 86fd799

Please sign in to comment.