From: W. Trevor King Date: Thu, 27 Nov 2008 15:16:18 +0000 (-0500) Subject: Command completion simplified and working for list, dummies for other cmds. X-Git-Tag: 1.0.0~100 X-Git-Url: http://git.tremily.us/?a=commitdiff_plain;h=216e39db0bd3491ee3740f69a8f2afd49f76eb2f;p=be.git Command completion simplified and working for list, dummies for other cmds. All the other commands currently use default_complete(), which has no effect other than catching the --complete option and effectively aborting execution. This closes 8e1bbda4-35b6-4579-849d-117b1596ee99 --- diff --git a/.be/bugs/8e1bbda4-35b6-4579-849d-117b1596ee99/values b/.be/bugs/8e1bbda4-35b6-4579-849d-117b1596ee99/values index 1629785..f88ca6c 100644 --- a/.be/bugs/8e1bbda4-35b6-4579-849d-117b1596ee99/values +++ b/.be/bugs/8e1bbda4-35b6-4579-849d-117b1596ee99/values @@ -15,7 +15,7 @@ severity=serious -status=open +status=fixed diff --git a/README.dev b/README.dev index 4cbf554..837fac1 100644 --- a/README.dev +++ b/README.dev @@ -14,6 +14,10 @@ provide the following elements: The entry function for your plugin. args is everything from sys.argv after the name of your plugin (e.g. for the command `be open abc', args=['abc']). + + Note: be supports command-completion. To avoid raising errors you + need to deal with possible '--complete' options and arguments. + See the 'Command completion' section below for more information. help() Return the string to be output by `be help ', `be --help', etc. @@ -27,8 +31,39 @@ consistent interface Again, you can just browse around in becommands to get a feel for things. +Testing +------- + Run any doctests in your plugin with be$ python test.py for example be$ python test.py merge + +Command completion +------------------ + +BE implements a general framework to make it easy to support command +completion for arbitrary plugins. In order to support this system, +all becommands should properly handle the '--complete' commandline +argument, returning a list of possible completions. For example + $ be --commands + lists options accepted by be and the names of all available becommands. + $ be list --commands + lists options accepted by becommand/list + $ be list --status --commands + lists arguments accepted by the becommand/list --status option + $ be show -- --commands + lists possible vals for the first positional argument of becommand/show +This is a lot of information, but command-line completion is really +convenient for the user. See becommand/list.py and becommand/show.py +for example implementations. The basic idea is to raise + cmdutil.GetCompletions(['list','of','possible','completions']) +once you've determined what that list should be. + +However, command completion is not critcal. The first priority is to +implement the target functionality, with fancy shell sugar coming +later. In recognition of this, cmdutil provides the default_complete +function which ensures that if '--complete' is any one of the +arguments, options, or option-arguments, GetCompletions will be raised +with and empty list. diff --git a/becommands/assign.py b/becommands/assign.py index b00c5c2..9869350 100644 --- a/becommands/assign.py +++ b/becommands/assign.py @@ -41,7 +41,9 @@ def execute(args, test=False): >>> bd.bug_from_shortname("a").assigned is None True """ - options, args = get_parser().parse_args(args) + parser = get_parser() + options, args = parser.parse_args(args) + cmdutil.default_complete(options, args, parser) assert(len(args) in (0, 1, 2)) if len(args) == 0: raise cmdutil.UsageError("Please specify a bug id.") diff --git a/becommands/close.py b/becommands/close.py index d125397..37fa032 100644 --- a/becommands/close.py +++ b/becommands/close.py @@ -31,7 +31,9 @@ def execute(args, test=False): >>> print bd.bug_from_shortname("a").status closed """ - options, args = get_parser().parse_args(args) + parser = get_parser() + options, args = parser.parse_args(args) + cmdutil.default_complete(options, args, parser) if len(args) == 0: raise cmdutil.UsageError("Please specify a bug id.") if len(args) > 1: diff --git a/becommands/comment.py b/becommands/comment.py index 4470f75..5eac102 100644 --- a/becommands/comment.py +++ b/becommands/comment.py @@ -55,7 +55,9 @@ def execute(args, test=False): I like cheese """ - options, args = get_parser().parse_args(args) + parser = get_parser() + options, args = parser.parse_args(args) + cmdutil.default_complete(options, args, parser) if len(args) == 0: raise cmdutil.UsageError("Please specify a bug or comment id.") if len(args) > 2: diff --git a/becommands/diff.py b/becommands/diff.py index 1696913..c090fa8 100644 --- a/becommands/diff.py +++ b/becommands/diff.py @@ -39,7 +39,9 @@ def execute(args, test=False): status: open -> closed """ - options, args = get_parser().parse_args(args) + parser = get_parser() + options, args = parser.parse_args(args) + cmdutil.default_complete(options, args, parser) if len(args) == 0: revision = None if len(args) == 1: diff --git a/becommands/help.py b/becommands/help.py index 1c99af5..e228eed 100644 --- a/becommands/help.py +++ b/becommands/help.py @@ -26,11 +26,14 @@ def execute(args): Options: -h, --help Print a help message + --complete Print a list of available completions Print help for specified command or list of all commands. """ - options, args = get_parser().parse_args(args) + parser = get_parser() + options, args = parser.parse_args(args) + cmdutil.default_complete(options, args, parser) if len(args) > 1: raise cmdutil.UsageError("Too many arguments.") if len(args) == 0: diff --git a/becommands/list.py b/becommands/list.py index 25e72d3..ff340e3 100644 --- a/becommands/list.py +++ b/becommands/list.py @@ -34,16 +34,7 @@ def execute(args, test=False): """ parser = get_parser() options, args = parser.parse_args(args) - - for option in [o.dest for o in parser.option_list if o.dest != None]: - value = getattr(options, option) - if value == "--complete": - if option == "status": - raise cmdutil.GetCompletions(status_values) - raise cmdutil.GetCompletions() - if "--complete" in args: - raise cmdutil.GetCompletions() # no completions for arguments yet - + complete(options, args, parser) if len(args) > 0: raise cmdutil.UsageError("Too many arguments.") @@ -203,3 +194,14 @@ The boolean options are ignored if the matching string option is used. def help(): return get_parser().help_str() + longhelp + +def complete(options, args, parser): + for option, value in cmdutil.option_value_pairs(options, parser): + if value == "--complete": + if option == "status": + raise cmdutil.GetCompletions(status_values) + elif option == "severity": + raise cmdutil.GetCompletions(severity_values) + raise cmdutil.GetCompletions() + if "--complete" in args: + raise cmdutil.GetCompletions() # no positional arguments for list diff --git a/becommands/merge.py b/becommands/merge.py index d1829d6..583bd28 100644 --- a/becommands/merge.py +++ b/becommands/merge.py @@ -120,7 +120,9 @@ def execute(args, test=False): >>> print b.status closed """ - options, args = get_parser().parse_args(args) + parser = get_parser() + options, args = parser.parse_args(args) + cmdutil.default_complete(options, args, parser) if len(args) < 2: raise cmdutil.UsageError("Please specify two bug ids.") if len(args) > 2: diff --git a/becommands/new.py b/becommands/new.py index bc17f83..58fabbc 100644 --- a/becommands/new.py +++ b/becommands/new.py @@ -38,7 +38,9 @@ def execute(args, test=False): >>> bug.target == None True """ - options, args = get_parser().parse_args(args) + parser = get_parser() + options, args = parser.parse_args(args) + cmdutil.default_complete(options, args, parser) if len(args) != 1: raise cmdutil.UsageError("Please supply a summary message") bd = bugdir.BugDir(from_disk=True, manipulate_encodings=not test) diff --git a/becommands/open.py b/becommands/open.py index 9a9667d..41db5e9 100644 --- a/becommands/open.py +++ b/becommands/open.py @@ -30,7 +30,9 @@ def execute(args, test=False): >>> print bd.bug_from_shortname("b").status open """ - options, args = get_parser().parse_args(args) + parser = get_parser() + options, args = parser.parse_args(args) + cmdutil.default_complete(options, args, parser) if len(args) == 0: raise cmdutil.UsageError, "Please specify a bug id." if len(args) > 1: diff --git a/becommands/remove.py b/becommands/remove.py index 386d9d4..cd877c5 100644 --- a/becommands/remove.py +++ b/becommands/remove.py @@ -35,7 +35,9 @@ def execute(args, test=False): ... print "Bug not found" Bug not found """ - options, args = get_parser().parse_args(args) + parser = get_parser() + options, args = parser.parse_args(args) + cmdutil.default_complete(options, args, parser) if len(args) != 1: raise cmdutil.UsageError, "Please specify a bug id." bd = bugdir.BugDir(from_disk=True, manipulate_encodings=not test) diff --git a/becommands/set.py b/becommands/set.py index 3904262..aef5eb3 100644 --- a/becommands/set.py +++ b/becommands/set.py @@ -32,7 +32,9 @@ def execute(args, test=False): >>> execute(["target"], test=True) None """ - options, args = get_parser().parse_args(args) + parser = get_parser() + options, args = parser.parse_args(args) + cmdutil.default_complete(options, args, parser) if len(args) > 2: raise cmdutil.UsageError, "Too many arguments" bd = bugdir.BugDir(from_disk=True, manipulate_encodings=not test) diff --git a/becommands/set_root.py b/becommands/set_root.py index 11f38b9..3749e28 100644 --- a/becommands/set_root.py +++ b/becommands/set_root.py @@ -55,7 +55,9 @@ def execute(args, test=False): UserError: No such directory: /highly-unlikely-to-exist >>> os.chdir('/') """ - options, args = get_parser().parse_args(args) + parser = get_parser() + options, args = parser.parse_args(args) + cmdutil.default_complete(options, args, parser) if len(args) > 1: raise cmdutil.UsageError if len(args) == 1: diff --git a/becommands/severity.py b/becommands/severity.py index 3c856de..29a5545 100644 --- a/becommands/severity.py +++ b/becommands/severity.py @@ -33,7 +33,9 @@ def execute(args, test=False): Traceback (most recent call last): UserError: Invalid severity level: none """ - options, args = get_parser().parse_args(args) + parser = get_parser() + options, args = parser.parse_args(args) + cmdutil.default_complete(options, args, parser) if len(args) not in (1,2): raise cmdutil.UsageError bd = bugdir.BugDir(from_disk=True, manipulate_encodings=not test) diff --git a/becommands/show.py b/becommands/show.py index c58e6bb..3c00712 100644 --- a/becommands/show.py +++ b/becommands/show.py @@ -35,7 +35,9 @@ def execute(args, test=False): Bug A """ - options, args = get_parser().parse_args(args) + parser = get_parser() + options, args = parser.parse_args(args) + cmdutil.default_complete(options, args, parser) if len(args) == 0: raise cmdutil.UsageError bd = bugdir.BugDir(from_disk=True, manipulate_encodings=not test) diff --git a/becommands/status.py b/becommands/status.py index 73d43f8..4ac7170 100644 --- a/becommands/status.py +++ b/becommands/status.py @@ -33,7 +33,9 @@ def execute(args, test=False): Traceback (most recent call last): UserError: Invalid status: none """ - options, args = get_parser().parse_args(args) + parser = get_parser() + options, args = parser.parse_args(args) + cmdutil.default_complete(options, args, parser) if len(args) not in (1,2): raise cmdutil.UsageError bd = bugdir.BugDir(from_disk=True, manipulate_encodings=not test) diff --git a/becommands/target.py b/becommands/target.py index 4371ef0..497ebf0 100644 --- a/becommands/target.py +++ b/becommands/target.py @@ -32,7 +32,9 @@ def execute(args, test=False): >>> execute(["a"], test=True) No target assigned. """ - options, args = get_parser().parse_args(args) + parser = get_parser() + options, args = parser.parse_args(args) + cmdutil.default_complete(options, args, parser) if len(args) not in (1, 2): raise cmdutil.UsageError bd = bugdir.BugDir(from_disk=True, manipulate_encodings=not test) diff --git a/libbe/cmdutil.py b/libbe/cmdutil.py index 2bfe6d4..7f6e5ac 100644 --- a/libbe/cmdutil.py +++ b/libbe/cmdutil.py @@ -86,7 +86,7 @@ def help(cmd=None): ret.append("be %s%*s %s" % (name, numExtraSpaces, "", desc)) return "\n".join(ret) -def options(cmd): +def completions(cmd): parser = get_command(cmd).get_parser() longopts = [] for opt in parser.option_list: @@ -97,7 +97,7 @@ def raise_get_help(option, opt, value, parser): raise GetHelp def raise_get_completions(option, opt, value, parser): - raise GetCompletions(options(sys.argv[1])) + raise GetCompletions(completions(sys.argv[1])) class CmdOptionParser(optparse.OptionParser): def __init__(self, usage): @@ -121,6 +121,26 @@ class CmdOptionParser(optparse.OptionParser): self.print_help(f) return f.getvalue() +def option_value_pairs(options, parser): + """ + Iterate through OptionParser (option, value) pairs. + """ + for option in [o.dest for o in parser.option_list if o.dest != None]: + value = getattr(options, option) + yield (option, value) + +def default_complete(options, args, parser): + """ + A dud complete implementation for becommands to that the + --complete argument doesn't cause any problems. Use this + until you've set up a command-specific complete function. + """ + for option,value in option_value_pairs(options, parser): + if value == "--complete": + raise cmdutil.GetCompletions() + if "--complete" in args: + raise cmdutil.GetCompletions() + def underlined(instring): """Produces a version of a string that is underlined with '='