Skip to content

Commit

Permalink
Add --no-backtracking option for new resolver
Browse files Browse the repository at this point in the history
Adds a new option `--no-backtracking` to the new resolver. Off by
default (no change to default behaviour), enabling this option prevents
us from running the backtracking behaviour when pinning fails and
instead skip directly to the error reporting.

This can be especially useful in a CI situation, where the developer is
interested in knowing about conflicts up front in order to manually
solve them, or is worried about allowing for a potentially large amount
of time spent in backtracking.
  • Loading branch information
TheKevJames committed Dec 11, 2020
1 parent 27d8687 commit 7f5dd8e
Show file tree
Hide file tree
Showing 9 changed files with 33 additions and 13 deletions.
1 change: 1 addition & 0 deletions news/9258.feature.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
New resolver: Add ``--no-backtracking`` option to enable failfast debugging.
11 changes: 11 additions & 0 deletions src/pip/_internal/cli/cmdoptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -927,6 +927,17 @@ def check_list_path_option(options):
),
) # type: Callable[..., Option]

disable_backtracking = partial(
Option,
'--no-backtracking',
dest='disable_backtracking',
action='store_true',
default=False,
help='Do not attempt to backtrack to resolve package conflicts. This will '
'cause conflicts which may be otherwise automatically solveable to '
'fail fast and require manual intervention!',
) # type: Callable[..., Option]


##########
# groups #
Expand Down
4 changes: 3 additions & 1 deletion src/pip/_internal/commands/download.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ def add_options(self):
self.cmd_opts.add_option(cmdoptions.no_build_isolation())
self.cmd_opts.add_option(cmdoptions.use_pep517())
self.cmd_opts.add_option(cmdoptions.no_use_pep517())
self.cmd_opts.add_option(cmdoptions.disable_backtracking())

self.cmd_opts.add_option(
'-d', '--dest', '--destination-dir', '--destination-directory',
Expand Down Expand Up @@ -128,7 +129,8 @@ def run(self, options, args):
self.trace_basic_info(finder)

requirement_set = resolver.resolve(
reqs, check_supported_wheels=True
reqs, check_supported_wheels=True,
should_backtrack=not options.disable_backtracking
)

downloaded = [] # type: List[str]
Expand Down
4 changes: 3 additions & 1 deletion src/pip/_internal/commands/install.py
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,7 @@ def add_options(self):
self.cmd_opts.add_option(cmdoptions.prefer_binary())
self.cmd_opts.add_option(cmdoptions.require_hashes())
self.cmd_opts.add_option(cmdoptions.progress_bar())
self.cmd_opts.add_option(cmdoptions.disable_backtracking())

index_opts = cmdoptions.make_option_group(
cmdoptions.index_group,
Expand Down Expand Up @@ -318,7 +319,8 @@ def run(self, options, args):
self.trace_basic_info(finder)

requirement_set = resolver.resolve(
reqs, check_supported_wheels=not options.target_dir
reqs, check_supported_wheels=not options.target_dir,
should_backtrack=not options.disable_backtracking
)

try:
Expand Down
4 changes: 3 additions & 1 deletion src/pip/_internal/commands/wheel.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ def add_options(self):
self.cmd_opts.add_option(cmdoptions.no_deps())
self.cmd_opts.add_option(cmdoptions.build_dir())
self.cmd_opts.add_option(cmdoptions.progress_bar())
self.cmd_opts.add_option(cmdoptions.disable_backtracking())

self.cmd_opts.add_option(
'--global-option',
Expand Down Expand Up @@ -152,7 +153,8 @@ def run(self, options, args):
self.trace_basic_info(finder)

requirement_set = resolver.resolve(
reqs, check_supported_wheels=True
reqs, check_supported_wheels=True,
should_backtrack=not options.disable_backtracking
)

reqs_to_build = [] # type: List[InstallRequirement]
Expand Down
4 changes: 2 additions & 2 deletions src/pip/_internal/resolution/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@


class BaseResolver(object):
def resolve(self, root_reqs, check_supported_wheels):
# type: (List[InstallRequirement], bool) -> RequirementSet
def resolve(self, root_reqs, check_supported_wheels, should_backtrack):
# type: (List[InstallRequirement], bool, bool) -> RequirementSet
raise NotImplementedError()

def get_installation_order(self, req_set):
Expand Down
4 changes: 2 additions & 2 deletions src/pip/_internal/resolution/legacy/resolver.py
Original file line number Diff line number Diff line change
Expand Up @@ -149,8 +149,8 @@ def __init__(
self._discovered_dependencies = \
defaultdict(list) # type: DiscoveredDependencies

def resolve(self, root_reqs, check_supported_wheels):
# type: (List[InstallRequirement], bool) -> RequirementSet
def resolve(self, root_reqs, check_supported_wheels, _should_backtrack):
# type: (List[InstallRequirement], bool, bool) -> RequirementSet
"""Resolve what operations need to be done
As a side-effect of this method, the packages (and their dependencies)
Expand Down
5 changes: 3 additions & 2 deletions src/pip/_internal/resolution/resolvelib/resolver.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,8 @@ def __init__(
self.upgrade_strategy = upgrade_strategy
self._result = None # type: Optional[Result]

def resolve(self, root_reqs, check_supported_wheels):
# type: (List[InstallRequirement], bool) -> RequirementSet
def resolve(self, root_reqs, check_supported_wheels, should_backtrack):
# type: (List[InstallRequirement], bool, bool) -> RequirementSet

constraints = {} # type: Dict[str, Constraint]
user_requested = set() # type: Set[str]
Expand Down Expand Up @@ -120,6 +120,7 @@ def resolve(self, root_reqs, check_supported_wheels):
try_to_avoid_resolution_too_deep = 2000000
self._result = resolver.resolve(
requirements, max_rounds=try_to_avoid_resolution_too_deep,
should_backtrack=should_backtrack,
)

except ResolutionImpossible as e:
Expand Down
9 changes: 5 additions & 4 deletions src/pip/_vendor/resolvelib/resolvers.py
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,7 @@ def _backtrack(self):
# No way to backtrack anymore.
return False

def resolve(self, requirements, max_rounds):
def resolve(self, requirements, max_rounds, should_backtrack):
if self._states:
raise RuntimeError("already resolved")

Expand Down Expand Up @@ -341,7 +341,7 @@ def resolve(self, requirements, max_rounds):
if failure_causes:
# Backtrack if pinning fails. The backtrack process puts us in
# an unpinned state, so we can work on it in the next round.
success = self._backtrack()
success = self._backtrack() if should_backtrack else False

# Dead ends everywhere. Give up.
if not success:
Expand Down Expand Up @@ -413,7 +413,7 @@ class Resolver(AbstractResolver):

base_exception = ResolverException

def resolve(self, requirements, max_rounds=100):
def resolve(self, requirements, max_rounds=100, should_backtrack=True):
"""Take a collection of constraints, spit out the resolution result.
The return value is a representation to the final resolution result. It
Expand Down Expand Up @@ -442,5 +442,6 @@ def resolve(self, requirements, max_rounds=100):
`max_rounds` argument.
"""
resolution = Resolution(self.provider, self.reporter)
state = resolution.resolve(requirements, max_rounds=max_rounds)
state = resolution.resolve(requirements, max_rounds=max_rounds,
should_backtrack=should_backtrack)
return _build_result(state)

0 comments on commit 7f5dd8e

Please sign in to comment.