[SCM] WebKit Debian packaging branch, webkit-1.1, updated. upstream/1.1.16-1409-g5afdf4d
eric at webkit.org
eric at webkit.org
Thu Dec 3 13:40:52 UTC 2009
The following commit has been merged in the webkit-1.1 branch:
commit e91b451d1d5b84acd020c6fc7b9eed734d03967c
Author: eric at webkit.org <eric at webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Date: Fri Nov 20 00:55:19 2009 +0000
2009-11-19 Eric Seidel <eric at webkit.org>
Reviewed by Adam Barth.
Move MultiCommandTool and Command into a separate file and add some basic unit tests
https://bugs.webkit.org/show_bug.cgi?id=31695
* Scripts/bugzilla-tool:
* Scripts/modules/multicommandtool.py: Added.
* Scripts/modules/multicommandtool_unittest.py: Added.
* Scripts/run-webkit-unittests:
git-svn-id: http://svn.webkit.org/repository/webkit/trunk@51223 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/WebKitTools/ChangeLog b/WebKitTools/ChangeLog
index 77eb3c1..e53e7ac 100644
--- a/WebKitTools/ChangeLog
+++ b/WebKitTools/ChangeLog
@@ -1,5 +1,17 @@
2009-11-19 Eric Seidel <eric at webkit.org>
+ Reviewed by Adam Barth.
+
+ Move MultiCommandTool and Command into a separate file and add some basic unit tests
+ https://bugs.webkit.org/show_bug.cgi?id=31695
+
+ * Scripts/bugzilla-tool:
+ * Scripts/modules/multicommandtool.py: Added.
+ * Scripts/modules/multicommandtool_unittest.py: Added.
+ * Scripts/run-webkit-unittests:
+
+2009-11-19 Eric Seidel <eric at webkit.org>
+
No review, just adding a FIXME.
Split out command parsing and help printing from BugzillaTool
diff --git a/WebKitTools/Scripts/bugzilla-tool b/WebKitTools/Scripts/bugzilla-tool
index 3bbbc95..ce2a78d 100755
--- a/WebKitTools/Scripts/bugzilla-tool
+++ b/WebKitTools/Scripts/bugzilla-tool
@@ -38,19 +38,21 @@ import sys
import time
from datetime import datetime, timedelta
-from optparse import OptionParser, IndentedHelpFormatter, SUPPRESS_USAGE, make_option
+from optparse import make_option
# Import WebKit-specific modules.
from modules.bugzilla import Bugzilla, parse_bug_id
+from modules.buildbot import BuildBot
from modules.changelogs import ChangeLog
from modules.comments import bug_comment_from_commit_text
from modules.logging import error, log, tee
+from modules.multicommandtool import MultiCommandTool, Command
from modules.patchcollection import PatchCollection
from modules.scm import CommitMessage, detect_scm_system, ScriptError, CheckoutNeedsUpdate
-from modules.buildbot import BuildBot
from modules.statusbot import StatusBot
from modules.workqueue import WorkQueue, WorkQueueDelegate
+
def plural(noun):
# This is a dumb plural() implementation which was just enough for our uses.
if re.search("h$", noun):
@@ -82,29 +84,6 @@ def commit_message_for_this_commit(scm):
return CommitMessage("".join(changelog_messages).splitlines())
-class Command:
- def __init__(self, help_text, argument_names="", options=[], requires_local_commits=False):
- self.help_text = help_text
- self.argument_names = argument_names
- self.options = options
- self.option_parser = HelpPrintingOptionParser(usage=SUPPRESS_USAGE, add_help_option=False, option_list=self.options)
- self.requires_local_commits = requires_local_commits
-
- def name_with_arguments(self, command_name):
- usage_string = command_name
- if len(self.options) > 0:
- usage_string += " [options]"
- if self.argument_names:
- usage_string += " " + self.argument_names
- return usage_string
-
- def parse_args(self, args):
- return self.option_parser.parse_args(args)
-
- def execute(self, options, args, tool):
- raise NotImplementedError, "subclasses must implement"
-
-
class BugsToCommit(Command):
def __init__(self):
Command.__init__(self, "Bugs in the commit queue")
@@ -834,119 +813,6 @@ class StyleQueue(AbstractQueue):
log(message)
-class NonWrappingEpilogIndentedHelpFormatter(IndentedHelpFormatter):
- def __init__(self):
- IndentedHelpFormatter.__init__(self)
-
- # The standard IndentedHelpFormatter paragraph-wraps the epilog, killing our custom formatting.
- def format_epilog(self, epilog):
- if epilog:
- return "\n" + epilog + "\n"
- return ""
-
-
-class HelpPrintingOptionParser(OptionParser):
- def error(self, msg):
- self.print_usage(sys.stderr)
- error_message = "%s: error: %s\n" % (self.get_prog_name(), msg)
- error_message += "\nType \"" + self.get_prog_name() + " --help\" to see usage.\n"
- self.exit(2, error_message)
-
-
-class MultiCommandTool:
- def __init__(self, commands):
- self.commands = commands
- # FIXME: Calling self._commands_usage() in the constructor is bad because
- # it calls self.should_show_command_help which is subclass-defined.
- # The subclass will not be fully initialized at this point.
- self.global_option_parser = HelpPrintingOptionParser(usage=self._usage_line(), formatter=NonWrappingEpilogIndentedHelpFormatter(), epilog=self._commands_usage())
-
- @staticmethod
- def _usage_line():
- return "Usage: %prog [options] command [command-options] [command-arguments]"
-
- # FIXME: This can all be simplified once Command objects know their own names.
- @staticmethod
- def _name_and_arguments(command):
- return command['object'].name_with_arguments(command["name"])
-
- def _command_help_formatter(self):
- # Use our own help formatter so as to indent enough.
- formatter = IndentedHelpFormatter()
- formatter.indent()
- formatter.indent()
- return formatter
-
- @classmethod
- def _help_for_command(cls, command, formatter, longest_name_length):
- help_text = " " + cls._name_and_arguments(command).ljust(longest_name_length + 3) + command['object'].help_text + "\n"
- help_text += command['object'].option_parser.format_option_help(formatter)
- return help_text
-
- def _commands_usage(self):
- # Only show commands which are relevant to this checkout. This might be confusing to some users?
- relevant_commands = filter(self.should_show_command_help, self.commands)
- longest_name_length = max(map(lambda command: len(self._name_and_arguments(command)), relevant_commands))
- command_help_texts = map(lambda command: self._help_for_command(command, self._command_help_formatter(), longest_name_length), relevant_commands)
- return "Commands:\n" + "".join(command_help_texts)
-
- def handle_global_args(self, args):
- (options, args) = self.global_option_parser.parse_args(args)
- if len(args):
- # We'll never hit this because _split_args splits at the first arg without a leading "-"
- self.global_option_parser.error("Extra arguments before command: " + args)
-
- @staticmethod
- def _split_args(args):
- # Assume the first argument which doesn't start with "-" is the command name.
- command_index = 0
- for arg in args:
- if arg[0] != "-":
- break
- command_index += 1
- else:
- return (args[:], None, [])
-
- global_args = args[:command_index]
- command = args[command_index]
- command_args = args[command_index + 1:]
- return (global_args, command, command_args)
-
- def command_by_name(self, command_name):
- for command in self.commands:
- if command_name == command["name"]:
- return command
- return None
-
- def should_show_command_help(self, command):
- raise NotImplementedError, "subclasses must implement"
-
- def should_execute_command(self, command):
- raise NotImplementedError, "subclasses must implement"
-
- def main(self):
- (global_args, command_name, args_after_command_name) = self._split_args(sys.argv[1:])
-
- # Handle --help, etc:
- self.handle_global_args(global_args)
-
- if not command_name:
- self.global_option_parser.error("No command specified")
-
- command = self.command_by_name(command_name)
- if not command:
- self.global_option_parser.error(command_name + " is not a recognized command")
-
- command_object = command["object"]
-
- (should_execute, failure_reason) = self.should_execute_command(command)
- if not should_execute:
- error(failure_reason)
-
- (command_options, command_args) = command_object.parse_args(args_after_command_name)
- return command_object.execute(command_options, command_args, self)
-
-
class BugzillaTool(MultiCommandTool):
def __init__(self):
# HACK: Set self.cached_scm before calling MultiCommandTool.__init__ because
diff --git a/WebKitTools/Scripts/modules/multicommandtool.py b/WebKitTools/Scripts/modules/multicommandtool.py
new file mode 100644
index 0000000..6e1d661
--- /dev/null
+++ b/WebKitTools/Scripts/modules/multicommandtool.py
@@ -0,0 +1,167 @@
+# Copyright (c) 2009, Google Inc. All rights reserved.
+# Copyright (c) 2009 Apple Inc. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+# MultiCommandTool provides a framework for writing svn-like/git-like tools
+# which are called with the following format:
+# tool-name [global options] command-name [command options]
+
+import sys
+
+from optparse import OptionParser, IndentedHelpFormatter, SUPPRESS_USAGE, make_option
+
+class Command:
+ def __init__(self, help_text, argument_names=None, options=None, requires_local_commits=False):
+ self.help_text = help_text
+ self.argument_names = argument_names
+ self.options = options
+ self.option_parser = HelpPrintingOptionParser(usage=SUPPRESS_USAGE, add_help_option=False, option_list=self.options)
+ self.requires_local_commits = requires_local_commits
+
+ def name_with_arguments(self, command_name):
+ usage_string = command_name
+ if self.options:
+ usage_string += " [options]"
+ if self.argument_names:
+ usage_string += " " + self.argument_names
+ return usage_string
+
+ def parse_args(self, args):
+ return self.option_parser.parse_args(args)
+
+ def execute(self, options, args, tool):
+ raise NotImplementedError, "subclasses must implement"
+
+
+class NonWrappingEpilogIndentedHelpFormatter(IndentedHelpFormatter):
+ # The standard IndentedHelpFormatter paragraph-wraps the epilog, killing our custom formatting.
+ def format_epilog(self, epilog):
+ if epilog:
+ return "\n" + epilog + "\n"
+ return ""
+
+
+class HelpPrintingOptionParser(OptionParser):
+ def error(self, msg):
+ self.print_usage(sys.stderr)
+ error_message = "%s: error: %s\n" % (self.get_prog_name(), msg)
+ error_message += "\nType \"" + self.get_prog_name() + " --help\" to see usage.\n"
+ self.exit(1, error_message)
+
+
+class MultiCommandTool:
+ def __init__(self, commands):
+ self.commands = commands
+ # FIXME: Calling self._commands_usage() in the constructor is bad because
+ # it calls self.should_show_command_help which is subclass-defined.
+ # The subclass will not be fully initialized at this point.
+ self.global_option_parser = HelpPrintingOptionParser(usage=self._usage_line(), formatter=NonWrappingEpilogIndentedHelpFormatter(), epilog=self._commands_usage())
+
+ @staticmethod
+ def _usage_line():
+ return "Usage: %prog [options] command [command-options] [command-arguments]"
+
+ # FIXME: This can all be simplified once Command objects know their own names.
+ @staticmethod
+ def _name_and_arguments(command):
+ return command['object'].name_with_arguments(command["name"])
+
+ def _command_help_formatter(self):
+ # Use our own help formatter so as to indent enough.
+ formatter = IndentedHelpFormatter()
+ formatter.indent()
+ formatter.indent()
+ return formatter
+
+ @classmethod
+ def _help_for_command(cls, command, formatter, longest_name_length):
+ help_text = " " + cls._name_and_arguments(command).ljust(longest_name_length + 3) + command['object'].help_text + "\n"
+ help_text += command['object'].option_parser.format_option_help(formatter)
+ return help_text
+
+ def _commands_usage(self):
+ # Only show commands which are relevant to this checkout. This might be confusing to some users?
+ relevant_commands = filter(self.should_show_command_help, self.commands)
+ longest_name_length = max(map(lambda command: len(self._name_and_arguments(command)), relevant_commands))
+ command_help_texts = map(lambda command: self._help_for_command(command, self._command_help_formatter(), longest_name_length), relevant_commands)
+ return "Commands:\n" + "".join(command_help_texts)
+
+ def handle_global_args(self, args):
+ (options, args) = self.global_option_parser.parse_args(args)
+ # We should never hit this because _split_args splits at the first arg without a leading "-".
+ if args:
+ self.global_option_parser.error("Extra arguments before command: " + args)
+
+ @staticmethod
+ def _split_args(args):
+ # Assume the first argument which doesn't start with "-" is the command name.
+ command_index = 0
+ for arg in args:
+ if arg[0] != "-":
+ break
+ command_index += 1
+ else:
+ return (args[:], None, [])
+
+ global_args = args[:command_index]
+ command = args[command_index]
+ command_args = args[command_index + 1:]
+ return (global_args, command, command_args)
+
+ def command_by_name(self, command_name):
+ for command in self.commands:
+ if command_name == command["name"]:
+ return command
+ return None
+
+ def should_show_command_help(self, command):
+ raise NotImplementedError, "subclasses must implement"
+
+ def should_execute_command(self, command):
+ raise NotImplementedError, "subclasses must implement"
+
+ def main(self):
+ (global_args, command_name, args_after_command_name) = self._split_args(sys.argv[1:])
+
+ # Handle --help, etc:
+ self.handle_global_args(global_args)
+
+ if not command_name:
+ self.global_option_parser.error("No command specified")
+
+ command = self.command_by_name(command_name)
+ if not command:
+ self.global_option_parser.error(command_name + " is not a recognized command")
+
+ (should_execute, failure_reason) = self.should_execute_command(command)
+ if not should_execute:
+ error(failure_reason)
+
+ command_object = command["object"]
+ (command_options, command_args) = command_object.parse_args(args_after_command_name)
+ return command_object.execute(command_options, command_args, self)
diff --git a/WebKitTools/Scripts/modules/multicommandtool_unittest.py b/WebKitTools/Scripts/modules/multicommandtool_unittest.py
new file mode 100644
index 0000000..997e11a
--- /dev/null
+++ b/WebKitTools/Scripts/modules/multicommandtool_unittest.py
@@ -0,0 +1,89 @@
+# Copyright (c) 2009, Google Inc. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+import unittest
+from multicommandtool import MultiCommandTool, Command
+
+from optparse import make_option
+
+class TrivialCommand(Command):
+ def __init__(self, **kwargs):
+ Command.__init__(self, "help text", **kwargs)
+
+ def execute(self, options, args, tool):
+ pass
+
+
+class CommandTest(unittest.TestCase):
+ def test_name_with_arguments(self):
+ command_with_args = TrivialCommand(argument_names="ARG1 ARG2")
+ self.assertEqual(command_with_args.name_with_arguments("simple"), "simple ARG1 ARG2")
+
+ command_with_args = TrivialCommand(options=[make_option("--my_option")])
+ self.assertEqual(command_with_args.name_with_arguments("simple"), "simple [options]")
+
+
+class TrivialTool(MultiCommandTool):
+ def __init__(self, commands):
+ MultiCommandTool.__init__(self, commands)
+
+ def should_show_command_help(self, command):
+ return True
+
+ def should_execute_command(self, command):
+ return True
+
+
+class MultiCommandToolTest(unittest.TestCase):
+ def _assert_split(self, args, expected_split):
+ self.assertEqual(MultiCommandTool._split_args(args), expected_split)
+
+ def test_split_args(self):
+ # MultiCommandToolTest._split_args returns: (global_args, command, command_args)
+ full_args = ["--global-option", "command", "--option", "arg"]
+ full_args_expected = (["--global-option"], "command", ["--option", "arg"])
+ self._assert_split(full_args, full_args_expected)
+
+ full_args = []
+ full_args_expected = ([], None, [])
+ self._assert_split(full_args, full_args_expected)
+
+ full_args = ["command", "arg"]
+ full_args_expected = ([], "command", ["arg"])
+ self._assert_split(full_args, full_args_expected)
+
+ def test_command_by_name(self):
+ foo_command = { "name" : "foo_command", "object" : TrivialCommand() }
+ tool = TrivialTool([foo_command])
+
+ self.assertEqual(tool.command_by_name("foo_command"), foo_command)
+ self.assertEqual(tool.command_by_name("bar"), None)
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/WebKitTools/Scripts/run-webkit-unittests b/WebKitTools/Scripts/run-webkit-unittests
index 8e518e2..99e2d08 100755
--- a/WebKitTools/Scripts/run-webkit-unittests
+++ b/WebKitTools/Scripts/run-webkit-unittests
@@ -36,6 +36,7 @@ from modules.committers_unittest import *
from modules.cpp_style_unittest import *
from modules.diff_parser_unittest import *
from modules.logging_unittest import *
+from modules.multicommandtool_unittest import *
from modules.patchcollection_unittest import *
from modules.scm_unittest import *
from modules.workqueue_unittest import *
--
WebKit Debian packaging
More information about the Pkg-webkit-commits
mailing list