Chromium Code Reviews| Index: subcommand.py |
| diff --git a/subcommand.py b/subcommand.py |
| index 2f58a209ee9e80484b0284b7c3722a44e341bdfa..6adeddd1d407aa65d00422f85f005b9549f03985 100644 |
| --- a/subcommand.py |
| +++ b/subcommand.py |
| @@ -51,6 +51,17 @@ def usage(more): |
| return hook |
| +def example(more): |
| + """Adds a 'example' property to a CMD function. |
| + |
| + It will be shown in the epilog. |
| + """ |
| + def hook(fn): |
| + fn.example = more |
| + 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 +73,14 @@ def CMDhelp(parser, args): |
| assert False |
| +def _is_color_enabled(): |
|
iannucci
2013/08/16 21:32:05
_get_color_module() -> colorama or None
M-A Ruel
2013/08/17 00:19:10
Done.
|
| + """Looks if a module named colorama was imported. |
| + |
| + 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 +145,64 @@ 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 = _is_color_enabled() |
| + 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() |
|
M-A Ruel
2013/08/16 21:08:05
The following is formatting fine tuning so the out
|
| + 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. |
| - parser.description = command.__doc__ |
| - parser.description = (parser.description or '').strip() |
| - if parser.description: |
| - parser.description += '\n' |
| + # Use the command's docstring if available. For commands, unlike module |
| + # docstring, realign. |
| + lines = (command.__doc__ or '').rstrip().splitlines() |
|
iannucci
2013/08/16 21:32:05
you may want to just use textwrap.dedent()
M-A Ruel
2013/08/17 00:19:10
Done.
|
| + # Determine alignment automatically. |
| + alignment = 0 |
| + for i in lines[1:]: |
| + if not i: |
| + continue |
| + if i: |
| + # Cheezy way to count the leading number of whitespaces. |
| + alignment = len(i) - len(i.lstrip(' ')) |
| + break |
| + lines_fixed = [lines[0]] + [ |
| + l[alignment:] if len(l) >= alignment else l for l in lines[1:]] |
| + parser.description = '\n'.join(lines_fixed) |
| + if parser.description: |
| + parser.description += '\n' |
| + parser.epilog = getattr(command, 'example', None) |
|
iannucci
2013/08/16 21:32:05
let's call it .example instead, and then add 'Exam
M-A Ruel
2013/08/17 00:19:10
I've decided to switch to naming it epilog, so the
|
| + 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 +223,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 +247,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 |