Skip to content

Commit

Permalink
Add --unstable-feature=resolver
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
uranusjr committed Mar 11, 2020
1 parent e15ef59 commit fc810d7
Show file tree
Hide file tree
Showing 6 changed files with 95 additions and 8 deletions.
14 changes: 14 additions & 0 deletions src/pip/_internal/cli/cmdoptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -915,6 +915,19 @@ def check_list_path_option(options):
) # 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 #
##########
Expand Down Expand Up @@ -943,6 +956,7 @@ def check_list_path_option(options):
disable_pip_version_check,
no_color,
no_python_version_warning,
unstable_feature,
]
} # type: Dict[str, Any]

Expand Down
24 changes: 21 additions & 3 deletions src/pip/_internal/cli/req_command.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@
)
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,
Expand All @@ -42,6 +41,7 @@
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,
Expand Down Expand Up @@ -248,7 +248,7 @@ def make_resolver(
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.
"""
Expand All @@ -258,7 +258,25 @@ def make_resolver(
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,
Expand Down
20 changes: 20 additions & 0 deletions src/pip/_internal/resolution/base.py
Original file line number Diff line number Diff line change
@@ -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()
9 changes: 4 additions & 5 deletions src/pip/_internal/resolution/legacy/resolver.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
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 (
Expand All @@ -38,17 +39,15 @@
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__)
Expand Down Expand Up @@ -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.
"""
Expand Down
Empty file.
36 changes: 36 additions & 0 deletions src/pip/_internal/resolution/resolvelib/resolver.py
Original file line number Diff line number Diff line change
@@ -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()

0 comments on commit fc810d7

Please sign in to comment.