Add --unstable-feature=resolver

This introduces a new general option --unstable-feature that can be used
to opt into "preview" features in pip not enabled by default. Currently
the only available feature is "resolver".

A stub resolver interface (which would fail on invocation) is provided
to respond to the flag.

The --unstable-feature option is hidden from --help since the resolver
does not yet work. This suppression should be removed when we release
the resolver for general/public testing.
This commit is contained in:
Tzu-ping Chung 2020-03-11 17:16:11 +08:00
parent e15ef595dd
commit fc810d7353
6 changed files with 95 additions and 8 deletions

View File

@ -915,6 +915,19 @@ no_python_version_warning = partial(
) # type: Callable[..., Option]
unstable_feature = partial(
Option,
'--unstable-feature',
dest='unstable_features',
metavar='feature',
action='append',
default=[],
choices=['resolver'],
help=SUPPRESS_HELP, # TODO: Enable this when the resolver actually works.
# help='Enable unstable feature(s) that may be backward incompatible.',
) # type: Callable[..., Option]
##########
# groups #
##########
@ -943,6 +956,7 @@ general_group = {
disable_pip_version_check,
no_color,
no_python_version_warning,
unstable_feature,
]
} # type: Dict[str, Any]

View File

@ -26,7 +26,6 @@ from pip._internal.req.constructors import (
)
from pip._internal.req.req_file import parse_requirements
from pip._internal.req.req_set import RequirementSet
from pip._internal.resolution.legacy.resolver import Resolver
from pip._internal.self_outdated_check import (
make_link_collector,
pip_self_version_check,
@ -42,6 +41,7 @@ if MYPY_CHECK_RUNNING:
from pip._internal.models.target_python import TargetPython
from pip._internal.req.req_install import InstallRequirement
from pip._internal.req.req_tracker import RequirementTracker
from pip._internal.resolution.base import BaseResolver
from pip._internal.utils.temp_dir import (
TempDirectory,
TempDirectoryTypeRegistry,
@ -248,7 +248,7 @@ class RequirementCommand(IndexGroupCommand):
use_pep517=None, # type: Optional[bool]
py_version_info=None # type: Optional[Tuple[int, ...]]
):
# type: (...) -> Resolver
# type: (...) -> BaseResolver
"""
Create a Resolver instance for the given parameters.
"""
@ -258,7 +258,25 @@ class RequirementCommand(IndexGroupCommand):
wheel_cache=wheel_cache,
use_pep517=use_pep517,
)
return Resolver(
# The long import name and duplicated invocation is needed to convince
# Mypy into correctly typechecking. Otherwise it would complain the
# "Resolver" class being redefined.
if 'resolver' in options.unstable_features:
import pip._internal.resolution.resolvelib.resolver
return pip._internal.resolution.resolvelib.resolver.Resolver(
preparer=preparer,
finder=finder,
make_install_req=make_install_req,
use_user_site=use_user_site,
ignore_dependencies=options.ignore_dependencies,
ignore_installed=ignore_installed,
ignore_requires_python=ignore_requires_python,
force_reinstall=force_reinstall,
upgrade_strategy=upgrade_strategy,
py_version_info=py_version_info,
)
import pip._internal.resolution.legacy.resolver
return pip._internal.resolution.legacy.resolver.Resolver(
preparer=preparer,
finder=finder,
make_install_req=make_install_req,

View File

@ -0,0 +1,20 @@
from pip._internal.utils.typing import MYPY_CHECK_RUNNING
if MYPY_CHECK_RUNNING:
from typing import Callable, List
from pip._internal.req.req_install import InstallRequirement
from pip._internal.req.req_set import RequirementSet
InstallRequirementProvider = Callable[
[str, InstallRequirement], InstallRequirement
]
class BaseResolver(object):
def resolve(self, root_reqs, check_supported_wheels):
# type: (List[InstallRequirement], bool) -> RequirementSet
raise NotImplementedError()
def get_installation_order(self, req_set):
# type: (RequirementSet) -> List[InstallRequirement]
raise NotImplementedError()

View File

@ -29,6 +29,7 @@ from pip._internal.exceptions import (
UnsupportedPythonVersion,
)
from pip._internal.req.req_set import RequirementSet
from pip._internal.resolution.base import BaseResolver
from pip._internal.utils.logging import indent_log
from pip._internal.utils.misc import dist_in_usersite, normalize_version_info
from pip._internal.utils.packaging import (
@ -38,17 +39,15 @@ from pip._internal.utils.packaging import (
from pip._internal.utils.typing import MYPY_CHECK_RUNNING
if MYPY_CHECK_RUNNING:
from typing import Callable, DefaultDict, List, Optional, Set, Tuple
from typing import DefaultDict, List, Optional, Set, Tuple
from pip._vendor import pkg_resources
from pip._internal.distributions import AbstractDistribution
from pip._internal.index.package_finder import PackageFinder
from pip._internal.operations.prepare import RequirementPreparer
from pip._internal.req.req_install import InstallRequirement
from pip._internal.resolution.base import InstallRequirementProvider
InstallRequirementProvider = Callable[
[str, InstallRequirement], InstallRequirement
]
DiscoveredDependencies = DefaultDict[str, List[InstallRequirement]]
logger = logging.getLogger(__name__)
@ -102,7 +101,7 @@ def _check_dist_requires_python(
))
class Resolver(object):
class Resolver(BaseResolver):
"""Resolves which packages need to be installed/uninstalled to perform \
the requested operation without breaking the requirements of any package.
"""

View File

@ -0,0 +1,36 @@
from pip._internal.resolution.base import BaseResolver
from pip._internal.utils.typing import MYPY_CHECK_RUNNING
if MYPY_CHECK_RUNNING:
from typing import List, Optional, Tuple
from pip._internal.index.package_finder import PackageFinder
from pip._internal.operations.prepare import RequirementPreparer
from pip._internal.req.req_install import InstallRequirement
from pip._internal.req.req_set import RequirementSet
from pip._internal.resolution.base import InstallRequirementProvider
class Resolver(BaseResolver):
def __init__(
self,
preparer, # type: RequirementPreparer
finder, # type: PackageFinder
make_install_req, # type: InstallRequirementProvider
use_user_site, # type: bool
ignore_dependencies, # type: bool
ignore_installed, # type: bool
ignore_requires_python, # type: bool
force_reinstall, # type: bool
upgrade_strategy, # type: str
py_version_info=None, # type: Optional[Tuple[int, ...]]
):
super(Resolver, self).__init__()
def resolve(self, root_reqs, check_supported_wheels):
# type: (List[InstallRequirement], bool) -> RequirementSet
raise NotImplementedError()
def get_installation_order(self, req_set):
# type: (RequirementSet) -> List[InstallRequirement]
raise NotImplementedError()