Skip to content

Commit 28464dd

Browse files
committed
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.
1 parent e15ef59 commit 28464dd

File tree

6 files changed

+96
-9
lines changed

6 files changed

+96
-9
lines changed

src/pip/_internal/cli/cmdoptions.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -915,6 +915,19 @@ def check_list_path_option(options):
915915
) # type: Callable[..., Option]
916916

917917

918+
unstable_feature = partial(
919+
Option,
920+
'--unstable-feature',
921+
dest='unstable_features',
922+
metavar='feature',
923+
action='append',
924+
default=None,
925+
choices=['resolver'],
926+
help=SUPPRESS_HELP, # TODO: Enable this when the resolver actually works.
927+
# help='Enable unstable feature(s) that may be backward incompatible.',
928+
) # type: Callable[..., Option]
929+
930+
918931
##########
919932
# groups #
920933
##########
@@ -943,6 +956,7 @@ def check_list_path_option(options):
943956
disable_pip_version_check,
944957
no_color,
945958
no_python_version_warning,
959+
unstable_feature,
946960
]
947961
} # type: Dict[str, Any]
948962

src/pip/_internal/cli/req_command.py

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@
2626
)
2727
from pip._internal.req.req_file import parse_requirements
2828
from pip._internal.req.req_set import RequirementSet
29-
from pip._internal.resolution.legacy.resolver import Resolver
3029
from pip._internal.self_outdated_check import (
3130
make_link_collector,
3231
pip_self_version_check,
@@ -36,12 +35,13 @@
3635

3736
if MYPY_CHECK_RUNNING:
3837
from optparse import Values
39-
from typing import Any, List, Optional, Tuple
38+
from typing import Any, List, Optional, Tuple, Type
4039

4140
from pip._internal.cache import WheelCache
4241
from pip._internal.models.target_python import TargetPython
4342
from pip._internal.req.req_install import InstallRequirement
4443
from pip._internal.req.req_tracker import RequirementTracker
44+
from pip._internal.resolution.base.resolver import BaseResolver
4545
from pip._internal.utils.temp_dir import (
4646
TempDirectory,
4747
TempDirectoryTypeRegistry,
@@ -234,6 +234,21 @@ def make_requirement_preparer(
234234
use_user_site=use_user_site,
235235
)
236236

237+
@staticmethod
238+
def get_resolver_cls(options):
239+
# type: (Values) -> Type[BaseResolver]
240+
"""Get the resolver implementation for instantiation.
241+
242+
This long import name is needed to convince Mypy into correctly
243+
typecheck. Otherwise it would complain the "Resolver" class being
244+
redefined.
245+
"""
246+
if 'resolver' in options.unstable_features:
247+
import pip._internal.resolution.resolvelib.resolver
248+
return pip._internal.resolution.resolvelib.resolver.Resolver
249+
import pip._internal.resolution.legacy.resolver
250+
return pip._internal.resolution.legacy.resolver.Resolver
251+
237252
@staticmethod
238253
def make_resolver(
239254
preparer, # type: RequirementPreparer
@@ -248,7 +263,7 @@ def make_resolver(
248263
use_pep517=None, # type: Optional[bool]
249264
py_version_info=None # type: Optional[Tuple[int, ...]]
250265
):
251-
# type: (...) -> Resolver
266+
# type: (...) -> BaseResolver
252267
"""
253268
Create a Resolver instance for the given parameters.
254269
"""
@@ -258,7 +273,7 @@ def make_resolver(
258273
wheel_cache=wheel_cache,
259274
use_pep517=use_pep517,
260275
)
261-
return Resolver(
276+
return RequirementCommand.get_resolver_cls(options)(
262277
preparer=preparer,
263278
finder=finder,
264279
make_install_req=make_install_req,

src/pip/_internal/resolution/base.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
from pip._internal.utils.typing import MYPY_CHECK_RUNNING
2+
3+
4+
if MYPY_CHECK_RUNNING:
5+
from typing import Callable, List
6+
from pip._internal.req.req_install import InstallRequirement
7+
from pip._internal.req.req_set import RequirementSet
8+
9+
InstallRequirementProvider = Callable[
10+
[str, InstallRequirement], InstallRequirement
11+
]
12+
13+
14+
class BaseResolver(object):
15+
def resolve(self, root_reqs, check_supported_wheels):
16+
# type: (List[InstallRequirement], bool) -> RequirementSet
17+
raise NotImplementedError()
18+
19+
def get_installation_order(self, req_set):
20+
# type: (RequirementSet) -> List[InstallRequirement]
21+
raise NotImplementedError()

src/pip/_internal/resolution/legacy/resolver.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
UnsupportedPythonVersion,
3030
)
3131
from pip._internal.req.req_set import RequirementSet
32+
from pip._internal.resolution.base.resolver import BaseResolver
3233
from pip._internal.utils.logging import indent_log
3334
from pip._internal.utils.misc import dist_in_usersite, normalize_version_info
3435
from pip._internal.utils.packaging import (
@@ -38,17 +39,15 @@
3839
from pip._internal.utils.typing import MYPY_CHECK_RUNNING
3940

4041
if MYPY_CHECK_RUNNING:
41-
from typing import Callable, DefaultDict, List, Optional, Set, Tuple
42+
from typing import DefaultDict, List, Optional, Set, Tuple
4243
from pip._vendor import pkg_resources
4344

4445
from pip._internal.distributions import AbstractDistribution
4546
from pip._internal.index.package_finder import PackageFinder
4647
from pip._internal.operations.prepare import RequirementPreparer
4748
from pip._internal.req.req_install import InstallRequirement
49+
from pip._internal.resolution.base import InstallRequirementProvider
4850

49-
InstallRequirementProvider = Callable[
50-
[str, InstallRequirement], InstallRequirement
51-
]
5251
DiscoveredDependencies = DefaultDict[str, List[InstallRequirement]]
5352

5453
logger = logging.getLogger(__name__)
@@ -102,7 +101,7 @@ def _check_dist_requires_python(
102101
))
103102

104103

105-
class Resolver(object):
104+
class Resolver(BaseResolver):
106105
"""Resolves which packages need to be installed/uninstalled to perform \
107106
the requested operation without breaking the requirements of any package.
108107
"""

src/pip/_internal/resolution/resolvelib/__init__.py

Whitespace-only changes.
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
from pip._internal.resolution.base import (
2+
BaseResolver,
3+
InstallRequirementProvider,
4+
)
5+
from pip._internal.utils.typing import MYPY_CHECK_RUNNING
6+
7+
if MYPY_CHECK_RUNNING:
8+
from typing import List, Optional, Tuple
9+
10+
from pip._internal.index.package_finder import PackageFinder
11+
from pip._internal.operations.prepare import RequirementPreparer
12+
from pip._internal.req.req_install import InstallRequirement
13+
from pip._internal.req.req_set import RequirementSet
14+
15+
16+
class Resolver(BaseResolver):
17+
def __init__(
18+
self,
19+
preparer, # type: RequirementPreparer
20+
finder, # type: PackageFinder
21+
make_install_req, # type: InstallRequirementProvider
22+
use_user_site, # type: bool
23+
ignore_dependencies, # type: bool
24+
ignore_installed, # type: bool
25+
ignore_requires_python, # type: bool
26+
force_reinstall, # type: bool
27+
upgrade_strategy, # type: str
28+
py_version_info=None, # type: Optional[Tuple[int, ...]]
29+
):
30+
super(Resolver, self).__init__()
31+
32+
def resolve(self, root_reqs, check_supported_wheels):
33+
# type: (List[InstallRequirement], bool) -> RequirementSet
34+
raise NotImplementedError()
35+
36+
def get_installation_order(self, req_set):
37+
# type: (RequirementSet) -> List[InstallRequirement]
38+
raise NotImplementedError()

0 commit comments

Comments
 (0)