Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1055)

Unified Diff: subcommand.py

Issue 1040503003: Formalizes support for '-' in command names. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/depot_tools
Patch Set: Fix regression in new code which broke aliases Created 5 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: subcommand.py
diff --git a/subcommand.py b/subcommand.py
index b6f8e5cd5f348cd8cf515951c543507435dc58fb..0201c90aa35d61a17252fd4eeb1e227bd76375d3 100644
--- a/subcommand.py
+++ b/subcommand.py
@@ -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,32 +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.
"""
- # Implicitly replace foo-bar to foo_bar since foo-bar is not a valid python
- # symbol but it's faster to type.
- name = name.replace('-', '_')
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]]
@@ -137,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.
@@ -153,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:
@@ -169,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()
@@ -200,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('.')
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698