| Index: client/third_party/depot_tools/subcommand.py
|
| diff --git a/client/third_party/depot_tools/subcommand.py b/client/third_party/depot_tools/subcommand.py
|
| index a87fa66d5ae7e26aa4c07e14b95ebadd236bd7d9..0201c90aa35d61a17252fd4eeb1e227bd76375d3 100644
|
| --- a/client/third_party/depot_tools/subcommand.py
|
| +++ b/client/third_party/depot_tools/subcommand.py
|
| @@ -1,6 +1,6 @@
|
| # Copyright 2013 The Chromium Authors. All rights reserved.
|
| -# Use of this source code is governed under the Apache License, Version 2.0 that
|
| -# can be found in the LICENSE file.
|
| +# Use of this source code is governed by a BSD-style license that can be
|
| +# found in the LICENSE file.
|
|
|
| """Manages subcommands in a script.
|
|
|
| @@ -37,6 +37,7 @@ Explanation:
|
| will result in oldname not being documented but supported and redirecting to
|
| newcmd. Make it a real function that calls the old function if you want it
|
| to be documented.
|
| + - CMDfoo_bar will be command 'foo-bar'.
|
| """
|
|
|
| import difflib
|
| @@ -82,6 +83,11 @@ def _get_color_module():
|
| return sys.modules.get('colorama') or sys.modules.get('third_party.colorama')
|
|
|
|
|
| +def _function_to_name(name):
|
| + """Returns the name of a CMD function."""
|
| + return name[3:].replace('_', '-')
|
| +
|
| +
|
| class CommandDispatcher(object):
|
| def __init__(self, module):
|
| """module is the name of the main python module where to look for commands.
|
| @@ -103,29 +109,31 @@ class CommandDispatcher(object):
|
|
|
| Automatically adds 'help' if not already defined.
|
|
|
| + Normalizes '_' in the commands to '-'.
|
| +
|
| A command can be effectively disabled by defining a global variable to None,
|
| e.g.:
|
| CMDhelp = None
|
| """
|
| cmds = dict(
|
| - (fn[3:], getattr(self.module, fn))
|
| - for fn in dir(self.module) if fn.startswith('CMD'))
|
| + (_function_to_name(name), getattr(self.module, name))
|
| + for name in dir(self.module) if name.startswith('CMD'))
|
| cmds.setdefault('help', CMDhelp)
|
| return cmds
|
|
|
| - def find_nearest_command(self, name):
|
| - """Retrieves the function to handle a command.
|
| + def find_nearest_command(self, name_asked):
|
| + """Retrieves the function to handle a command as supplied by the user.
|
|
|
| - It automatically tries to guess the intended command by handling typos or
|
| - incomplete names.
|
| + It automatically tries to guess the _intended command_ by handling typos
|
| + and/or incomplete names.
|
| """
|
| commands = self.enumerate_commands()
|
| - if name in commands:
|
| - return commands[name]
|
| + if name_asked in commands:
|
| + return commands[name_asked]
|
|
|
| # An exact match was not found. Try to be smart and look if there's
|
| # something similar.
|
| - commands_with_prefix = [c for c in commands if c.startswith(name)]
|
| + commands_with_prefix = [c for c in commands if c.startswith(name_asked)]
|
| if len(commands_with_prefix) == 1:
|
| return commands[commands_with_prefix[0]]
|
|
|
| @@ -134,7 +142,7 @@ class CommandDispatcher(object):
|
| return difflib.SequenceMatcher(a=a, b=b).ratio()
|
|
|
| hamming_commands = sorted(
|
| - ((close_enough(c, name), c) for c in commands),
|
| + ((close_enough(c, name_asked), c) for c in commands),
|
| reverse=True)
|
| if (hamming_commands[0][0] - hamming_commands[1][0]) < 0.3:
|
| # Too ambiguous.
|
| @@ -150,8 +158,8 @@ class CommandDispatcher(object):
|
| """Generates the short list of supported commands."""
|
| commands = self.enumerate_commands()
|
| docs = sorted(
|
| - (name, self._create_command_summary(name, handler))
|
| - for name, handler in commands.iteritems())
|
| + (cmd_name, self._create_command_summary(cmd_name, handler))
|
| + for cmd_name, handler in commands.iteritems())
|
| # Skip commands without a docstring.
|
| docs = [i for i in docs if i[1]]
|
| # Then calculate maximum length for alignment:
|
| @@ -166,14 +174,14 @@ class CommandDispatcher(object):
|
| return (
|
| 'Commands are:\n' +
|
| ''.join(
|
| - ' %s%-*s%s %s\n' % (green, length, name, reset, doc)
|
| - for name, doc in docs))
|
| + ' %s%-*s%s %s\n' % (green, length, cmd_name, reset, doc)
|
| + for cmd_name, doc in docs))
|
|
|
| def _add_command_usage(self, parser, command):
|
| """Modifies an OptionParser object with the function's documentation."""
|
| - name = command.__name__[3:]
|
| - if name == 'help':
|
| - name = '<command>'
|
| + cmd_name = _function_to_name(command.__name__)
|
| + if cmd_name == 'help':
|
| + cmd_name = '<command>'
|
| # Use the module's docstring as the description for the 'help' command if
|
| # available.
|
| parser.description = (self.module.__doc__ or '').rstrip()
|
| @@ -189,7 +197,7 @@ class CommandDispatcher(object):
|
| rest = textwrap.dedent('\n'.join(lines[1:]))
|
| parser.description = '\n'.join((lines[0], rest))
|
| else:
|
| - parser.description = lines[0]
|
| + parser.description = lines[0] if lines else ''
|
| if parser.description:
|
| parser.description += '\n'
|
| parser.epilog = getattr(command, 'epilog', None)
|
| @@ -197,14 +205,15 @@ class CommandDispatcher(object):
|
| parser.epilog = '\n' + parser.epilog.strip() + '\n'
|
|
|
| more = getattr(command, 'usage_more', '')
|
| - parser.set_usage(
|
| - 'usage: %%prog %s [options]%s' % (name, '' if not more else ' ' + more))
|
| + extra = '' if not more else ' ' + more
|
| + parser.set_usage('usage: %%prog %s [options]%s' % (cmd_name, extra))
|
|
|
| @staticmethod
|
| - def _create_command_summary(name, command):
|
| - """Creates a oneline summary from the command's docstring."""
|
| - if name != command.__name__[3:]:
|
| - # Skip aliases.
|
| + def _create_command_summary(cmd_name, command):
|
| + """Creates a oneliner summary from the command's docstring."""
|
| + if cmd_name != _function_to_name(command.__name__):
|
| + # Skip aliases. For example using at module level:
|
| + # CMDfoo = CMDbar
|
| return ''
|
| doc = command.__doc__ or ''
|
| line = doc.split('\n', 1)[0].rstrip('.')
|
|
|