aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorW. Trevor King <wking@drexel.edu>2009-12-12 00:31:55 -0500
committerW. Trevor King <wking@drexel.edu>2009-12-12 00:31:55 -0500
commitbf3d434b244c57556bec979acbc658c30eb58221 (patch)
treea31746f74a9aaaf1d95c4bd7e1ef1ae30041f2f8
parenta153347564e4c6baa0388fda05530f5548d16ac5 (diff)
downloadbugseverywhere-bf3d434b244c57556bec979acbc658c30eb58221.tar.gz
Added libbe.command.base (with Command class) and moved list command to new format.
-rwxr-xr-xbe5
-rw-r--r--libbe/command/__init__.py13
-rw-r--r--libbe/command/base.py224
-rw-r--r--libbe/command/list.py340
-rw-r--r--libbe/ui/__init__.py1
-rw-r--r--libbe/ui/base.py23
-rw-r--r--libbe/ui/util/__init__.py69
-rw-r--r--libbe/ui/util/cmdutil.py141
-rw-r--r--libbe/ui/util/repo.py4
-rw-r--r--libbe/util/plugin.py60
-rw-r--r--libbe/util/utility.py9
-rw-r--r--test.py10
12 files changed, 579 insertions, 320 deletions
diff --git a/be b/be
index f026c05..5e3088e 100755
--- a/be
+++ b/be
@@ -43,6 +43,11 @@ parser.add_option("--no-pager", dest="no_pager", default=False,
action='store_true',
help="Do not pipe git output into a pager.")
+# Option(name='repo', short_name='r',
+# help='Select BE repository (see `be help repo`) rather than'
+# 'the current directory.',
+# arg=Argument(name='repo', metavar='REPO', default='.',
+# completion_callback=libbe.ui.util.repo.complete)),
try:
options,args = parser.parse_args()
diff --git a/libbe/command/__init__.py b/libbe/command/__init__.py
index 794013c..344a8a2 100644
--- a/libbe/command/__init__.py
+++ b/libbe/command/__init__.py
@@ -14,3 +14,16 @@
# 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.
+
+import base
+
+UserError = base.UserError
+UnkownCommand = base.UnknownCommand
+get_command = base.get_command
+commands = base.commands
+Option = base.Option
+Argument = base.Argument
+Command = base.Command
+
+__all__ = [UserError, UnkownCommand, get_command, commands,
+ Option, Argument, Command]
diff --git a/libbe/command/base.py b/libbe/command/base.py
new file mode 100644
index 0000000..973b840
--- /dev/null
+++ b/libbe/command/base.py
@@ -0,0 +1,224 @@
+# Copyright
+
+import optparse
+import sys
+
+import libbe
+import libbe.util.plugin
+import libbe.ui.util.repo
+
+class UserError(Exception):
+ pass
+
+class UnknownCommand(UserError):
+ def __init__(self, cmd):
+ Exception.__init__(self, "Unknown command '%s'" % cmd)
+ self.cmd = cmd
+
+
+def get_command(command_name):
+ """Retrieves the module for a user command
+
+ >>> try:
+ ... get_command('asdf')
+ ... except UnknownCommand, e:
+ ... print e
+ Unknown command 'asdf'
+ >>> repr(get_command('list')).startswith("<module 'libbe.command.list' from ")
+ True
+ """
+ try:
+ cmd = libbe.util.plugin.import_by_name(
+ 'libbe.command.%s' % command_name.replace("-", "_"))
+ except ImportError, e:
+ raise UnknownCommand(command_name)
+ return cmd
+
+def commands():
+ for modname in libbe.util.plugin.modnames('libbe.command'):
+ if modname != 'base':
+ yield modname
+
+class CommandInput (object):
+ def __init__(self, name, help=''):
+ self.name = name
+ self.help = help
+
+class Option (CommandInput):
+ def __init__(self, option_callback=None, short_name=None, arg=None,
+ type=None, *args, **kwargs):
+ CommandInput.__init__(self, *args, **kwargs)
+ self.option_callback = option_callback
+ self.short_name = short_name
+ self.arg = arg
+ self.type = type
+ if self.arg != None:
+ assert self.arg.name == self.name, \
+ 'Name missmatch: %s != %s' % (self.arg.name, self.name)
+
+class Argument (CommandInput):
+ def __init__(self, metavar=None, default=None,
+ optional=False, repeatable=False,
+ completion_callback=None, *args, **kwargs):
+ CommandInput.__init__(self, *args, **kwargs)
+ self.metavar = metavar
+ self.default = default
+ self.optional = optional
+ self.repeatable = repeatable
+ self.completion_callback = completion_callback
+
+class _DummyParser (object):
+ def __init__(self, options):
+ self.option_list = options
+ self.option_groups = []
+ for option in self.option_list: # add required methods and attributes
+ option.dest = option.name
+ option._short_opts = []
+ if option.short_name != None:
+ option._short_opts.append('-' + option.short_name)
+ option._long_opts = ['--' + option.name]
+ option.takes_value = lambda : option.arg != None
+ if option.takes_value():
+ option.metavar = option.arg.metavar
+ else:
+ option.metavar = None
+
+class OptionFormatter (optparse.IndentedHelpFormatter):
+ def __init__(self, options):
+ optparse.IndentedHelpFormatter.__init__(self)
+ self.options = options
+ def option_help(self):
+ # based on optparse.OptionParser.format_option_help()
+ parser = _DummyParser(self.options)
+ self.store_option_strings(parser)
+ ret = []
+ ret.append(self.format_heading('Options'))
+ self.indent()
+ for option in self.options:
+ ret.append(self.format_option(option))
+ ret.append('\n')
+ self.dedent()
+ # Drop the last '\n', or the header if no options or option groups:
+ return ''.join(ret[:-1])
+
+class Command (object):
+ """
+ >>> c = Command()
+ >>> print c.help()
+ usage: be command [options]
+ <BLANKLINE>
+ Options:
+ -h HELP, --help=HELP Print a help message
+ <BLANKLINE>
+ --complete=STRING Print a list of possible completions
+ <BLANKLINE>
+ -r REPO, --repo=REPO Select BE repository (see `be help repo`) rather
+ thanthe current directory.
+ <BLANKLINE>
+ A detailed help message.
+ """
+
+ name = 'command'
+
+ def __init__(self, input_encoding=None, output_encoding=None):
+ self.status = None
+ self.result = None
+ self.input_encoding = None
+ self.output_encoding = None
+ self.options = [
+ Option(name='help', short_name='h',
+ help='Print a help message',
+ option_callback=self.help),
+ Option(name='complete', type='string',
+ help='Print a list of possible completions',
+ arg=Argument(name='complete', metavar='STRING', optional=True)),
+ ]
+ self.args = []
+
+ def run(self, bugdir, options=None, args=None):
+ if options == None:
+ options = {}
+ if args == None:
+ args = []
+ params = {}
+ for option in self.options:
+ if option.name in options:
+ params[option.name] = options.pop(option.name)
+ elif option.arg != None:
+ params[option.name] = option.arg.default
+ else: # non-arg options are flags, set to default flag value
+ params[option.name] = False
+ if len(options) > 0:
+ raise UserError, 'Invalid options passed to command %s:\n %s' \
+ % (self.name, '\n '.join(['%s: %s' % (k,v)
+ for k,v in options.items()]))
+ for arg in self.args:
+ pass
+ if params['help'] == True:
+ pass
+ else:
+ params.pop('help')
+ if params['complete'] != None:
+ pass
+ else:
+ params.pop('complete')
+ self._setup_io(self.input_encoding, self.output_encoding)
+ self.status = self._run(bugdir, **params)
+ return self.status
+
+ def _run(self, bugdir, **kwargs):
+ pass
+
+ def _setup_io(self, input_encoding=None, output_encoding=None):
+ if input_encoding == None:
+ input_encoding = get_terminal_encoding()
+ if output_encoding == None:
+ output_encoding = get_terminal_encoding()
+ self.stdin = codecs.getwriter(input_encoding)(sys.stdin)
+ self.stdin.encoding = input_encoding
+ self.stdout = codecs.getwriter(output_encoding)(sys.stdout)
+ self.stdout.encoding = output_encoding
+
+ def help(self, *args):
+ return '\n\n'.join([self._usage(),
+ self._option_help(),
+ self._long_help()])
+# if cmd != None:
+# return get_command(cmd).help()
+# cmdlist = []
+# for name in commands():
+# module = get_command(name)
+# cmdlist.append((name, module.__desc__))
+# 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."])
+# longhelp = "\n".join(ret)
+# if parser == None:
+# return longhelp
+# return parser.help_str() + "\n" + longhelp
+
+ def _usage(self):
+ usage = 'usage: be %s [options]' % self.name
+ num_optional = 0
+ for arg in self.args:
+ usage += ' '
+ if arg.optional == True:
+ usage += '['
+ num_optional += 1
+ usage += arg.metavar
+ if arg.repeatable == True:
+ usage += ' ...'
+ usage += ']'*num_optional
+ return usage
+
+ def _option_help(self):
+ o = OptionFormatter(self.options)
+ return o.option_help().strip('\n')
+
+ def _long_help(self):
+ return "A detailed help message."
diff --git a/libbe/command/list.py b/libbe/command/list.py
index 1c3e78d..508cc70 100644
--- a/libbe/command/list.py
+++ b/libbe/command/list.py
@@ -16,184 +16,208 @@
# 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.
-"""List bugs"""
-from libbe import cmdutil, bugdir, bug
+
import os
import re
-__desc__ = __doc__
+
+import libbe
+import libbe.command
+import libbe.bug
+import libbe.util.utility
+import libbe.ui.util
# get a list of * for cmp_*() comparing two bugs.
-AVAILABLE_CMPS = [fn[4:] for fn in dir(bug) if fn[:4] == 'cmp_']
-AVAILABLE_CMPS.remove("attr") # a cmp_* template.
+AVAILABLE_CMPS = [fn[4:] for fn in dir(libbe.bug) if fn[:4] == 'cmp_']
+AVAILABLE_CMPS.remove('attr') # a cmp_* template.
-def execute(args, manipulate_encodings=True, restrict_file_access=False,
- dir="."):
- """
- >>> import os
- >>> bd = bugdir.SimpleBugDir()
- >>> os.chdir(bd.root)
- >>> execute([], manipulate_encodings=False)
- a:om: Bug A
- >>> execute(["--status", "closed"], manipulate_encodings=False)
- b:cm: Bug B
- >>> bd.cleanup()
- """
- parser = get_parser()
- options, args = parser.parse_args(args)
- complete(options, args, parser)
- if len(args) > 0:
- raise cmdutil.UsageError("Too many arguments.")
- cmp_list = []
- if options.sort_by != None:
- for cmp in options.sort_by.split(','):
- if cmp not in AVAILABLE_CMPS:
- raise cmdutil.UserError(
- "Invalid sort on '%s'.\nValid sorts:\n %s"
- % (cmp, '\n '.join(AVAILABLE_CMPS)))
- cmp_list.append(eval('bug.cmp_%s' % cmp))
-
- bd = bugdir.BugDir(from_disk=True,
- manipulate_encodings=manipulate_encodings,
- root=dir)
- bd.load_all_bugs()
- # select status
- if options.status != None:
- if options.status == "all":
- status = bug.status_values
- else:
- status = cmdutil.select_values(options.status, bug.status_values)
- else:
- status = []
- if options.active == True:
- status.extend(list(bug.active_status_values))
- if options.unconfirmed == True:
- status.append("unconfirmed")
- if options.open == True:
- status.append("opened")
- if options.test == True:
- status.append("test")
- if status == []: # set the default value
- status = bug.active_status_values
- # select severity
- if options.severity != None:
- if options.severity == "all":
- severity = bug.severity_values
- else:
- severity = cmdutil.select_values(options.severity,
- bug.severity_values)
- else:
- severity = []
- if options.wishlist == True:
- severity.extend("wishlist")
- if options.important == True:
- serious = bug.severity_values.index("serious")
- severity.append(list(bug.severity_values[serious:]))
- if severity == []: # set the default value
- severity = bug.severity_values
- # select assigned
- if options.assigned != None:
- if options.assigned == "all":
- assigned = "all"
- else:
- possible_assignees = []
- for _bug in bd:
- if _bug.assigned != None \
- and not _bug.assigned in possible_assignees:
- possible_assignees.append(_bug.assigned)
- assigned = cmdutil.select_values(options.assigned,
- possible_assignees)
- print 'assigned', assigned
- else:
- assigned = []
- if options.mine == True:
- assigned.extend('-')
- if assigned == []: # set the default value
- assigned = "all"
- for i in range(len(assigned)):
- if assigned[i] == '-':
- assigned[i] = bd.user_id
- if options.extra_strings != None:
- extra_string_regexps = [re.compile(x) for x in options.extra_strings.split(',')]
-
- def filter(bug):
- if status != "all" and not bug.status in status:
+class Filter (object):
+ def __init__(self, status, severity, assigned, extra_strings_regexps):
+ self.status = status
+ self.severity = severity
+ self.assigned = assigned
+ self.extra_strings_regexps = extra_strings_regexps
+
+ def __call__(self, bug):
+ if self.status != "all" and not bug.status in self.status:
return False
- if severity != "all" and not bug.severity in severity:
+ if self.severity != "all" and not bug.severity in self.severity:
return False
- if assigned != "all" and not bug.assigned in assigned:
+ if self.assigned != "all" and not bug.assigned in self.assigned:
return False
- if options.extra_strings != None:
- if len(bug.extra_strings) == 0 and len(extra_string_regexps) > 0:
+ if len(bug.extra_strings) == 0:
+ if len(self.extra_strings_regexps) > 0:
return False
+ else:
for string in bug.extra_strings:
- for regexp in extra_string_regexps:
+ for regexp in self.extra_strings_regexps:
if not regexp.match(string):
return False
return True
- bugs = [b for b in bd if filter(b) ]
- if len(bugs) == 0 and options.xml == False:
- print "No matching bugs found"
+class List (libbe.command.Command):
+ """List bugs
+
+ >>> import libbe.bugdir
+ >>> bd = libbe.bugdir.SimpleBugDir()
+ >>> bd.uuid = '1234abcd'
+ >>> cmd = List()
+ >>> cmd._setup_io = lambda i_enc,o_enc : None
+ >>> cmd.run(bd)
+ 123/a:om: Bug A
+ >>> cmd.run(bd, {'status':'closed'})
+ 123/b:cm: Bug B
+ >>> bd.cleanup()
+ """
+
+ name = 'list'
+
+ def __init__(self, *args, **kwargs):
+ libbe.command.Command.__init__(self, *args, **kwargs)
+ self.options.extend([
+ libbe.command.Option(name='status',
+ help='Only show bugs matching the STATUS specifier',
+ arg=libbe.command.Argument(
+ name='status', metavar='STATUS', default='active',
+ completion_callback=libbe.ui.util.complete_status)),
+ libbe.command.Option(name='severity',
+ help='Only show bugs matching the SEVERITY specifier',
+ arg=libbe.command.Argument(
+ name='severity', metavar='SEVERITY', default='all',
+ completion_callback=libbe.ui.util.complete_severity)),
+ libbe.command.Option(name='assigned', short_name='a',
+ help='Only show bugs matching ASSIGNED',
+ arg=libbe.command.Argument(
+ name='assigned', metavar='ASSIGNED', default='all',
+ completion_callback=libbe.ui.util.complete_assigned)),
+ libbe.command.Option(name='extra-strings', short_name='e',
+ help='Only show bugs matching STRINGS, e.g. --extra-strings'
+ ' TAG:working,TAG:xml',
+ arg=libbe.command.Argument(
+ name='extra-strings', metavar='STRINGS', default=None,
+ completion_callback=libbe.ui.util.complete_extra_strings)),
+ libbe.command.Option(name='sort', short_name='S',
+ help='Adjust bug-sort criteria with comma-separated list '
+ 'SORT. e.g. "--sort creator,time". '
+ 'Available criteria: %s' % ','.join(AVAILABLE_CMPS),
+ arg=libbe.command.Argument(
+ name='sort', metavar='SORT', default=None,
+ completion_callback=libbe.ui.util.Completer(AVAILABLE_CMPS))),
+ libbe.command.Option(name='uuids', short_name='u',
+ help='Only print the bug UUIDS'),
+ libbe.command.Option(name='xml', short_name='x',
+ help='Dump output in XML format'),
+ ])
+# parser.add_option("-S", "--sort", metavar="SORT-BY", dest="sort_by",
+# help="Adjust bug-sort criteria with comma-separated list SORT-BY. e.g. \"--sort creator,time\". Available criteria: %s" % ','.join(AVAILABLE_CMPS), default=None)
+# # boolean options. All but uuids and xml are special cases of long forms
+# ("w", "wishlist", "List bugs with 'wishlist' severity"),
+# ("i", "important", "List bugs with >= 'serious' severity"),
+# ("A", "active", "List all active bugs"),
+# ("U", "unconfirmed", "List unconfirmed bugs"),
+# ("o", "open", "List open bugs"),
+# ("T", "test", "List bugs in testing"),
+# ("m", "mine", "List bugs assigned to you"))
+# for s in bools:
+# attr = s[1].replace('-','_')
+# short = "-%c" % s[0]
+# long = "--%s" % s[1]
+# help = s[2]
+# parser.add_option(short, long, action="store_true",
+# dest=attr, help=help, default=False)
+# return parser
+#
+# ])
+
+ def _run(self, bugdir, **params):
+ cmp_list, status, severity, assigned, extra_strings_regexps = \
+ self._parse_params(params)
+ filter = Filter(status, severity, assigned, extra_strings_regexps)
+ bugs = [bugdir.bug_from_uuid(uuid) for uuid in bugdir.uuids()]
+ bugs = [b for b in bugs if filter(b) == True]
+ self.result = bugs
+ if len(bugs) == 0 and params['xml'] == False:
+ print "No matching bugs found"
- def list_bugs(cur_bugs, title=None, just_uuids=False, xml=False):
+ # sort bugs
+ bugs = self._sort_bugs(bugs, cmp_list)
+
+ # print list of bugs
+ if params['uuids'] == True:
+ for bug in bugs:
+ print bug.uuid
+ else:
+ self._list_bugs(bugs, xml=params['xml'])
+
+ def _parse_params(self, params):
+ cmp_list = []
+ if params['sort'] != None:
+ for cmp in params['sort'].sort_by.split(','):
+ if cmp not in AVAILABLE_CMPS:
+ raise libbe.command.UserError(
+ "Invalid sort on '%s'.\nValid sorts:\n %s"
+ % (cmp, '\n '.join(AVAILABLE_CMPS)))
+ cmp_list.append(eval('libbe.bug.cmp_%s' % cmp))
+ # select status
+ if params['status'] == 'all':
+ status = libbe.bug.status_values
+ elif params['status'] == 'active':
+ status = list(libbe.bug.active_status_values)
+ elif params['status'] == 'inactive':
+ status = list(libbe.bug.inactive_status_values)
+ else:
+ status = libbe.ui.util.select_values(
+ params['status'], libbe.bug.status_values)
+ # select severity
+ if params['severity'] == 'all':
+ severity = libbe.bug.severity_values
+ elif params['important'] == True:
+ serious = libbe.bug.severity_values.index('serious')
+ severity.append(list(libbe.bug.severity_values[serious:]))
+ else:
+ severity = libbe.ui.util.select_values(
+ params['severity'], bug.severity_values)
+ # select assigned
+ if params['assigned'] == "all":
+ assigned = "all"
+ else:
+ possible_assignees = []
+ for bug in self.bugdir:
+ if bug.assigned != None \
+ and not bug.assigned in possible_assignees:
+ possible_assignees.append(bug.assigned)
+ assigned = libbe.ui.util.select_values(
+ params['assigned'], possible_assignees)
+ for i in range(len(assigned)):
+ if assigned[i] == '-':
+ assigned[i] = params['user-id']
+ if params['extra-strings'] == None:
+ extra_strings_regexps = []
+ else:
+ extra_strings_regexps = [re.compile(x)
+ for x in params['extra-strings'].split(',')]
+ return (cmp_list, status, severity, assigned, extra_strings_regexps)
+
+ def _sort_bugs(self, bugs, cmp_list=[]):
+ cmp_list.extend(libbe.bug.DEFAULT_CMP_FULL_CMP_LIST)
+ cmp_fn = libbe.bug.BugCompoundComparator(cmp_list=cmp_list)
+ bugs.sort(cmp_fn)
+ return bugs
+
+ def _list_bugs(self, bugs, xml=False):
if xml == True:
- print '<?xml version="1.0" encoding="%s" ?>' % bd.encoding
+ print '<?xml version="1.0" encoding="%s" ?>' % self.stdout.encoding
print "<bugs>"
- if len(cur_bugs) > 0:
- if title != None and xml == False:
- print cmdutil.underlined(title)
- for bg in cur_bugs:
+ if len(bugs) > 0:
+ for bug in bugs:
if xml == True:
- print bg.xml(show_comments=True)
- elif just_uuids:
- print bg.uuid
+ print bug.xml(show_comments=True)
else:
- print bg.string(shortlist=True)
+ print bug.string(shortlist=True)
if xml == True:
print "</bugs>"
- # sort bugs
- cmp_list.extend(bug.DEFAULT_CMP_FULL_CMP_LIST)
- cmp_fn = bug.BugCompoundComparator(cmp_list=cmp_list)
- bugs.sort(cmp_fn)
-
- # print list of bugs
- list_bugs(bugs, just_uuids=options.uuids, xml=options.xml)
-
-def get_parser():
- parser = cmdutil.CmdOptionParser("be list [options]")
- parser.add_option("--status", dest="status", metavar="STATUS",
- help="Only show bugs matching the STATUS specifier")
- parser.add_option("--severity", dest="severity", metavar="SEVERITY",
- help="Only show bugs matching the SEVERITY specifier")
- parser.add_option("-a", "--assigned", metavar="ASSIGNED", dest="assigned",
- help="List bugs matching ASSIGNED", default=None)
- parser.add_option("-e", "--extra-strings", metavar="STRINGS", dest="extra_strings",
- help="List bugs matching _all_ extra strings in comma-seperated list STRINGS. e.g. --extra-strings TAG:working,TAG:xml", default=None)
- parser.add_option("-S", "--sort", metavar="SORT-BY", dest="sort_by",
- help="Adjust bug-sort criteria with comma-separated list SORT-BY. e.g. \"--sort creator,time\". Available criteria: %s" % ','.join(AVAILABLE_CMPS), default=None)
- # boolean options. All but uuids and xml are special cases of long forms
- bools = (("u", "uuids", "Only print the bug UUIDS"),
- ("x", "xml", "Dump as XML"),
- ("w", "wishlist", "List bugs with 'wishlist' severity"),
- ("i", "important", "List bugs with >= 'serious' severity"),
- ("A", "active", "List all active bugs"),
- ("U", "unconfirmed", "List unconfirmed bugs"),
- ("o", "open", "List open bugs"),
- ("T", "test", "List bugs in testing"),
- ("m", "mine", "List bugs assigned to you"))
- for s in bools:
- attr = s[1].replace('-','_')
- short = "-%c" % s[0]
- long = "--%s" % s[1]
- help = s[2]
- parser.add_option(short, long, action="store_true",
- dest=attr, help=help, default=False)
- return parser
-
-
-def help():
- longhelp="""
+ def _long_help(self):
+ return """
This command lists bugs. Normally it prints a short string like
576:om: Allow attachments
Where
@@ -224,9 +248,7 @@ assigned
In addition, there are some shortcut options that set boolean flags.
The boolean options are ignored if the matching string option is used.
-""" % (','.join(bug.status_values),
- ','.join(bug.severity_values))
- return get_parser().help_str() + longhelp
+""" % (','.join(bug.status_values), ','.join(bug.severity_values))
def complete(options, args, parser):
for option, value in cmdutil.option_value_pairs(options, parser):
diff --git a/libbe/ui/__init__.py b/libbe/ui/__init__.py
new file mode 100644
index 0000000..b98f164
--- /dev/null
+++ b/libbe/ui/__init__.py
@@ -0,0 +1 @@
+# Copyright
diff --git a/libbe/ui/base.py b/libbe/ui/base.py
new file mode 100644
index 0000000..d26115f
--- /dev/null
+++ b/libbe/ui/base.py
@@ -0,0 +1,23 @@
+ def _setup_user_id(self, user_id):
+ if isinstance(self.storage, storage.vcs.base.VCS):
+ self.storage.user_id = user_id
+ def _guess_user_id(self):
+ if isinstance(self.storage, storage.vcs.base.VCS):
+ return self.storage.get_user_id()
+ def _set_user_id(self, old_user_id, new_user_id):
+ self._setup_user_id(new_user_id)
+ self._prop_save_settings(old_user_id, new_user_id)
+
+ @_versioned_property(name="user_id",
+ doc=
+"""The user's prefered name, e.g. 'John Doe <jdoe@example.com>'. Note
+that the Arch VCS backend *enforces* ids with this format.""",
+ change_hook=_set_user_id,
+ generator=_guess_user_id)
+ def user_id(): return {}
+
+ @_versioned_property(name="default_assignee",
+ doc=
+"""The default assignee for new bugs e.g. 'John Doe <jdoe@example.com>'.""")
+ def default_assignee(): return {}
+
diff --git a/libbe/ui/util/__init__.py b/libbe/ui/util/__init__.py
new file mode 100644
index 0000000..a650d33
--- /dev/null
+++ b/libbe/ui/util/__init__.py
@@ -0,0 +1,69 @@
+# Copyright
+
+class Completer (object):
+ def __init__(self, options):
+ self.options = options
+ def __call__(self, bugdir, fragment=None):
+ return [fragment]
+
+def complete_status(bugdir, fragment=None):
+ return [fragment]
+def complete_severity(bugdir, fragment=None):
+ return [fragment]
+def complete_assigned(bugdir, fragment=None):
+ return [fragment]
+def complete_extra_strings(bugdir, fragment=None):
+ return [fragment]
+
+def select_values(string, possible_values, name="unkown"):
+ """
+ This function allows the user to select values from a list of
+ possible values. The default is to select all the values:
+
+ >>> select_values(None, ['abc', 'def', 'hij'])
+ ['abc', 'def', 'hij']
+
+ The user selects values with a comma-separated limit_string.
+ Prepending a minus sign to such a list denotes blacklist mode:
+
+ >>> select_values('-abc,hij', ['abc', 'def', 'hij'])
+ ['def']
+
+ Without the leading -, the selection is in whitelist mode:
+
+ >>> select_values('abc,hij', ['abc', 'def', 'hij'])
+ ['abc', 'hij']
+
+ In either case, appropriate errors are raised if on of the
+ user-values is not in the list of possible values. The name
+ parameter lets you make the error message more clear:
+
+ >>> select_values('-xyz,hij', ['abc', 'def', 'hij'], name="foobar")
+ Traceback (most recent call last):
+ ...
+ UserError: Invalid foobar xyz
+ ['abc', 'def', 'hij']
+ >>> select_values('xyz,hij', ['abc', 'def', 'hij'], name="foobar")
+ Traceback (most recent call last):
+ ...
+ UserError: Invalid foobar xyz
+ ['abc', 'def', 'hij']
+ """
+ possible_values = list(possible_values) # don't alter the original
+ if string == None:
+ pass
+ elif string.startswith('-'):
+ blacklisted_values = set(string[1:].split(','))
+ for value in blacklisted_values:
+ if value not in possible_values:
+ raise UserError('Invalid %s %s\n %s'
+ % (name, value, possible_values))
+ possible_values.remove(value)
+ else:
+ whitelisted_values = string.split(',')
+ for value in whitelisted_values:
+ if value not in possible_values:
+ raise UserError('Invalid %s %s\n %s'
+ % (name, value, possible_values))
+ possible_values = whitelisted_values
+ return possible_values
diff --git a/libbe/ui/util/cmdutil.py b/libbe/ui/util/cmdutil.py
index c567984..b2d8a99 100644
--- a/libbe/ui/util/cmdutil.py
+++ b/libbe/ui/util/cmdutil.py
@@ -37,48 +37,11 @@ if libbe.TESTING == True:
import doctest
-class UserError(Exception):
- def __init__(self, msg):
- Exception.__init__(self, msg)
-
-class UnknownCommand(UserError):
- def __init__(self, cmd):
- Exception.__init__(self, "Unknown command '%s'" % cmd)
- self.cmd = cmd
-
-class UsageError(Exception):
- pass
-
-class GetHelp(Exception):
- pass
-
-class GetCompletions(Exception):
- def __init__(self, completions=[]):
- msg = "Get allowed completions"
- Exception.__init__(self, msg)
- self.completions = completions
def iter_commands():
for name, module in plugin.iter_plugins("becommands"):
yield name.replace("_", "-"), module
-def get_command(command_name):
- """Retrieves the module for a user command
-
- >>> try:
- ... get_command("asdf")
- ... except UnknownCommand, e:
- ... print e
- Unknown command 'asdf'
- >>> repr(get_command("list")).startswith("<module 'becommands.list' from ")
- True
- """
- cmd = plugin.get_plugin("becommands", command_name.replace("-", "_"))
- if cmd is None:
- raise UnknownCommand(command_name)
- return cmd
-
-
def execute(cmd, args,
manipulate_encodings=True, restrict_file_access=False,
dir="."):
@@ -92,37 +55,20 @@ def execute(cmd, args,
ret = 0
return ret
-def help(cmd=None, parser=None):
- if cmd != None:
- return get_command(cmd).help()
- else:
- cmdlist = []
- for name, module in iter_commands():
- cmdlist.append((name, module.__desc__))
- 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."])
- longhelp = "\n".join(ret)
- if parser == None:
- return longhelp
- return parser.help_str() + "\n" + longhelp
+class GetHelp(Exception):
+ pass
-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 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):
- print "got completion arg"
if hasattr(parser, "command") and parser.command == "be":
comps = []
for command, module in iter_commands():
@@ -132,6 +78,14 @@ def raise_get_completions(option, opt, value, parser):
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)
@@ -211,67 +165,6 @@ def complete_path(path):
comps.extend(glob.glob(comps[0]+"/*"))
return comps
-def underlined(instring):
- """Produces a version of a string that is underlined with '='
-
- >>> underlined("Underlined String")
- 'Underlined String\\n================='
- """
-
- return "%s\n%s" % (instring, "="*len(instring))
-
-def select_values(string, possible_values, name="unkown"):
- """
- This function allows the user to select values from a list of
- possible values. The default is to select all the values:
-
- >>> select_values(None, ['abc', 'def', 'hij'])
- ['abc', 'def', 'hij']
-
- The user selects values with a comma-separated limit_string.
- Prepending a minus sign to such a list denotes blacklist mode:
-
- >>> select_values('-abc,hij', ['abc', 'def', 'hij'])
- ['def']
-
- Without the leading -, the selection is in whitelist mode:
-
- >>> select_values('abc,hij', ['abc', 'def', 'hij'])
- ['abc', 'hij']
-
- In either case, appropriate errors are raised if on of the
- user-values is not in the list of possible values. The name
- parameter lets you make the error message more clear:
-
- >>> select_values('-xyz,hij', ['abc', 'def', 'hij'], name="foobar")
- Traceback (most recent call last):
- ...
- UserError: Invalid foobar xyz
- ['abc', 'def', 'hij']
- >>> select_values('xyz,hij', ['abc', 'def', 'hij'], name="foobar")
- Traceback (most recent call last):
- ...
- UserError: Invalid foobar xyz
- ['abc', 'def', 'hij']
- """
- possible_values = list(possible_values) # don't alter the original
- if string == None:
- pass
- elif string.startswith('-'):
- blacklisted_values = set(string[1:].split(','))
- for value in blacklisted_values:
- if value not in possible_values:
- raise UserError('Invalid %s %s\n %s'
- % (name, value, possible_values))
- possible_values.remove(value)
- else:
- whitelisted_values = string.split(',')
- for value in whitelisted_values:
- if value not in possible_values:
- raise UserError('Invalid %s %s\n %s'
- % (name, value, possible_values))
- possible_values = whitelisted_values
- return possible_values
def restrict_file_access(bugdir, path):
"""
@@ -352,5 +245,7 @@ def bug_comment_from_id(bdir, id):
raise UserError(e.message)
return (bug, comm)
+
+
if libbe.TESTING == True:
suite = doctest.DocTestSuite()
diff --git a/libbe/ui/util/repo.py b/libbe/ui/util/repo.py
new file mode 100644
index 0000000..174c5b1
--- /dev/null
+++ b/libbe/ui/util/repo.py
@@ -0,0 +1,4 @@
+# Copyright
+
+def complete(string):
+ pass
diff --git a/libbe/util/plugin.py b/libbe/util/plugin.py
index edb4922..982c5ca 100644
--- a/libbe/util/plugin.py
+++ b/libbe/util/plugin.py
@@ -26,50 +26,42 @@ import os
import os.path
import sys
-import libbe
-if libbe.TESTING == True:
- import doctest
-def import_by_name(mod_name):
- module = __import__(mod_name)
- components = mod_name.split('.')
+_PLUGIN_PATH = os.path.realpath(
+ os.path.dirname(
+ os.path.dirname(
+ os.path.dirname(__file__))))
+if _PLUGIN_PATH not in sys.path:
+ sys.path.append(_PLUGIN_PATH)
+
+def import_by_name(modname):
+ """
+ >>> mod = import_by_name('libbe.bugdir')
+ >>> 'BugDir' in dir(mod)
+ True
+ >>> import_by_name('libbe.highly_unlikely')
+ Traceback (most recent call last):
+ ...
+ ImportError: No module named highly_unlikely
+ """
+ module = __import__(modname)
+ components = modname.split('.')
for comp in components[1:]:
module = getattr(module, comp)
return module
-def iter_plugins(prefix):
+def modnames(prefix):
"""
- >>> "list" in [n for n,m in iter_plugins("becommands")]
+ >>> 'list' in [n for n in modnames('libbe.command')]
True
- >>> "plugin" in [n for n,m in iter_plugins("libbe")]
+ >>> 'plugin' in [n for n in modnames('libbe.util')]
True
"""
- modfiles = os.listdir(os.path.join(plugin_path, prefix))
+ components = prefix.split('.')
+ modfiles = os.listdir(os.path.join(_PLUGIN_PATH, *components))
modfiles.sort()
for modfile in modfiles:
if modfile.startswith('.'):
continue # the occasional emacs temporary file
- if modfile.endswith(".py") and modfile != "__init__.py":
- yield modfile[:-3], my_import(prefix+"."+modfile[:-3])
-
-
-def get_plugin(prefix, name):
- """
- >>> get_plugin("becommands", "asdf") is None
- True
- >>> q = repr(get_plugin("becommands", "list"))
- >>> q.startswith("<module 'becommands.list' from ")
- True
- """
- dirprefix = os.path.join(*prefix.split('.'))
- command_path = os.path.join(plugin_path, dirprefix, name+".py")
- if os.path.isfile(command_path):
- return my_import(prefix + "." + name)
- return None
-
-plugin_path = os.path.realpath(os.path.dirname(os.path.dirname(__file__)))
-if plugin_path not in sys.path:
- sys.path.append(plugin_path)
-
-if libbe.TESTING == True:
- suite = doctest.DocTestSuite()
+ if modfile.endswith('.py') and modfile != '__init__.py':
+ yield modfile[:-3]
diff --git a/libbe/util/utility.py b/libbe/util/utility.py
index f954422..779eaa5 100644
--- a/libbe/util/utility.py
+++ b/libbe/util/utility.py
@@ -147,5 +147,14 @@ def iterable_full_of_strings(value, alternative=None):
return False
return True
+def underlined(instring):
+ """Produces a version of a string that is underlined with '='
+
+ >>> underlined("Underlined String")
+ 'Underlined String\\n================='
+ """
+
+ return "%s\n%s" % (instring, "="*len(instring))
+
if libbe.TESTING == True:
suite = doctest.DocTestSuite()
diff --git a/test.py b/test.py
index 2b93bcf..fecff8c 100644
--- a/test.py
+++ b/test.py
@@ -1,3 +1,5 @@
+# Copyright
+
"""Usage: python test.py [module(s) ...]
When called without optional module names, run the test suites for
@@ -49,14 +51,14 @@ def python_tree(root_path='libbe', root_modname='libbe'):
f.parent.append(f)
return tree
-def add_module_tests(suite, module_name):
- mod = import_by_name(module_name)
- if mod == None:
- raise KeyError, 'module "%s" not found' % module_name
+def add_module_tests(suite, modname):
+ mod = import_by_name(modname)
if hasattr(mod, 'suite'):
s = mod.suite
else:
s = unittest.TestLoader().loadTestsFromModule(mod)
+ sdoc = doctest.DocTestSuite(mod)
+ suite.addTest(sdoc)
suite.addTest(s)
suite = unittest.TestSuite()