Changeset 51237 in webkit


Ignore:
Timestamp:
Nov 20, 2009 6:54:34 AM (14 years ago)
Author:
abarth@webkit.org
Message:

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

Reviewed by Adam Barth.

MultiCommandTool should find Command objects automatically instead of with a manual list
https://bugs.webkit.org/show_bug.cgi?id=31710

  • Scripts/bugzilla-tool:
  • Scripts/modules/multicommandtool.py:
    • Use some wild python-fu to crawl all the known subclasses of Command.
Location:
trunk/WebKitTools
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • trunk/WebKitTools/ChangeLog

    r51236 r51237  
     12009-11-20  Eric Seidel  <eric@webkit.org>
     2
     3        Reviewed by Adam Barth.
     4
     5        MultiCommandTool should find Command objects automatically instead of with a manual list
     6        https://bugs.webkit.org/show_bug.cgi?id=31710
     7
     8        * Scripts/bugzilla-tool:
     9        * Scripts/modules/multicommandtool.py:
     10         - Use some wild python-fu to crawl all the known subclasses of Command.
     11
    1122009-11-20  Adam Barth  <abarth@webkit.org>
    213
  • trunk/WebKitTools/Scripts/bugzilla-tool

    r51236 r51237  
    7070
    7171class BugsToCommit(Command):
     72    name = "bugs-to-commit"
    7273    def __init__(self):
    7374        Command.__init__(self, "Bugs in the commit queue")
     
    8081
    8182class PatchesToCommit(Command):
     83    name = "patches-to-commit"
    8284    def __init__(self):
    8385        Command.__init__(self, "Patches in the commit queue")
     
    9193
    9294class ReviewedPatches(Command):
    93     def __init__(self):
    94         Command.__init__(self, "r+\'d patches on a bug", "BUGID")
     95    name = "reviewed-patches"
     96    def __init__(self):
     97        Command.__init__(self, "r+'d patches on a bug", "BUGID")
    9598
    9699    def execute(self, options, args, tool):
     
    102105
    103106class CheckStyle(Command):
     107    name = "check-style"
    104108    def __init__(self):
    105109        options = WebKitLandingScripts.cleaning_options()
     
    131135
    132136class ApplyAttachment(Command):
     137    name = "apply-attachment"
    133138    def __init__(self):
    134139        options = WebKitApplyingScripts.apply_options() + WebKitLandingScripts.cleaning_options()
     
    143148
    144149class ApplyPatches(Command):
     150    name = "apply-patches"
    145151    def __init__(self):
    146152        options = WebKitApplyingScripts.apply_options() + WebKitLandingScripts.cleaning_options()
     
    211217
    212218class LandDiff(Command):
     219    name = "land-diff"
    213220    def __init__(self):
    214221        options = [
     
    260267
    261268class AbstractPatchProcessingCommand(Command):
    262     def __init__(self, description, args_description, options):
    263         Command.__init__(self, description, args_description, options=options)
     269    def __init__(self, help_text, args_description, options):
     270        Command.__init__(self, help_text, args_description, options=options)
    264271
    265272    def _fetch_list_of_patches_to_process(self, options, args, tool):
     
    293300
    294301class AbstractPatchLandingCommand(AbstractPatchProcessingCommand):
    295     def __init__(self, description, args_description):
     302    def __init__(self, help_text, args_description):
    296303        options = WebKitLandingScripts.cleaning_options() + WebKitLandingScripts.land_options()
    297         AbstractPatchProcessingCommand.__init__(self, description, args_description, options)
     304        AbstractPatchProcessingCommand.__init__(self, help_text, args_description, options)
    298305
    299306    def _prepare_to_process(self, options, args, tool):
     
    307314
    308315class LandAttachment(AbstractPatchLandingCommand):
     316    name = "land-attachment"
    309317    def __init__(self):
    310318        AbstractPatchLandingCommand.__init__(self, "Lands a patches from bugzilla, optionally building and testing them first", "ATTACHMENT_ID [ATTACHMENT_IDS]")
     
    315323
    316324class LandPatches(AbstractPatchLandingCommand):
     325    name = "land-patches"
    317326    def __init__(self):
    318327        AbstractPatchLandingCommand.__init__(self, "Lands all patches on the given bugs, optionally building and testing them first", "BUGID [BUGIDS]")
     
    328337
    329338class CommitMessageForCurrentDiff(Command):
     339    name = "commit-message"
    330340    def __init__(self):
    331341        Command.__init__(self, "Prints a commit message suitable for the uncommitted changes.")
     
    337347
    338348class ObsoleteAttachments(Command):
     349    name = "obsolete-attachments"
    339350    def __init__(self):
    340351        Command.__init__(self, "Marks all attachments on a bug as obsolete.", "BUGID")
     
    349360
    350361class PostDiff(Command):
     362    name = "post-diff"
    351363    def __init__(self):
    352364        options = [
     
    389401
    390402class PostCommits(Command):
     403    name = "post-commits"
    391404    def __init__(self):
    392405        options = [
     
    439452
    440453class Rollout(Command):
     454    name = "rollout"
    441455    def __init__(self):
    442456        options = WebKitLandingScripts.land_options()
     
    496510
    497511class CreateBug(Command):
     512    name = "create-bug"
    498513    def __init__(self):
    499514        options = [
     
    573588
    574589class TreeStatus(Command):
     590    name = "tree-status"
    575591    def __init__(self):
    576592        Command.__init__(self, "Print out the status of the webkit builders.")
     
    701717        # so that we don't need to create 'epilog' before constructing HelpPrintingOptionParser.
    702718        self.cached_scm = None
    703        
    704         # FIXME: Commands should know their own name and register themselves with the BugzillaTool instead of having a manual list.
    705         MultiCommandTool.__init__(self, commands=[
    706             { "name" : "bugs-to-commit", "object" : BugsToCommit() },
    707             { "name" : "patches-to-commit", "object" : PatchesToCommit() },
    708             { "name" : "reviewed-patches", "object" : ReviewedPatches() },
    709             { "name" : "create-bug", "object" : CreateBug() },
    710             { "name" : "apply-attachment", "object" : ApplyAttachment() },
    711             { "name" : "apply-patches", "object" : ApplyPatches() },
    712             { "name" : "land-diff", "object" : LandDiff() },
    713             { "name" : "land-attachment", "object" : LandAttachment() },
    714             { "name" : "land-patches", "object" : LandPatches() },
    715             { "name" : "check-style", "object" : CheckStyle() },
    716             { "name" : "commit-message", "object" : CommitMessageForCurrentDiff() },
    717             { "name" : "obsolete-attachments", "object" : ObsoleteAttachments() },
    718             { "name" : "post-diff", "object" : PostDiff() },
    719             { "name" : "post-commits", "object" : PostCommits() },
    720             { "name" : "tree-status", "object" : TreeStatus() },
    721             { "name" : "commit-queue", "object" : CommitQueue() },
    722             { "name" : "style-queue", "object" : StyleQueue() },
    723             { "name" : "rollout", "object" : Rollout() },
    724         ])
     719        MultiCommandTool.__init__(self)
    725720        self.global_option_parser.add_option("--dry-run", action="callback", help="do not touch remote servers", callback=self.dry_run_callback)
    726721
     
    750745
    751746    def should_show_command_help(self, command):
    752         if command["object"].requires_local_commits:
     747        if command.requires_local_commits:
    753748            return self.scm().supports_local_commits()
    754749        return True
    755750
    756751    def should_execute_command(self, command):
    757         if command["object"].requires_local_commits and not self.scm().supports_local_commits():
    758             failure_reason = "%s requires local commits using %s in %s." % (command["name"], self.scm().display_name(), self.scm().checkout_root)
     752        if command.requires_local_commits and not self.scm().supports_local_commits():
     753            failure_reason = "%s requires local commits using %s in %s." % (command.name, self.scm().display_name(), self.scm().checkout_root)
    759754            return (False, failure_reason)
    760755        return (True, None)
  • trunk/WebKitTools/Scripts/modules/multicommandtool.py

    r51226 r51237  
    3838from modules.logging import log
    3939
    40 class Command:
     40class Command(object):
     41    name = None
    4142    def __init__(self, help_text, argument_names=None, options=None, requires_local_commits=False):
    4243        self.help_text = help_text
     
    4647        self.requires_local_commits = requires_local_commits
    4748
    48     def name_with_arguments(self, command_name):
    49         usage_string = command_name
     49    def name_with_arguments(self):
     50        usage_string = self.name
    5051        if self.options:
    5152            usage_string += " [options]"
     
    5960    def execute(self, options, args, tool):
    6061        raise NotImplementedError, "subclasses must implement"
    61 
    6262
    6363class NonWrappingEpilogIndentedHelpFormatter(IndentedHelpFormatter):
     
    7777
    7878
    79 class MultiCommandTool:
    80     def __init__(self, commands):
    81         self.commands = commands
     79class MultiCommandTool(object):
     80    def __init__(self):
     81        self.commands = [cls() for cls in self._find_all_commands() if cls.name]
    8282        # FIXME: Calling self._commands_usage() in the constructor is bad because
    8383        # it calls self.should_show_command_help which is subclass-defined.
     
    8585        self.global_option_parser = HelpPrintingOptionParser(usage=self._usage_line(), formatter=NonWrappingEpilogIndentedHelpFormatter(), epilog=self._commands_usage())
    8686
     87    @classmethod
     88    def _add_all_subclasses(cls, class_to_crawl, seen_classes):
     89        for subclass in class_to_crawl.__subclasses__():
     90            if subclass not in seen_classes:
     91                seen_classes.add(subclass)
     92                cls._add_all_subclasses(subclass, seen_classes)
     93
     94    @classmethod
     95    def _find_all_commands(cls):
     96        commands = set()
     97        cls._add_all_subclasses(Command, commands)
     98        return sorted(commands)
     99
    87100    @staticmethod
    88101    def _usage_line():
    89102        return "Usage: %prog [options] command [command-options] [command-arguments]"
    90 
    91     # FIXME: This can all be simplified once Command objects know their own names.
    92     @staticmethod
    93     def _name_and_arguments(command):
    94         return command['object'].name_with_arguments(command["name"])
    95103
    96104    def _command_help_formatter(self):
     
    103111    @classmethod
    104112    def _help_for_command(cls, command, formatter, longest_name_length):
    105         help_text = "  " + cls._name_and_arguments(command).ljust(longest_name_length + 3) + command['object'].help_text + "\n"
    106         help_text += command['object'].option_parser.format_option_help(formatter)
     113        help_text = "  " + command.name_with_arguments().ljust(longest_name_length + 3) + command.help_text + "\n"
     114        help_text += command.option_parser.format_option_help(formatter)
    107115        return help_text
    108116
    109117    @classmethod
    110118    def _standalone_help_for_command(cls, command):
    111         return cls._help_for_command(command, IndentedHelpFormatter(), len(cls._name_and_arguments(command)))
     119        return cls._help_for_command(command, IndentedHelpFormatter(), len(command.name_with_arguments()))
    112120
    113121    def _commands_usage(self):
    114122        # Only show commands which are relevant to this checkout.  This might be confusing to some users?
    115123        relevant_commands = filter(self.should_show_command_help, self.commands)
    116         longest_name_length = max(map(lambda command: len(self._name_and_arguments(command)), relevant_commands))
     124        longest_name_length = max(map(lambda command: len(command.name_with_arguments()), relevant_commands))
    117125        command_help_texts = map(lambda command: self._help_for_command(command, self._command_help_formatter(), longest_name_length), relevant_commands)
    118126        return "Commands:\n" + "".join(command_help_texts)
     
    142150    def command_by_name(self, command_name):
    143151        for command in self.commands:
    144             if command_name == command["name"]:
     152            if command_name == command.name:
    145153                return command
    146154        return None
     
    179187            return 0
    180188
    181         command_object = command["object"]
    182         (command_options, command_args) = command_object.parse_args(args_after_command_name)
    183         return command_object.execute(command_options, command_args, self)
     189        (command_options, command_args) = command.parse_args(args_after_command_name)
     190        return command.execute(command_options, command_args, self)
Note: See TracChangeset for help on using the changeset viewer.