Commit 39c0b22f authored by maruel@chromium.org's avatar maruel@chromium.org

Convert gclient to use subcommand.py

Update subcommand to support examples, always disable format_description and
format_epilog and add colors when enabled.

R=iannucci@chromium.org
BUG=

Review URL: https://chromiumcodereview.appspot.com/22824018

git-svn-id: svn://svn.chromium.org/chrome/trunk/tools/depot_tools@218180 0039d316-1c4b-4281-b951-d872f2087c98
parent 0633fb4f
This diff is collapsed.
......@@ -1077,6 +1077,8 @@ def CMDstatus(parser, args):
parser.add_option('-f', '--fast', action='store_true',
help='Do not retrieve review status')
(options, args) = parser.parse_args(args)
if args:
parser.error('Unsupported args: %s' % args)
if options.field:
cl = Changelist()
......@@ -1183,6 +1185,22 @@ def CMDstatus(parser, args):
return 0
def colorize_CMDstatus_doc():
"""To be called once in main() to add colors to git cl status help."""
colors = [i for i in dir(Fore) if i[0].isupper()]
def colorize_line(line):
for color in colors:
if color in line.upper():
# Extract whitespaces first and the leading '-'.
indent = len(line) - len(line.lstrip(' ')) + 1
return line[:indent] + getattr(Fore, color) + line[indent:] + Fore.RESET
return line
lines = CMDstatus.__doc__.splitlines()
CMDstatus.__doc__ = '\n'.join(colorize_line(l) for l in lines)
@subcommand.usage('[issue_number]')
def CMDissue(parser, args):
"""Sets or displays the current code review issue number.
......@@ -2156,13 +2174,6 @@ class OptionParser(optparse.OptionParser):
logging.basicConfig(level=levels[min(options.verbose, len(levels) - 1)])
return options, args
def format_description(self, _):
"""Disables automatic reformatting."""
lines = self.description.rstrip().splitlines()
lines_fixed = [lines[0]] + [l[2:] if len(l) >= 2 else l for l in lines[1:]]
description = ''.join(l + '\n' for l in lines_fixed)
return description[0].upper() + description[1:]
def main(argv):
if sys.hexversion < 0x02060000:
......@@ -2175,6 +2186,7 @@ def main(argv):
global settings
settings = Settings()
colorize_CMDstatus_doc()
dispatcher = subcommand.CommandDispatcher(__name__)
try:
return dispatcher.execute(OptionParser(), argv)
......
......@@ -41,6 +41,7 @@ Explanation:
import difflib
import sys
import textwrap
def usage(more):
......@@ -51,6 +52,17 @@ def usage(more):
return hook
def epilog(text):
"""Adds an 'epilog' property to a CMD function.
It will be shown in the epilog. Usually useful for examples.
"""
def hook(fn):
fn.epilog = text
return fn
return hook
def CMDhelp(parser, args):
"""Prints list of commands or help for a specific command."""
# This is the default help implementation. It can be disabled or overriden if
......@@ -62,6 +74,14 @@ def CMDhelp(parser, args):
assert False
def _get_color_module():
"""Returns the colorama module if available.
If so, assumes colors are supported and return the module handle.
"""
return sys.modules.get('colorama') or sys.modules.get('third_party.colorama')
class CommandDispatcher(object):
def __init__(self, module):
"""module is the name of the main python module where to look for commands.
......@@ -126,21 +146,57 @@ class CommandDispatcher(object):
return commands[hamming_commands[0][1]]
def _gen_commands_list(self):
"""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())
# Skip commands without a docstring.
docs = [i for i in docs if i[1]]
# Then calculate maximum length for alignment:
length = max(len(c) for c in commands)
# Look if color is supported.
colors = _get_color_module()
green = reset = ''
if colors:
green = colors.Fore.GREEN
reset = colors.Fore.RESET
return (
'Commands are:\n' +
''.join(
' %s%-*s%s %s\n' % (green, length, name, reset, doc)
for name, doc in docs))
def _add_command_usage(self, parser, command):
"""Modifies an OptionParser object with the function's documentation."""
name = command.__name__[3:]
more = getattr(command, 'usage_more', '')
if name == 'help':
name = '<command>'
# Use the module's docstring as the description for the 'help' command if
# available.
parser.description = self.module.__doc__
parser.description = (self.module.__doc__ or '').rstrip()
if parser.description:
parser.description += '\n\n'
parser.description += self._gen_commands_list()
# Do not touch epilog.
else:
# Use the command's docstring if available. For commands, unlike module
# docstring, realign.
lines = (command.__doc__ or '').rstrip().splitlines()
if lines[:1]:
rest = textwrap.dedent('\n'.join(lines[1:]))
parser.description = '\n'.join((lines[0], rest))
else:
# Use the command's docstring if available.
parser.description = command.__doc__
parser.description = (parser.description or '').strip()
parser.description = lines[0]
if parser.description:
parser.description += '\n'
parser.epilog = getattr(command, 'epilog', None)
if parser.epilog:
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))
......@@ -161,18 +217,11 @@ class CommandDispatcher(object):
Fallbacks to 'help' if not disabled.
"""
commands = self.enumerate_commands()
length = max(len(c) for c in commands)
# Lists all the commands in 'help'.
if commands['help']:
docs = sorted(
(name, self._create_command_summary(name, handler))
for name, handler in commands.iteritems())
# Skip commands without a docstring.
commands['help'].usage_more = (
'\n\nCommands are:\n' + '\n'.join(
' %-*s %s' % (length, name, doc) for name, doc in docs if doc))
# Unconditionally disable format_description() and format_epilog().
# Technically, a formatter should be used but it's not worth (yet) the
# trouble.
parser.format_description = lambda _: parser.description or ''
parser.format_epilog = lambda _: parser.epilog or ''
if args:
if args[0] in ('-h', '--help') and len(args) > 1:
......@@ -192,10 +241,11 @@ class CommandDispatcher(object):
self._add_command_usage(parser, command)
return command(parser, args[1:])
if commands['help']:
cmdhelp = self.enumerate_commands().get('help')
if cmdhelp:
# Not a known command. Default to help.
self._add_command_usage(parser, commands['help'])
return commands['help'](parser, args)
self._add_command_usage(parser, cmdhelp)
return cmdhelp(parser, args)
# Nothing can be done.
return 2
......@@ -401,13 +401,20 @@ class GClientSmokeSVN(GClientSmokeBase):
# TODO(maruel): safesync.
self.gclient(['config', self.svn_base + 'trunk/src/'])
# Test unversioned checkout.
# Use --jobs 1 otherwise the order is not deterministic.
self.parseGclient(
['sync', '--deps', 'mac', '--jobs', '8'],
['running', 'running',
['sync', '--deps', 'mac', '--jobs', '1'],
[
'running',
'running',
# This is due to the way svn update is called for a
# single file when File() is used in a DEPS file.
('running', os.path.join(self.root_dir, 'src', 'file', 'other')),
'running', 'running', 'running', 'running'],
'running',
'running',
'running',
'running',
],
untangle=True)
tree = self.mangle_svn_tree(
('trunk/src@2', 'src'),
......@@ -957,12 +964,18 @@ class GClientSmokeGIT(GClientSmokeBase):
# Test incremental versioned sync: sync backward.
expect3 = ('running',
os.path.join(self.root_dir, 'src', 'repo2', 'repo_renamed'))
# Use --jobs 1 otherwise the order is not deterministic.
self.parseGclient(
['sync', '--revision', 'src@' + self.githash('repo_1', 1),
'--deps', 'mac', '--delete_unversioned_trees', '--jobs', '8'],
['running', ('running', self.root_dir + '/src/repo4'),
'running', ('running', self.root_dir + '/src/repo2/repo3'),
expect3, 'deleting'],
'--deps', 'mac', '--delete_unversioned_trees', '--jobs', '1'],
[
'running',
('running', self.root_dir + '/src/repo2/repo3'),
'running',
('running', self.root_dir + '/src/repo4'),
expect3,
'deleting',
],
untangle=True)
tree = self.mangle_git_tree(('repo_1@1', 'src'),
('repo_2@2', 'src/repo2'),
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment