Changeset 51403 in webkit


Ignore:
Timestamp:
Nov 25, 2009 6:22:27 PM (14 years ago)
Author:
eric@webkit.org
Message:

2009-11-25 Eric Seidel <eric@webkit.org>

Reviewed by Adam Barth.

'bugzilla-tool help' should only show common commands like how 'git help' does
https://bugs.webkit.org/show_bug.cgi?id=31772

I also took this opportunity to make 'help' a real Command.
Making 'help' a real command required adding Command.tool (which we've wanted to do for a while).

  • Scripts/bugzilla-tool:
    • change should_show_command_help to should_show_in_main_help
  • Scripts/modules/commands/download.py:
    • Mark commands as being shown in main help or not.
    • show_in_main_help = False is not required (default is false), but it seemed to make the commands more self-documenting.
  • Scripts/modules/commands/queries.py: ditto
  • Scripts/modules/commands/queues.py: ditto
  • Scripts/modules/commands/upload.py: ditto
  • Scripts/modules/multicommandtool.py:
    • Make Command hold a pointer to tool in self.tool. Most Command subclasses do not take advantage of this yet, but it was required for HelpCommand to be able to reach the tool from _help_epilog().
    • Move MultiCommandTool._standalone_help_for_command to Command.standalone_help
    • Move MultiCommandTool._help_epilog to Command._help_epilog
    • Move "help" logic into HelpCommand.execute()
    • Change should_show_command_help to should_show_in_main_help and add a default implementation.
  • Scripts/modules/multicommandtool_unittest.py:
    • Test hiding of Commands in --help, and that all commands are shown in 'help --all-commands'
Location:
trunk/WebKitTools
Files:
8 edited

Legend:

Unmodified
Added
Removed
  • trunk/WebKitTools/ChangeLog

    r51399 r51403  
     12009-11-25  Eric Seidel  <eric@webkit.org>
     2
     3        Reviewed by Adam Barth.
     4
     5        'bugzilla-tool help' should only show common commands like how 'git help' does
     6        https://bugs.webkit.org/show_bug.cgi?id=31772
     7
     8        I also took this opportunity to make 'help' a real Command.
     9        Making 'help' a real command required adding Command.tool (which we've wanted to do for a while).
     10
     11        * Scripts/bugzilla-tool:
     12         - change should_show_command_help to should_show_in_main_help
     13        * Scripts/modules/commands/download.py:
     14         - Mark commands as being shown in main help or not.
     15         - show_in_main_help = False is not required (default is false),
     16           but it seemed to make the commands more self-documenting.
     17        * Scripts/modules/commands/queries.py: ditto
     18        * Scripts/modules/commands/queues.py: ditto
     19        * Scripts/modules/commands/upload.py: ditto
     20        * Scripts/modules/multicommandtool.py:
     21         - Make Command hold a pointer to tool in self.tool.  Most Command
     22           subclasses do not take advantage of this yet, but it was required
     23           for HelpCommand to be able to reach the tool from _help_epilog().
     24         - Move MultiCommandTool._standalone_help_for_command to Command.standalone_help
     25         - Move MultiCommandTool._help_epilog to Command._help_epilog
     26         - Move "help" logic into HelpCommand.execute()
     27         - Change should_show_command_help to should_show_in_main_help and add a default implementation.
     28        * Scripts/modules/multicommandtool_unittest.py:
     29         - Test hiding of Commands in --help, and that all commands are shown in 'help --all-commands'
     30
    1312009-11-25  Brian Weinstein  <bweinstein@apple.com>
    232
  • trunk/WebKitTools/Scripts/bugzilla-tool

    r51287 r51403  
    7272        return __file__
    7373
    74     def should_show_command_help(self, command):
     74    def should_show_in_main_help(self, command):
     75        if not command.show_in_main_help:
     76            return False
    7577        if command.requires_local_commits:
    7678            return self.scm().supports_local_commits()
  • trunk/WebKitTools/Scripts/modules/commands/download.py

    r51383 r51403  
    6868class Build(Command):
    6969    name = "build"
     70    show_in_main_help = False
    7071    def __init__(self):
    7172        options = WebKitLandingScripts.cleaning_options()
     
    8182class ApplyAttachment(Command):
    8283    name = "apply-attachment"
     84    show_in_main_help = True
    8385    def __init__(self):
    8486        options = WebKitApplyingScripts.apply_options() + WebKitLandingScripts.cleaning_options()
     
    9496class ApplyPatches(Command):
    9597    name = "apply-patches"
     98    show_in_main_help = True
    9699    def __init__(self):
    97100        options = WebKitApplyingScripts.apply_options() + WebKitLandingScripts.cleaning_options()
     
    160163class LandDiff(Command):
    161164    name = "land-diff"
     165    show_in_main_help = True
    162166    def __init__(self):
    163167        options = [
     
    256260class CheckStyle(AbstractPatchProcessingCommand):
    257261    name = "check-style"
     262    show_in_main_help = False
    258263    def __init__(self):
    259264        options = WebKitLandingScripts.cleaning_options()
     
    285290class BuildAttachment(AbstractPatchProcessingCommand):
    286291    name = "build-attachment"
     292    show_in_main_help = False
    287293    def __init__(self):
    288294        options = WebKitLandingScripts.cleaning_options()
     
    320326class LandAttachment(AbstractPatchLandingCommand):
    321327    name = "land-attachment"
     328    show_in_main_help = True
    322329    def __init__(self):
    323330        AbstractPatchLandingCommand.__init__(self, "Land patches from bugzilla, optionally building and testing them first", "ATTACHMENT_ID [ATTACHMENT_IDS]")
     
    329336class LandPatches(AbstractPatchLandingCommand):
    330337    name = "land-patches"
     338    show_in_main_help = True
    331339    def __init__(self):
    332340        AbstractPatchLandingCommand.__init__(self, "Land all patches on the given bugs, optionally building and testing them first", "BUGID [BUGIDS]")
     
    343351class Rollout(Command):
    344352    name = "rollout"
     353    show_in_main_help = True
    345354    def __init__(self):
    346355        options = WebKitLandingScripts.cleaning_options()
  • trunk/WebKitTools/Scripts/modules/commands/queries.py

    r51287 r51403  
    5757class BugsToCommit(Command):
    5858    name = "bugs-to-commit"
     59    show_in_main_help = False
    5960    def __init__(self):
    6061        Command.__init__(self, "List bugs in the commit-queue")
     
    6869class PatchesToCommit(Command):
    6970    name = "patches-to-commit"
     71    show_in_main_help = False
    7072    def __init__(self):
    7173        Command.__init__(self, "List patches in the commit-queue")
     
    8082class ReviewedPatches(Command):
    8183    name = "reviewed-patches"
     84    show_in_main_help = False
    8285    def __init__(self):
    8386        Command.__init__(self, "List r+'d patches on a bug", "BUGID")
     
    9295class TreeStatus(Command):
    9396    name = "tree-status"
     97    show_in_main_help = True
    9498    def __init__(self):
    9599        Command.__init__(self, "Print the status of the %s buildbots" % BuildBot.default_host)
  • trunk/WebKitTools/Scripts/modules/commands/queues.py

    r51287 r51403  
    111111class CommitQueue(AbstractQueue):
    112112    name = "commit-queue"
     113    show_in_main_help = False
    113114    def __init__(self):
    114115        AbstractQueue.__init__(self)
     
    167168class StyleQueue(AbstractTryQueue):
    168169    name = "style-queue"
     170    show_in_main_help = False
    169171    def __init__(self):
    170172        AbstractTryQueue.__init__(self)
     
    179181class BuildQueue(AbstractTryQueue):
    180182    name = "build-queue"
     183    show_in_main_help = False
    181184    def __init__(self):
    182185        options = WebKitPort.port_options()
  • trunk/WebKitTools/Scripts/modules/commands/upload.py

    r51383 r51403  
    5757class CommitMessageForCurrentDiff(Command):
    5858    name = "commit-message"
     59    show_in_main_help = False
    5960    def __init__(self):
    6061        Command.__init__(self, "Print a commit message suitable for the uncommitted changes")
     
    6768class ObsoleteAttachments(Command):
    6869    name = "obsolete-attachments"
     70    show_in_main_help = False
    6971    def __init__(self):
    7072        Command.__init__(self, "Mark all attachments on a bug as obsolete", "BUGID")
     
    8082class PostDiff(Command):
    8183    name = "post-diff"
     84    show_in_main_help = True
    8285    def __init__(self):
    8386        options = [
     
    121124class PostCommits(Command):
    122125    name = "post-commits"
     126    show_in_main_help = True
    123127    def __init__(self):
    124128        options = [
     
    169173class CreateBug(Command):
    170174    name = "create-bug"
     175    show_in_main_help = True
    171176    def __init__(self):
    172177        options = [
  • trunk/WebKitTools/Scripts/modules/multicommandtool.py

    r51383 r51403  
    4141class Command(object):
    4242    name = None
     43    # show_in_main_help = False # Subclasses must define show_in_main_help, we leave it out here to enforce that.
    4344    def __init__(self, help_text, argument_names=None, options=None, requires_local_commits=False):
    4445        self.help_text = help_text
     
    4849        self.option_parser = HelpPrintingOptionParser(usage=SUPPRESS_USAGE, add_help_option=False, option_list=self.options)
    4950        self.requires_local_commits = requires_local_commits
     51        self.tool = None
     52
     53    # The tool calls bind_to_tool on each Command after adding it to its list.
     54    def bind_to_tool(self, tool):
     55        # Command instances can only be bound to one tool at a time.
     56        if self.tool and tool != self.tool:
     57            raise Exception("Command already bound to tool!")
     58        self.tool = tool
    5059
    5160    @staticmethod
     
    9099        return self.execute(command_options, command_args, tool) or 0
    91100
     101    def standalone_help(self):
     102        help_text = self.name_with_arguments().ljust(len(self.name_with_arguments()) + 3) + self.help_text + "\n"
     103        help_text += self.option_parser.format_option_help(IndentedHelpFormatter())
     104        return help_text
     105
    92106    def execute(self, options, args, tool):
    93107        raise NotImplementedError, "subclasses must implement"
     
    102116        self.print_usage(sys.stderr)
    103117        error_message = "%s: error: %s\n" % (self.get_prog_name(), msg)
    104         error_message += "\nType \"" + self.get_prog_name() + " --help\" to see usage.\n"
     118        error_message += "\nType \"%s --help\" to see usage.\n" % self.get_prog_name()
    105119        self.exit(1, error_message)
    106120
     
    113127
    114128
     129class HelpCommand(Command):
     130    name = "help"
     131    show_in_main_help = False
     132
     133    def __init__(self):
     134        options = [
     135            make_option("-a", "--all-commands", action="store_true", dest="show_all_commands", help="Print all available commands"),
     136        ]
     137        Command.__init__(self, "Display information about this program or its subcommands", "[COMMAND]", options=options)
     138        self.show_all_commands = False # A hack used to pass --all-commands to _help_epilog even though it's called by the OptionParser.
     139
     140    def _help_epilog(self):
     141        # Only show commands which are relevant to this checkout's SCM system.  Might this be confusing to some users?
     142        if self.show_all_commands:
     143            epilog = "All %prog commands:\n"
     144            relevant_commands = self.tool.commands[:]
     145        else:
     146            epilog = "Common %prog commands:\n"
     147            relevant_commands = filter(self.tool.should_show_in_main_help, self.tool.commands)
     148        longest_name_length = max(map(lambda command: len(command.name), relevant_commands))
     149        relevant_commands.sort(lambda a, b: cmp(a.name, b.name))
     150        command_help_texts = map(lambda command: "   %s   %s\n" % (command.name.ljust(longest_name_length), command.help_text), relevant_commands)
     151        epilog += "%s\n" % "".join(command_help_texts)
     152        epilog += "See '%prog help --all-commands' to list all commands.\n"
     153        epilog += "See '%prog help COMMAND' for more information on a specific command.\n"
     154        return self.tool.global_option_parser.expand_prog_name(epilog)
     155
     156    def execute(self, options, args, tool):
     157        if args:
     158            command = self.tool.command_by_name(args[0])
     159            if command:
     160                print command.standalone_help()
     161                return 0
     162
     163        self.show_all_commands = options.show_all_commands
     164        tool.global_option_parser.print_help()
     165        return 0
     166
     167
    115168class MultiCommandTool(object):
    116169    def __init__(self, name=None, commands=None):
    117170        # Allow the unit tests to disable command auto-discovery.
    118171        self.commands = commands or [cls() for cls in self._find_all_commands() if cls.name]
    119         self.global_option_parser = HelpPrintingOptionParser(epilog_method=self._help_epilog, prog=name, usage=self._usage_line())
     172        self.help_command = self.command_by_name(HelpCommand.name)
     173        # Require a help command, even if the manual test list doesn't include one.
     174        if not self.help_command:
     175            self.help_command = HelpCommand()
     176            self.commands.append(self.help_command)
     177        for command in self.commands:
     178            command.bind_to_tool(self)
     179        self.global_option_parser = HelpPrintingOptionParser(epilog_method=self.help_command._help_epilog, prog=name, usage=self._usage_line())
    120180
    121181    @classmethod
     
    136196        return "Usage: %prog [options] COMMAND [ARGS]"
    137197
    138     @classmethod
    139     def _standalone_help_for_command(cls, command):
    140         help_text = command.name_with_arguments().ljust(len(command.name_with_arguments()) + 3) + command.help_text + "\n"
    141         help_text += command.option_parser.format_option_help(IndentedHelpFormatter())
    142         return help_text
    143 
    144198    def name(self):
    145199        return self.global_option_parser.get_prog_name()
    146 
    147     def _help_epilog(self):
    148         # Only show commands which are relevant to this checkout's SCM system.  Might this be confusing to some users?
    149         relevant_commands = filter(self.should_show_command_help, self.commands)
    150         longest_name_length = max(map(lambda command: len(command.name), relevant_commands))
    151         relevant_commands.sort(lambda a, b: cmp(a.name, b.name))
    152         command_help_texts = map(lambda command: "   %s   %s\n" % (command.name.ljust(longest_name_length), command.help_text), relevant_commands)
    153         epilog = "%prog supports the following commands:\n"
    154         epilog += "%s\n" % "".join(command_help_texts)
    155         epilog += "See '%prog help COMMAND' for more information on a specific command.\n"
    156         return self.global_option_parser.expand_prog_name(epilog)
    157200
    158201    def handle_global_args(self, args):
     
    187230        raise NotImplementedError, "subclasses must implement"
    188231
    189     def should_show_command_help(self, command):
    190         raise NotImplementedError, "subclasses must implement"
     232    def should_show_in_main_help(self, command):
     233        return command.show_in_main_help
    191234
    192235    def should_execute_command(self, command):
     
    199242        self.handle_global_args(global_args)
    200243
    201         if not command_name:
    202             self.global_option_parser.error("No command specified")
    203 
    204         if command_name == "help":
    205             if args_after_command_name:
    206                 command = self.command_by_name(args_after_command_name[0])
    207                 log(self._standalone_help_for_command(command))
    208             else:
    209                 self.global_option_parser.print_help()
    210             return 0
    211 
    212         command = self.command_by_name(command_name)
     244        command = self.command_by_name(command_name) or self.help_command
    213245        if not command:
    214246            self.global_option_parser.error("%s is not a recognized command" % command_name)
  • trunk/WebKitTools/Scripts/modules/multicommandtool_unittest.py

    r51383 r51403  
    3636class TrivialCommand(Command):
    3737    name = "trivial"
     38    show_in_main_help = True
    3839    def __init__(self, **kwargs):
    3940        Command.__init__(self, "help text", **kwargs)
     
    4243        pass
    4344
     45class UncommonCommand(TrivialCommand):
     46    name = "uncommon"
     47    show_in_main_help = False
    4448
    4549class CommandTest(unittest.TestCase):
     
    7781        return __file__
    7882
    79     def should_show_command_help(self, command):
    80         return True
    81 
    8283    def should_execute_command(self, command):
    83         return True
     84        return (True, None)
    8485
    8586
     
    108109        self.assertEqual(tool.command_by_name("bar"), None)
    109110
     111    def _assert_tool_main_outputs(self, tool, main_args, expected_stdout, expected_stderr = "", exit_code=0):
     112        capture = OutputCapture()
     113        capture.capture_output()
     114        exit_code = tool.main(main_args)
     115        (stdout_string, stderr_string) = capture.restore_output()
     116        self.assertEqual(stdout_string, expected_stdout)
     117        self.assertEqual(expected_stderr, expected_stderr)
     118
     119    def test_global_help(self):
     120        tool = TrivialTool(commands=[TrivialCommand(), UncommonCommand()])
     121        expected_common_commands_help = """Usage: trivial-tool [options] COMMAND [ARGS]
     122
     123Options:
     124  -h, --help  show this help message and exit
     125
     126Common trivial-tool commands:
     127   trivial   help text
     128
     129See 'trivial-tool help --all-commands' to list all commands.
     130See 'trivial-tool help COMMAND' for more information on a specific command.
     131
     132"""
     133        self._assert_tool_main_outputs(tool, ["tool", "help"], expected_common_commands_help)
     134        expected_all_commands_help = """Usage: trivial-tool [options] COMMAND [ARGS]
     135
     136Options:
     137  -h, --help  show this help message and exit
     138
     139All trivial-tool commands:
     140   help       Display information about this program or its subcommands
     141   trivial    help text
     142   uncommon   help text
     143
     144See 'trivial-tool help --all-commands' to list all commands.
     145See 'trivial-tool help COMMAND' for more information on a specific command.
     146
     147"""
     148        self._assert_tool_main_outputs(tool, ["tool", "help", "--all-commands"], expected_all_commands_help)
     149
    110150    def test_command_help(self):
    111151        command_with_options = TrivialCommand(options=[make_option("--my_option")])
    112152        tool = TrivialTool(commands=[command_with_options])
    113 
    114         capture = OutputCapture()
    115         capture.capture_output()
    116         exit_code = tool.main(["tool", "help", "trivial"])
    117         (stdout_string, stderr_string) = capture.restore_output()
    118153        expected_subcommand_help = "trivial [options]   help text\nOptions:\n  --my_option=MY_OPTION\n\n"
    119         self.assertEqual(exit_code, 0)
    120         self.assertEqual(stdout_string, "")
    121         self.assertEqual(stderr_string, expected_subcommand_help)
     154        self._assert_tool_main_outputs(tool, ["tool", "help", "trivial"], expected_subcommand_help)
    122155
    123156
Note: See TracChangeset for help on using the changeset viewer.