[SCM] WebKit Debian packaging branch, webkit-1.1, updated. upstream/1.1.17-1283-gcf603cf
eric at webkit.org
eric at webkit.org
Tue Jan 5 23:59:43 UTC 2010
The following commit has been merged in the webkit-1.1 branch:
commit 8053f333ba4a1238c964f8ff97df4e8fd5708158
Author: eric at webkit.org <eric at webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Date: Thu Dec 24 07:18:27 2009 +0000
2009-12-23 Eric Seidel <eric at webkit.org>
Reviewed by Adam Barth.
bugzilla-tool should accept global options anywhere
https://bugs.webkit.org/show_bug.cgi?id=26912
* Scripts/bugzilla-tool:
- Use the new global_options class property.
- Add a handle_global_options callback to avoid needing callbacks for each global option.
* Scripts/modules/multicommandtool.py:
- Make the code use one combined option parser.
This allows us to accept global options anywhere and
individual command options before commands.
- Add a handle_global_options callback to avoid needing callbacks for each global option.
- Make the Command hold the option parser, but allow the tool to override it.
- The default option parser is used for help printing and when Commands are run stand alone,
but are otherwise not used.
- Add Command.main to codify the idea that Commands should support being run stand-alone.
- Change _split_args to _split_command_name_from_args now that args are unified.
* Scripts/modules/multicommandtool_unittest.py:
- Test that "tool" and "tool help" show the same help.
- Test that args are accepted before commands
git-svn-id: http://svn.webkit.org/repository/webkit/trunk@52543 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/WebKitTools/ChangeLog b/WebKitTools/ChangeLog
index 2d2e8c1..adbc3d7 100644
--- a/WebKitTools/ChangeLog
+++ b/WebKitTools/ChangeLog
@@ -1,3 +1,27 @@
+2009-12-23 Eric Seidel <eric at webkit.org>
+
+ Reviewed by Adam Barth.
+
+ bugzilla-tool should accept global options anywhere
+ https://bugs.webkit.org/show_bug.cgi?id=26912
+
+ * Scripts/bugzilla-tool:
+ - Use the new global_options class property.
+ - Add a handle_global_options callback to avoid needing callbacks for each global option.
+ * Scripts/modules/multicommandtool.py:
+ - Make the code use one combined option parser.
+ This allows us to accept global options anywhere and
+ individual command options before commands.
+ - Add a handle_global_options callback to avoid needing callbacks for each global option.
+ - Make the Command hold the option parser, but allow the tool to override it.
+ - The default option parser is used for help printing and when Commands are run stand alone,
+ but are otherwise not used.
+ - Add Command.main to codify the idea that Commands should support being run stand-alone.
+ - Change _split_args to _split_command_name_from_args now that args are unified.
+ * Scripts/modules/multicommandtool_unittest.py:
+ - Test that "tool" and "tool help" show the same help.
+ - Test that args are accepted before commands
+
2009-12-20 Chris Jerdonek <chris.jerdonek at gmail.com>
Reviewed by David Levin.
diff --git a/WebKitTools/Scripts/bugzilla-tool b/WebKitTools/Scripts/bugzilla-tool
index d3323ed..5dbf91b 100755
--- a/WebKitTools/Scripts/bugzilla-tool
+++ b/WebKitTools/Scripts/bugzilla-tool
@@ -47,10 +47,13 @@ from modules.user import User
class BugzillaTool(MultiCommandTool):
+ global_options = [
+ make_option("--dry-run", action="store_true", dest="dry_run", default=False, help="do not touch remote servers"),
+ make_option("--status-host", action="store", dest="status_host", type="string", nargs=1, help="Hostname (e.g. localhost or commit.webkit.org) where status updates should be posted."),
+ ]
+
def __init__(self):
MultiCommandTool.__init__(self)
- self.global_option_parser.add_option("--dry-run", action="callback", help="do not touch remote servers", callback=self.dry_run_callback)
- self.global_option_parser.add_option("--status-host", action="callback", type="string", nargs=1, help="Hostname (e.g. localhost or commit.webkit.org) where status updates should be posted.", callback=self.status_host_callback)
self.bugs = Bugzilla()
self.buildbot = BuildBot()
@@ -59,13 +62,6 @@ class BugzillaTool(MultiCommandTool):
self._scm = None
self.status_bot = StatusBot()
- def dry_run_callback(self, option, opt, value, parser):
- self.scm().dryrun = True
- self.bugs.dryrun = True
-
- def status_host_callback(self, option, opt, value, parser):
- self.status_bot.set_host(value)
-
def scm(self):
# Lazily initialize SCM to not error-out before command line parsing (or when running non-scm commands).
original_cwd = os.path.abspath(".")
@@ -93,6 +89,14 @@ class BugzillaTool(MultiCommandTool):
return self.scm().supports_local_commits()
return True
+ # FIXME: This may be unnecessary since we pass global options to all commands during execute() as well.
+ def handle_global_options(self, options):
+ if options.dry_run:
+ self.scm().dryrun = True
+ self.bugs.dryrun = True
+ if options.status_host:
+ self.status_bot.set_host(options.status_host)
+
def should_execute_command(self, command):
if command.requires_local_commits and not self.scm().supports_local_commits():
failure_reason = "%s requires local commits using %s in %s." % (command.name, self.scm().display_name(), self.scm().checkout_root)
diff --git a/WebKitTools/Scripts/modules/multicommandtool.py b/WebKitTools/Scripts/modules/multicommandtool.py
index 53384d3..50bd11c 100644
--- a/WebKitTools/Scripts/modules/multicommandtool.py
+++ b/WebKitTools/Scripts/modules/multicommandtool.py
@@ -46,9 +46,23 @@ class Command(object):
self.argument_names = argument_names
self.required_arguments = self._parse_required_arguments(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
self.tool = None
+ # option_parser can be overriden by the tool using set_option_parser
+ # This default parser will be used for standalone_help printing.
+ self.option_parser = HelpPrintingOptionParser(usage=SUPPRESS_USAGE, add_help_option=False, option_list=self.options)
+
+ # This design is slightly awkward, but we need the
+ # the tool to be able to create and modify the option_parser
+ # before it knows what Command to run.
+ def set_option_parser(self, option_parser):
+ self.option_parser = option_parser
+ self._add_options_to_parser()
+
+ def _add_options_to_parser(self):
+ options = self.options or []
+ for option in options:
+ self.option_parser.add_option(option)
# The tool calls bind_to_tool on each Command after adding it to its list.
def bind_to_tool(self, tool):
@@ -84,19 +98,17 @@ class Command(object):
def parse_args(self, args):
return self.option_parser.parse_args(args)
- def check_arguments_and_execute(self, args_after_command_name, tool):
- (command_options, command_args) = self.parse_args(args_after_command_name)
-
- if len(command_args) < len(self.required_arguments):
+ def check_arguments_and_execute(self, options, args, tool=None):
+ if len(args) < len(self.required_arguments):
log("%s required, %s provided. Provided: %s Required: %s\nSee '%s help %s' for usage." % (
pluralize("argument", len(self.required_arguments)),
- pluralize("argument", len(command_args)),
- "'%s'" % " ".join(command_args),
+ pluralize("argument", len(args)),
+ "'%s'" % " ".join(args),
" ".join(self.required_arguments),
tool.name(),
self.name))
return 1
- return self.execute(command_options, command_args, tool) or 0
+ return self.execute(options, args, tool) or 0
def standalone_help(self):
help_text = self.name_with_arguments().ljust(len(self.name_with_arguments()) + 3) + self.help_text + "\n"
@@ -106,6 +118,12 @@ class Command(object):
def execute(self, options, args, tool):
raise NotImplementedError, "subclasses must implement"
+ # main() exists so that Commands can be turned into stand-alone scripts.
+ # Other parts of the code will likely require modification to work stand-alone.
+ def main(self, args=sys.argv):
+ (options, args) = self.parse_args(args)
+ # Some commands might require a dummy tool
+ return self.check_arguments_and_execute(options, args)
class HelpPrintingOptionParser(OptionParser):
def __init__(self, epilog_method=None, *args, **kwargs):
@@ -115,6 +133,7 @@ class HelpPrintingOptionParser(OptionParser):
def error(self, msg):
self.print_usage(sys.stderr)
error_message = "%s: error: %s\n" % (self.get_prog_name(), msg)
+ # This method is overriden to add this one line to the output:
error_message += "\nType \"%s --help\" to see usage.\n" % self.get_prog_name()
self.exit(1, error_message)
@@ -150,7 +169,12 @@ class HelpCommand(Command):
epilog += "%s\n" % "".join(command_help_texts)
epilog += "See '%prog help --all-commands' to list all commands.\n"
epilog += "See '%prog help COMMAND' for more information on a specific command.\n"
- return self.tool.global_option_parser.expand_prog_name(epilog)
+ return epilog.replace("%prog", self.tool.name()) # Use of %prog here mimics OptionParser.expand_prog_name().
+
+ # FIXME: This is a hack so that we don't show --all-commands as a global option:
+ def _remove_help_options(self):
+ for option in self.options:
+ self.option_parser.remove_option(option.get_opt_string())
def execute(self, options, args, tool):
if args:
@@ -160,12 +184,16 @@ class HelpCommand(Command):
return 0
self.show_all_commands = options.show_all_commands
- tool.global_option_parser.print_help()
+ self._remove_help_options()
+ self.option_parser.print_help()
return 0
class MultiCommandTool(object):
+ global_options = None
+
def __init__(self, name=None, commands=None):
+ self._name = name or OptionParser(prog=name).get_prog_name() # OptionParser has nice logic for fetching the name.
# Allow the unit tests to disable command auto-discovery.
self.commands = commands or [cls() for cls in self._find_all_commands() if cls.name]
self.help_command = self.command_by_name(HelpCommand.name)
@@ -175,7 +203,6 @@ class MultiCommandTool(object):
self.commands.append(self.help_command)
for command in self.commands:
command.bind_to_tool(self)
- self.global_option_parser = HelpPrintingOptionParser(epilog_method=self.help_command._help_epilog, prog=name, usage=self._usage_line())
@classmethod
def _add_all_subclasses(cls, class_to_crawl, seen_classes):
@@ -190,21 +217,15 @@ class MultiCommandTool(object):
cls._add_all_subclasses(Command, commands)
return sorted(commands)
- @staticmethod
- def _usage_line():
- return "Usage: %prog [options] COMMAND [ARGS]"
-
def name(self):
- return self.global_option_parser.get_prog_name()
+ return self._name
- 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: %s" % args)
+ def _create_option_parser(self):
+ usage = "Usage: %prog [options] COMMAND [ARGS]"
+ return HelpPrintingOptionParser(epilog_method=self.help_command._help_epilog, prog=self.name(), usage=usage)
@staticmethod
- def _split_args(args):
+ def _split_command_name_from_args(args):
# Assume the first argument which doesn't start with "-" is the command name.
command_index = 0
for arg in args:
@@ -212,12 +233,10 @@ class MultiCommandTool(object):
break
command_index += 1
else:
- return (args[:], None, [])
+ return (None, args[:])
- global_args = args[:command_index]
command = args[command_index]
- command_args = args[command_index + 1:]
- return (global_args, command, command_args)
+ return (command, args[:command_index] + args[command_index + 1:])
def command_by_name(self, command_name):
for command in self.commands:
@@ -232,21 +251,33 @@ class MultiCommandTool(object):
return command.show_in_main_help
def should_execute_command(self, command):
- raise NotImplementedError, "subclasses must implement"
+ return True
+
+ def _add_global_options(self, option_parser):
+ global_options = self.global_options or []
+ for option in global_options:
+ option_parser.add_option(option)
+
+ def handle_global_options(self, options):
+ pass
def main(self, argv=sys.argv):
- (global_args, command_name, args_after_command_name) = self._split_args(argv[1:])
+ (command_name, args) = self._split_command_name_from_args(argv[1:])
- # Handle --help, etc:
- self.handle_global_args(global_args)
+ option_parser = self._create_option_parser()
+ self._add_global_options(option_parser)
command = self.command_by_name(command_name) or self.help_command
if not command:
- self.global_option_parser.error("%s is not a recognized command" % command_name)
+ option_parser.error("%s is not a recognized command" % command_name)
+
+ command.set_option_parser(option_parser)
+ (options, args) = command.parse_args(args)
+ self.handle_global_options(options)
(should_execute, failure_reason) = self.should_execute_command(command)
if not should_execute:
log(failure_reason)
- return 0
+ return 0 # FIXME: Should this really be 0?
- return command.check_arguments_and_execute(args_after_command_name, self)
+ return command.check_arguments_and_execute(options, args, self)
diff --git a/WebKitTools/Scripts/modules/multicommandtool_unittest.py b/WebKitTools/Scripts/modules/multicommandtool_unittest.py
index ad872c9..7f485a2 100644
--- a/WebKitTools/Scripts/modules/multicommandtool_unittest.py
+++ b/WebKitTools/Scripts/modules/multicommandtool_unittest.py
@@ -64,7 +64,7 @@ class CommandTest(unittest.TestCase):
def test_required_arguments(self):
two_required_arguments = TrivialCommand(argument_names="ARG1 ARG2 [ARG3]")
expected_missing_args_error = "2 arguments required, 1 argument provided. Provided: 'foo' Required: ARG1 ARG2\nSee 'trivial-tool help trivial' for usage.\n"
- exit_code = OutputCapture().assert_outputs(self, two_required_arguments.check_arguments_and_execute, [["foo"], TrivialTool()], expected_stderr=expected_missing_args_error)
+ exit_code = OutputCapture().assert_outputs(self, two_required_arguments.check_arguments_and_execute, [None, ["foo"], TrivialTool()], expected_stderr=expected_missing_args_error)
self.assertEqual(exit_code, 1)
@@ -81,20 +81,20 @@ class TrivialTool(MultiCommandTool):
class MultiCommandToolTest(unittest.TestCase):
def _assert_split(self, args, expected_split):
- self.assertEqual(MultiCommandTool._split_args(args), expected_split)
+ self.assertEqual(MultiCommandTool._split_command_name_from_args(args), expected_split)
def test_split_args(self):
- # MultiCommandToolTest._split_args returns: (global_args, command, command_args)
+ # MultiCommandToolTest._split_command_name_from_args returns: (command, args)
full_args = ["--global-option", "command", "--option", "arg"]
- full_args_expected = (["--global-option"], "command", ["--option", "arg"])
+ full_args_expected = ("command", ["--global-option", "--option", "arg"])
self._assert_split(full_args, full_args_expected)
full_args = []
- full_args_expected = ([], None, [])
+ full_args_expected = (None, [])
self._assert_split(full_args, full_args_expected)
full_args = ["command", "arg"]
- full_args_expected = ([], "command", ["arg"])
+ full_args_expected = ("command", ["arg"])
self._assert_split(full_args, full_args_expected)
def test_command_by_name(self):
@@ -121,6 +121,7 @@ See 'trivial-tool help --all-commands' to list all commands.
See 'trivial-tool help COMMAND' for more information on a specific command.
"""
+ self._assert_tool_main_outputs(tool, ["tool"], expected_common_commands_help)
self._assert_tool_main_outputs(tool, ["tool", "help"], expected_common_commands_help)
expected_all_commands_help = """Usage: trivial-tool [options] COMMAND [ARGS]
@@ -137,6 +138,9 @@ See 'trivial-tool help COMMAND' for more information on a specific command.
"""
self._assert_tool_main_outputs(tool, ["tool", "help", "--all-commands"], expected_all_commands_help)
+ # Test that arguments can be passed before commands as well
+ self._assert_tool_main_outputs(tool, ["tool", "--all-commands", "help"], expected_all_commands_help)
+
def test_command_help(self):
command_with_options = TrivialCommand(options=[make_option("--my_option")])
--
WebKit Debian packaging
More information about the Pkg-webkit-commits
mailing list