Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
A new mechanism for showing "did you mean" help.
Makes suggestions for unknown goals and unknown flags. The old system for suggesting similar flag names was complex, and didn't handle or display scopes in a user-helpful way. So for example if the user used a global flag in goal scope, we would show a suggestion like: "Unknown flag --foobar, did you mean --foobar". This new implementation handles that case, and moves the error message computation and display out of the options system and into the local_pants_runner.py, which has enough context to do so. [ci skip-rust] [ci skip-build-wheels]
- Loading branch information
Showing
17 changed files
with
202 additions
and
282 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
# Copyright 2020 Pants project contributors (see CONTRIBUTORS.md). | ||
# Licensed under the Apache License, Version 2.0 (see LICENSE). | ||
|
||
import difflib | ||
|
||
from pants.engine.unions import UnionMembership | ||
from pants.help.help_info_extracter import HelpInfoExtracter | ||
from pants.help.maybe_color import MaybeColor | ||
from pants.option.errors import UnknownFlagsError | ||
from pants.option.options import Options | ||
from pants.option.scope import GLOBAL_SCOPE | ||
|
||
|
||
class FlagErrorHelpPrinter(MaybeColor): | ||
"""Prints help related to erroneous command-line flag specification to the console.""" | ||
|
||
def __init__(self, options: Options): | ||
super().__init__(options.for_global_scope().colors) | ||
self._bin_name = options.for_global_scope().pants_bin_name | ||
self._all_help_info = HelpInfoExtracter.get_all_help_info( | ||
options, | ||
# We only care about the options-related help info, so we pass in | ||
# dummy values for union_membership and consumed_scopes_mapper. | ||
UnionMembership({}), | ||
lambda x: tuple(), | ||
) | ||
|
||
def handle_unknown_flags(self, err: UnknownFlagsError): | ||
global_flags = self._all_help_info.scope_to_help_info[GLOBAL_SCOPE].collect_unscoped_flags() | ||
oshi_for_scope = self._all_help_info.scope_to_help_info.get(err.arg_scope) | ||
possibilities = oshi_for_scope.collect_unscoped_flags() if oshi_for_scope else [] | ||
|
||
if err.arg_scope == GLOBAL_SCOPE: | ||
# We allow all scoped flags for any scope in the global scope position on | ||
# the cmd line (that is, to the left of any goals). | ||
for oshi in self._all_help_info.scope_to_help_info.values(): | ||
possibilities.extend(oshi.collect_scoped_flags()) | ||
|
||
for flag in err.flags: | ||
print(f"Unknown flag {self.maybe_red(flag)} on {err.arg_scope or 'global'} scope") | ||
did_you_mean = difflib.get_close_matches(flag, possibilities) | ||
if err.arg_scope != GLOBAL_SCOPE and flag in global_flags: | ||
# It's a common error to use a global flag in a goal scope, so we special-case it. | ||
print( | ||
f"Did you mean to use the global {self.maybe_cyan(flag)}? " | ||
f"Global options must come before any goals." | ||
) | ||
elif did_you_mean: | ||
print(f"Did you mean {', '.join(self.maybe_cyan(g) for g in did_you_mean)}?") | ||
|
||
help_cmd = ( | ||
f"{self._bin_name} help" | ||
f"{'' if err.arg_scope == GLOBAL_SCOPE else (' ' + err.arg_scope)}" | ||
) | ||
print(f"Use `{self.maybe_green(help_cmd)}` to get help.") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.