diff options
Diffstat (limited to 'libbe/ui')
-rw-r--r-- | libbe/ui/base.py | 1 | ||||
-rwxr-xr-x | libbe/ui/command_line.py | 270 | ||||
-rw-r--r-- | libbe/ui/util/cmdutil.py | 108 |
3 files changed, 271 insertions, 108 deletions
diff --git a/libbe/ui/base.py b/libbe/ui/base.py index e69de29..b98f164 100644 --- a/libbe/ui/base.py +++ b/libbe/ui/base.py @@ -0,0 +1 @@ +# Copyright diff --git a/libbe/ui/command_line.py b/libbe/ui/command_line.py new file mode 100755 index 0000000..e84d32a --- /dev/null +++ b/libbe/ui/command_line.py @@ -0,0 +1,270 @@ +# Copyright (C) 2005-2009 Aaron Bentley and Panometrics, Inc. +# Chris Ball <cjb@laptop.org> +# Gianluca Montecchi <gian@grys.it> +# Oleg Romanyshyn <oromanyshyn@panoramicfeedback.com> +# W. Trevor King <wking@drexel.edu> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +""" +A command line interface to Bugs Everywhere. +""" + +import optparse +import os +import sys + +import libbe +import libbe.command +import libbe.command.util +import libbe.version +import libbe.ui.util.pager + +if libbe.TESTING == True: + import doctest + +class CallbackExit (Exception): + pass + +class CmdOptionParser(optparse.OptionParser): + def __init__(self, command): + self.command = command + optparse.OptionParser.__init__(self) + self.disable_interspersed_args() + self.remove_option('-h') + self._option_by_name = {} + for option in self.command.options: + self._add_option(option) + + def _add_option(self, option): + option.validate() + self._option_by_name[option.name] = option + opt_strings = ['--'+option.name] + if option.short_name != None: + opt_strings.append('-'+option.short_name) + if option.arg == None: # a callback option + opt = optparse.Option( + *opt_strings, action='callback', + callback=self.callback, help=option.help) + else: + kwargs = {} + if option.arg.type == 'bool': + action = 'store_true' + else: + kwargs['type'] = option.arg.type + action = 'store' + opt = optparse.Option( + *opt_strings, metavar=option.arg.metavar, + default=option.arg.default, action=action, + help=option.help, **kwargs) + opt._option = option + self.add_option(opt) + + def parse_args(self, args=None, values=None): + args = self._get_args(args) + options,parsed_args = optparse.OptionParser.parse_args( + self, args=args, values=values) + for name,value in options.__dict__.items(): + if value == '--complete': + argument = None + option = self._option_by_name[name] + if option.arg != None: + argument = option.arg + fragment = None + indices = [i for i,arg in enumerate(args) + if arg == '--complete'] + for i in indices: + assert i > 0 # this --complete is an option value + if args[i-1] in ['--%s' % o.name + for o in self.command.options]: + name = args[i-1][2:] + if name == option.name: + break + elif option.short_name != None \ + and args[i-1].startswith('-') \ + and args[i-1].endswith(option.short_name): + break + if i+1 < len(args): + fragment = args[i+1] + self.complete(argument, fragment) + for i,arg in enumerate(parsed_args): + if arg == '--complete': + if i < len(self.command.args): + argument = self.command.args[i] + else: + argument = self.command.args[-1] + if argument.repeatable == False: + raise libbe.command.UserError('Too many arguments') + fragment = None + if i < len(args) - 1: + fragment = args[i+1] + self.complete(argument, fragment) + if len(parsed_args) > len(self.command.args) \ + and self.command.args[-1] == False: + raise libbe.command.UserError('Too many arguments') + for arg in self.command.args[len(parsed_args):]: + if arg.optional == False: + raise libbe.command.UserError( + 'Missing required argument %s' % arg.metavar) + return (options, parsed_args) + + def callback(self, option, opt, value, parser): + command_option = option._option + if command_option.name == 'complete': + argument = None + fragment = None + if len(parser.rargs) > 0: + fragment = parser.rargs[0] + self.complete(argument, fragment) + else: + print command_option.callback( + self.command, command_option, value) + raise CallbackExit + + def complete(self, argument=None, fragment=None): + print argument, fragment + comps = self.command.complete(argument, fragment) + if fragment != None: + comps = [c for c in comps if c.startswith(fragment)] + print '\n'.join(comps) + raise CallbackExit + + +class BE (libbe.command.Command): + """Class for parsing the command line arguments for `be`. + This class does not contain a useful _run() method. Call this + module's main() function instead. + + >>> be = BE() + >>> p = CmdOptionParser(be) + >>> p.exit_after_callback = False + >>> try: + ... options,args = p.parse_args(['--help']) # doctest: +ELLIPSIS + ... except CallbackExit: + ... pass + usage: be [options] [COMMAND [command-options] [COMMAND-ARGS ...]] + <BLANKLINE> + Options: + -h HELP, --help=HELP Print a help message. + <BLANKLINE> + --complete=STRING Print a list of possible completions. + <BLANKLINE> + --version=VERSION Print version string. + ... + >>> options,args = p.parse_args(['--complete']) # doctest: +ELLIPSIS + """ + name = 'be' + + def __init__(self, *args, **kwargs): + libbe.command.Command.__init__(self, *args, **kwargs) + self.options.extend([ + libbe.command.Option(name='version', + help='Print version string.', + callback=self.version), + libbe.command.Option(name='full-version', + help='Print full version information.', + callback=self.full_version), + libbe.command.Option(name='repo', short_name='r', + help='Select BE repository (see `be help repo`) rather ' + 'than the current directory.', + arg=libbe.command.Argument( + name='repo', metavar='REPO', default='.', + completion_callback=libbe.command.util.complete_path)), + libbe.command.Option(name='paginate', + help='Pipe all output into less (or if set, $PAGER).'), + libbe.command.Option(name='no-pager', + help='Do not pipe git output into a pager.'), + ]) + self.args.extend([ + libbe.command.Argument( + name='command', optional=False, + completion_callback=libbe.command.util.complete_command), + libbe.command.Argument( + name='args', optional=True, repeatable=True) + ]) + + def _usage(self): + return 'usage: be [options] [COMMAND [command-options] [COMMAND-ARGS ...]]' + + def _long_help(self): + cmdlist = [] + for name in libbe.command.commands(): + module = libbe.command.get_command(name) + Class = getattr(module, name.capitalize()) + cmdlist.append((name, Class.__doc__.splitlines()[0])) + cmdlist.sort() + longest_cmd_len = max([len(name) for name,desc in cmdlist]) + ret = ['Bugs Everywhere - Distributed bug tracking', + '', 'Supported commands'] + for name, desc in cmdlist: + numExtraSpaces = longest_cmd_len-len(name) + ret.append('be %s%*s %s' % (name, numExtraSpaces, '', desc)) + ret.extend(['', 'Run', ' be help [command]', 'for more information.']) + return '\n'.join(ret) + + def version(self, *args): + return libbe.version.version(verbose=False) + + def full_version(self, *args): + return libbe.version.version(verbose=True) + +def main(): + parser = CmdOptionParser(BE()) + try: + options,args = parser.parse_args() + except CallbackExit: + return 0 + except libbe.command.UserError, e: + print 'ERROR:\n', e + return 1 + + paginate = 'auto' + if options.paginate == True: + paginate = 'always' + if options.no_pager== True: + paginate = 'never' + libbe.ui.util.pager.run_pager(paginate) + + command_name = args[0] + try: + module = libbe.command.get_command(command_name) + except libbe.command.UnknownCommand, e: + print e + return 1 + Class = getattr(module, command_name.capitalize()) + command = Class() + parser = CmdOptionParser(command) + if command.requires_bugdir == True: + storage = libbe.storage.get_storage(options['repo']) + storage.connect() + bugdir = BugDir(storage) + else: + storage = None + bugdir = None + try: + options,args = parser.parse_args(args[1:]) + command.run(bugdir, options, args) + except CallbackExit: + if storage != None: storage.disconnect() + return 0 + except libbe.command.UserError, e: + if storage != None: storage.disconnect() + print 'ERROR:\n', e + return 1 + if storage != None: storage.disconnect() + return 0 + +if __name__ == '__main__': + sys.exit(main()) diff --git a/libbe/ui/util/cmdutil.py b/libbe/ui/util/cmdutil.py index b2d8a99..86ff9fc 100644 --- a/libbe/ui/util/cmdutil.py +++ b/libbe/ui/util/cmdutil.py @@ -55,115 +55,7 @@ def execute(cmd, args, ret = 0 return ret -class GetHelp(Exception): - pass - - -class GetCompletions(Exception): - def __init__(self, completions=[]): - msg = "Get allowed completions" - Exception.__init__(self, msg) - self.completions = completions - -def raise_get_help(option, opt, value, parser): - raise GetHelp - -def raise_get_completions(option, opt, value, parser): - if hasattr(parser, "command") and parser.command == "be": - comps = [] - for command, module in iter_commands(): - comps.append(command) - for opt in parser.option_list: - comps.append(opt.get_opt_string()) - raise GetCompletions(comps) - raise GetCompletions(completions(sys.argv[1])) - -def completions(cmd): - parser = get_command(cmd).get_parser() - longopts = [] - for opt in parser.option_list: - longopts.append(opt.get_opt_string()) - return longopts - - -class CmdOptionParser(optparse.OptionParser): - def __init__(self, usage): - optparse.OptionParser.__init__(self, usage) - self.disable_interspersed_args() - self.remove_option("-h") - self.add_option("-h", "--help", action="callback", - callback=raise_get_help, help="Print a help message") - self.add_option("--complete", action="callback", - callback=raise_get_completions, - help="Print a list of available completions") - - def error(self, message): - raise UsageError(message) - - def iter_options(self): - return iter_combine([self._short_opt.iterkeys(), - self._long_opt.iterkeys()]) - - def help_str(self): - f = StringIO() - 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, bugid_args={}): - """ - A dud complete implementation for becommands so that the - --complete argument doesn't cause any problems. Use this - until you've set up a command-specific complete function. - - bugid_args is an optional dict where the keys are positional - arguments taking bug shortnames and the values are functions for - filtering, since that's a common enough operation. - e.g. for "be open [options] BUGID" - bugid_args = {0: lambda bug : bug.active == False} - A positional argument of -1 specifies all remaining arguments - (e.g in the case of "be show BUGID BUGID ..."). - """ - for option,value in option_value_pairs(options, parser): - if value == "--complete": - raise GetCompletions() - if len(bugid_args.keys()) > 0: - max_pos_arg = max(bugid_args.keys()) - else: - max_pos_arg = -1 - for pos,value in enumerate(args): - if value == "--complete": - filter = None - if pos in bugid_args: - filter = bugid_args[pos] - if pos > max_pos_arg and -1 in bugid_args: - filter = bugid_args[-1] - if filter != None: - bugshortnames = [] - try: - bd = bugdir.BugDir(from_disk=True, - manipulate_encodings=False) - bd.load_all_bugs() - bugs = [bug for bug in bd if filter(bug) == True] - bugshortnames = [bd.bug_shortname(bug) for bug in bugs] - except bugdir.NoBugDir: - pass - raise GetCompletions(bugshortnames) - raise GetCompletions() - -def complete_path(path): - """List possible path completions for path.""" - comps = glob.glob(path+"*") + glob.glob(path+"/*") - if len(comps) == 1 and os.path.isdir(comps[0]): - comps.extend(glob.glob(comps[0]+"/*")) - return comps def restrict_file_access(bugdir, path): |