提交 190c424b 编写于 作者: T Tzu-ping Chung

Implement Python as a dependency

If a dist contains Requires-Python metadata, it is converted into a
Requirement for the resolver based on whether the Requires-Python
is compatible or not.

If it is compatible, an ExplicitRequirement is returned to hold the
Python information (either sys.version_info, or the user-supplied
--python-version).

If it is incompatible, a special NoMatchRequirement is returned, which
never matches to anything, generating a ResolutionImpossible to report
the Python version incompatibility.

The --ignore-requires-python flag is implemented as to not return a
Requirement for Requires-Python at all.
上级 2a1f3c22
import logging
import sys
from pip._vendor.packaging.specifiers import InvalidSpecifier, SpecifierSet
from pip._vendor.packaging.utils import canonicalize_name
from pip._vendor.packaging.version import Version
from pip._internal.req.constructors import install_req_from_line
from pip._internal.req.req_install import InstallRequirement
from pip._internal.utils.misc import normalize_version_info
from pip._internal.utils.packaging import get_requires_python
from pip._internal.utils.typing import MYPY_CHECK_RUNNING
from .base import Candidate, format_name
if MYPY_CHECK_RUNNING:
from typing import Any, Optional, Sequence, Set
from pip._internal.models.link import Link
from typing import Any, Optional, Sequence, Set, Tuple
from pip._vendor.packaging.version import _BaseVersion
from pip._vendor.pkg_resources import Distribution
from pip._internal.models.link import Link
from .base import Requirement
from .factory import Factory
......@@ -95,12 +100,32 @@ class LinkCandidate(Candidate):
self._version == self.dist.parsed_version)
return self._dist
def _get_requires_python_specifier(self):
# type: () -> Optional[SpecifierSet]
requires_python = get_requires_python(self.dist)
if requires_python is None:
return None
try:
spec = SpecifierSet(requires_python)
except InvalidSpecifier as e:
logger.warning(
"Package %r has an invalid Requires-Python: %s", self.name, e,
)
return None
return spec
def get_dependencies(self):
# type: () -> Sequence[Requirement]
return [
deps = [
self._factory.make_requirement_from_spec(str(r), self._ireq)
for r in self.dist.requires()
]
python_dep = self._factory.make_requires_python_requirement(
self._get_requires_python_specifier(),
)
if python_dep:
deps.append(python_dep)
return deps
def get_install_requirement(self):
# type: () -> Optional[InstallRequirement]
......@@ -179,3 +204,31 @@ class ExtrasCandidate(LinkCandidate):
# depend on the base candidate, and we'll get the
# install requirement from that.
return None
class RequiresPythonCandidate(Candidate):
def __init__(self, py_version_info):
# type: (Optional[Tuple[int, ...]]) -> None
if py_version_info is not None:
version_info = normalize_version_info(py_version_info)
else:
version_info = sys.version_info[:3]
self._version = Version(".".join(str(c) for c in version_info))
@property
def name(self):
# type: () -> str
return "<Python>" # Avoid conflicting with the PyPI package "Python".
@property
def version(self):
# type: () -> _BaseVersion
return self._version
def get_dependencies(self):
# type: () -> Sequence[Requirement]
return []
def get_install_requirement(self):
# type: () -> Optional[InstallRequirement]
return None
from pip._internal.utils.typing import MYPY_CHECK_RUNNING
from .candidates import ExtrasCandidate, LinkCandidate
from .requirements import ExplicitRequirement, SpecifierRequirement
from .candidates import ExtrasCandidate, LinkCandidate, RequiresPythonCandidate
from .requirements import (
ExplicitRequirement,
NoMatchRequirement,
SpecifierRequirement,
)
if MYPY_CHECK_RUNNING:
from typing import Dict, Set
from typing import Dict, Optional, Set, Tuple
from pip._vendor.packaging.specifiers import SpecifierSet
from pip._internal.index.package_finder import PackageFinder
from pip._internal.models.link import Link
......@@ -21,10 +27,14 @@ class Factory(object):
finder, # type: PackageFinder
preparer, # type: RequirementPreparer
make_install_req, # type: InstallRequirementProvider
ignore_requires_python, # type: bool
py_version_info=None, # type: Optional[Tuple[int, ...]]
):
# type: (...) -> None
self.finder = finder
self.preparer = preparer
self._python_candidate = RequiresPythonCandidate(py_version_info)
self._ignore_requires_python = ignore_requires_python
self._make_install_req_from_spec = make_install_req
self._candidate_cache = {} # type: Dict[Link, LinkCandidate]
......@@ -56,3 +66,11 @@ class Factory(object):
# type: (str, InstallRequirement) -> Requirement
ireq = self._make_install_req_from_spec(specifier, comes_from)
return self.make_requirement_from_install_req(ireq)
def make_requires_python_requirement(self, specifier):
# type: (Optional[SpecifierSet]) -> Optional[Requirement]
if self._ignore_requires_python or specifier is None:
return None
if self._python_candidate.version in specifier:
return ExplicitRequirement(self._python_candidate)
return NoMatchRequirement(self._python_candidate.name)
......@@ -33,6 +33,27 @@ class ExplicitRequirement(Requirement):
return candidate == self.candidate
class NoMatchRequirement(Requirement):
"""A requirement that never matches anything.
"""
def __init__(self, name):
# type: (str) -> None
self._name = canonicalize_name(name)
@property
def name(self):
# type: () -> str
return self._name
def find_matches(self):
# type: () -> Sequence[Candidate]
return []
def is_satisfied_by(self, candidate):
# type: (Candidate) -> bool
return False
class SpecifierRequirement(Requirement):
def __init__(self, ireq, factory):
# type: (InstallRequirement, Factory) -> None
......
......@@ -43,6 +43,8 @@ class Resolver(BaseResolver):
finder=finder,
preparer=preparer,
make_install_req=make_install_req,
ignore_requires_python=ignore_requires_python,
py_version_info=py_version_info,
)
self.ignore_dependencies = ignore_dependencies
self._result = None # type: Optional[Result]
......
......@@ -52,6 +52,8 @@ def factory(finder, preparer):
finder=finder,
preparer=preparer,
make_install_req=install_req_from_line,
ignore_requires_python=False,
py_version_info=None,
)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册