Bug 1381802 - [mach] Ensure subcommand help is displayed whenever a subcommand and --help are specified, r=gps
authorAndrew Halberstadt <ahalberstadt@mozilla.com>
Tue, 18 Jul 2017 08:42:41 -0400
changeset 418221 6d94dc258f8f289afc29bc49bedc47faedcf9140
parent 418220 4f418e6b759c5bdbc85f21559c528dfe9d2791c9
child 418222 58f0d7cf815080b7991a5c36f241a24753e8d333
push id7566
push usermtabara@mozilla.com
push dateWed, 02 Aug 2017 08:25:16 +0000
treeherdermozilla-beta@86913f512c3c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersgps
bugs1381802
milestone56.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1381802 - [mach] Ensure subcommand help is displayed whenever a subcommand and --help are specified, r=gps After this patch, the following will all display the subcommand help where they previously displayed the command help: $ mach help <command> <subcommand> $ mach <command> --help <subcommand> $ mach <command> <subcommand> --help The command help will still be shown for: $ mach help <command> $ mach <command> --help MozReview-Commit-ID: 7EsVblnCaFM
python/mach/mach/dispatcher.py
--- a/python/mach/mach/dispatcher.py
+++ b/python/mach/mach/dispatcher.py
@@ -98,30 +98,30 @@ class CommandAction(argparse.Action):
             self._handle_main_help(parser, namespace.verbose)
             sys.exit(0)
         elif values:
             command = values[0].lower()
             args = values[1:]
             if command == 'help':
                 if args and args[0] not in ['-h', '--help']:
                     # Make sure args[0] is indeed a command.
-                    self._handle_command_help(parser, args[0])
+                    self._handle_command_help(parser, args[0], args)
                 else:
                     self._handle_main_help(parser, namespace.verbose)
                 sys.exit(0)
             elif '-h' in args or '--help' in args:
                 # -h or --help is in the command arguments.
                 if '--' in args:
                     # -- is in command arguments
                     if '-h' in args[:args.index('--')] or '--help' in args[:args.index('--')]:
                         # Honor -h or --help only if it appears before --
-                        self._handle_command_help(parser, command)
+                        self._handle_command_help(parser, command, args)
                         sys.exit(0)
                 else:
-                    self._handle_command_help(parser, command)
+                    self._handle_command_help(parser, command, args)
                     sys.exit(0)
         else:
             raise NoCommandError()
 
         # First see if the this is a user-defined alias
         if command in self._context.settings.alias:
             alias = self._context.settings.alias[command]
             defaults = shlex.split(alias)
@@ -136,26 +136,21 @@ class CommandAction(argparse.Action):
 
         usage = '%(prog)s [global arguments] ' + command + \
             ' [command arguments]'
 
         subcommand = None
 
         # If there are sub-commands, parse the intent out immediately.
         if handler.subcommand_handlers and args:
-            if len(args) == 1 and args[0] in ('help', '--help'):
-                self._handle_subcommand_main_help(parser, handler)
+            # mach <command> help <subcommand>
+            if set(args).intersection(('help', '--help')):
+                self._handle_subcommand_help(parser, handler, args)
                 sys.exit(0)
-            # mach <command> help <subcommand>
-            elif len(args) == 2 and args[0] == 'help':
-                subcommand = args[1]
-                subhandler = handler.subcommand_handlers[subcommand]
-                self._handle_subcommand_help(parser, command, subcommand, subhandler)
-                sys.exit(0)
-            # We are running a sub command.
+            # mach <command> <subcommand> ...
             elif args[0] in handler.subcommand_handlers:
                 subcommand = args[0]
                 handler = handler.subcommand_handlers[subcommand]
                 usage = '%(prog)s [global arguments] ' + command + ' ' + \
                     subcommand + ' [command arguments]'
                 args.pop(0)
 
         # We create a new parser, populate it with the command's arguments,
@@ -299,24 +294,24 @@ class CommandAction(argparse.Action):
         for arg in handler.arguments:
             # Apply our group keyword.
             group_name = arg[1].get('group')
             if group_name:
                 del arg[1]['group']
                 group = extra_groups[group_name]
             group.add_argument(*arg[0], **arg[1])
 
-    def _handle_command_help(self, parser, command):
+    def _handle_command_help(self, parser, command, args):
         handler = self._mach_registrar.command_handlers.get(command)
 
         if not handler:
             raise UnknownCommandError(command, 'query')
 
         if handler.subcommand_handlers:
-            self._handle_subcommand_main_help(parser, handler)
+            self._handle_subcommand_help(parser, handler, args)
             return
 
         # This code is worth explaining. Because we are doing funky things with
         # argument registration to allow the same option in both global and
         # command arguments, we can't simply put all arguments on the same
         # parser instance because argparse would complain. We can't register an
         # argparse subparser here because it won't properly show help for
         # global arguments. So, we employ a strategy similar to command
@@ -381,29 +376,35 @@ class CommandAction(argparse.Action):
 
         if handler.docstring:
             parser.description = format_docstring(handler.docstring)
 
         parser.formatter_class = argparse.RawDescriptionHelpFormatter
 
         parser.print_help()
 
-    def _handle_subcommand_help(self, parser, command, subcommand, handler):
-        parser.usage = '%(prog)s [global arguments] ' + command + \
-            ' ' + subcommand + ' [command arguments]'
+    def _handle_subcommand_help(self, parser, handler, args):
+        subcommand = set(args).intersection(handler.subcommand_handlers.keys())
+        if not subcommand:
+            return self._handle_subcommand_main_help(parser, handler)
+
+        subcommand = subcommand.pop()
+        subhandler = handler.subcommand_handlers[subcommand]
 
         c_parser = argparse.ArgumentParser(add_help=False,
             formatter_class=CommandFormatter)
         group = c_parser.add_argument_group('Sub Command Arguments')
-        self._populate_command_group(c_parser, handler, group)
+        self._populate_command_group(c_parser, subhandler, group)
 
-        if handler.docstring:
-            parser.description = format_docstring(handler.docstring)
+        if subhandler.docstring:
+            parser.description = format_docstring(subhandler.docstring)
 
         parser.formatter_class = argparse.RawDescriptionHelpFormatter
+        parser.usage = '%(prog)s [global arguments] ' + handler.name + \
+            ' ' + subcommand + ' [command arguments]'
 
         parser.print_help()
         print('')
         c_parser.print_help()
 
     def _suggest_command(self, command):
         # Make sure we don't suggest any deprecated commands.
         names = [h.name for h in self._mach_registrar.command_handlers.values()