Skip to content

Commit

Permalink
Bug 1057694 - Command suggestions in mach. r=gps
Browse files Browse the repository at this point in the history
  • Loading branch information
halflings committed Aug 26, 2014
1 parent d521111 commit 85ea4a4
Show file tree
Hide file tree
Showing 3 changed files with 24 additions and 8 deletions.
3 changes: 2 additions & 1 deletion python/mach/mach/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,12 @@ class NoCommandError(MachError):
class UnknownCommandError(MachError):
"""Raised when we attempted to execute an unknown command."""

def __init__(self, command, verb):
def __init__(self, command, verb, suggested_commands=None):
MachError.__init__(self)

self.command = command
self.verb = verb
self.suggested_commands = suggested_commands or []

class UnrecognizedArgumentError(MachError):
"""Raised when an unknown argument is passed to mach."""
Expand Down
20 changes: 15 additions & 5 deletions python/mach/mach/dispatcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from __future__ import unicode_literals

import argparse
import difflib
import sys

from operator import itemgetter
Expand Down Expand Up @@ -105,12 +106,21 @@ def __call__(self, parser, namespace, values, option_string=None):
else:
raise NoCommandError()

handler = self._mach_registrar.command_handlers.get(command)
# Command suggestion
if command not in self._mach_registrar.command_handlers:
# We first try to look for a valid command that is very similar to the given command.
suggested_commands = difflib.get_close_matches(command, self._mach_registrar.command_handlers.keys(), cutoff=0.8)
# If we find more than one matching command, or no command at all, we give command suggestions instead
# (with a lower matching threshold). All commands that start with the given command (for instance: 'mochitest-plain',
# 'mochitest-chrome', etc. for 'mochitest-') are also included.
if len(suggested_commands) != 1:
suggested_commands = set(difflib.get_close_matches(command, self._mach_registrar.command_handlers.keys(), cutoff=0.5))
suggested_commands |= {cmd for cmd in self._mach_registrar.command_handlers if cmd.startswith(command)}
raise UnknownCommandError(command, 'run', suggested_commands)
sys.stderr.write("We're assuming the '%s' command is '%s' and we're executing it for you.\n\n" % (command, suggested_commands[0]))
command = suggested_commands[0]

# FUTURE consider looking for commands with similar names and
# suggest or run them.
if not handler:
raise UnknownCommandError(command, 'run')
handler = self._mach_registrar.command_handlers.get(command)

# FUTURE
# If we wanted to conditionally enable commands based on whether
Expand Down
9 changes: 7 additions & 2 deletions python/mach/mach/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,10 +78,14 @@

UNKNOWN_COMMAND_ERROR = r'''
It looks like you are trying to %s an unknown mach command: %s
%s
Run |mach help| to show a list of commands.
'''.lstrip()

SUGGESTED_COMMANDS_MESSAGE = r'''
Did you want to %s any of these commands instead: %s?
'''

UNRECOGNIZED_ARGUMENT_ERROR = r'''
It looks like you passed an unrecognized argument into mach.
Expand Down Expand Up @@ -388,7 +392,8 @@ def _run(self, argv):
print(NO_COMMAND_ERROR)
return 1
except UnknownCommandError as e:
print(UNKNOWN_COMMAND_ERROR % (e.verb, e.command))
suggestion_message = SUGGESTED_COMMANDS_MESSAGE % (e.verb, ', '.join(e.suggested_commands)) if e.suggested_commands else ''
print(UNKNOWN_COMMAND_ERROR % (e.verb, e.command, suggestion_message))
return 1
except UnrecognizedArgumentError as e:
print(UNRECOGNIZED_ARGUMENT_ERROR % (e.command,
Expand Down

0 comments on commit 85ea4a4

Please sign in to comment.