diff options
author | W. Trevor King <wking@drexel.edu> | 2009-07-11 06:39:48 -0400 |
---|---|---|
committer | W. Trevor King <wking@drexel.edu> | 2009-07-11 06:39:48 -0400 |
commit | 71579e9906be1da2ec2e53a53beebece832cd04e (patch) | |
tree | d0f0f0a8aaff5d812b77b78f82fda9ccabd5c7fe | |
parent | e5c0d5f2f3f7637cad6baca9e33778d0c054195d (diff) | |
parent | f0300038113e2754d83ee73f32a51072ad9b1f3b (diff) | |
download | bugseverywhere-71579e9906be1da2ec2e53a53beebece832cd04e.tar.gz |
Merged "be comment --xml" code from home.
-rwxr-xr-x | be | 30 | ||||
-rw-r--r-- | becommands/comment.py | 43 | ||||
-rw-r--r-- | becommands/tag.py | 2 | ||||
-rw-r--r-- | libbe/cmdutil.py | 18 | ||||
-rw-r--r-- | libbe/comment.py | 86 | ||||
-rwxr-xr-x | test_usage.sh | 6 |
6 files changed, 137 insertions, 48 deletions
@@ -35,21 +35,21 @@ elif sys.argv[1] == '--version': print _version.version_info["revision_id"] else: try: - try: - sys.exit(cmdutil.execute(sys.argv[1], sys.argv[2:])) - except KeyError, e: - raise cmdutil.UserError("Unknown command \"%s\"" % e.args[0]) - except cmdutil.GetHelp: - print cmdutil.help(sys.argv[1]) - sys.exit(0) - except cmdutil.GetCompletions, e: - print '\n'.join(e.completions) - sys.exit(0) - except cmdutil.UsageError, e: - print "Invalid usage:", e - print "\nArgs:", sys.argv[1:] - print cmdutil.help(sys.argv[1]) - sys.exit(1) + sys.exit(cmdutil.execute(sys.argv[1], sys.argv[2:])) + except cmdutil.GetHelp: + print cmdutil.help(sys.argv[1]) + sys.exit(0) + except cmdutil.GetCompletions, e: + print '\n'.join(e.completions) + sys.exit(0) + except cmdutil.UnknownCommand, e: + print e + sys.exit(1) + except cmdutil.UsageError, e: + print "Invalid usage:", e + print "\nArgs:", sys.argv[1:] + print cmdutil.help(sys.argv[1]) + sys.exit(1) except cmdutil.UserError, e: print "ERROR:" print e diff --git a/becommands/comment.py b/becommands/comment.py index 0b3a576..a11cd90 100644 --- a/becommands/comment.py +++ b/becommands/comment.py @@ -1,7 +1,6 @@ # Copyright (C) 2005-2009 Aaron Bentley and Panometrics, Inc. # Chris Ball <cjb@laptop.org> # W. Trevor King <wking@drexel.edu> -# <abentley@panoramicfeedback.com> # # 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 @@ -17,9 +16,13 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA """Add a comment to a bug""" -from libbe import cmdutil, bugdir, settings_object, editor +from libbe import cmdutil, bugdir, comment, settings_object, editor import os import sys +try: # import core module, Python >= 2.5 + from xml.etree import ElementTree +except ImportError: # look for non-core module + from elementtree import ElementTree __desc__ = __doc__ def execute(args, test=False): @@ -108,15 +111,45 @@ def execute(args, test=False): if not body.endswith('\n'): body+='\n' - comment = parent.new_reply(body=body) - if options.content_type != None: - comment.content_type = options.content_type + if options.XML == False: + new = parent.new_reply(body=body) + if options.content_type != None: + new.content_type = options.content_type + else: # import XML comment [list] + # read in the comments + comment_list = ElementTree.XML(body) + if comment_list.tag not in ["bug", "comment-list"]: + raise comment.InvalidXML( + comment_list, "root element must be <bug> or <comment-list>") + new_comments = [] + uuids = [c.uuid for c in bug.comment_root.traverse()] # unique uuid check should be unique alt_id check + for child in comment_list.getchildren(): + if child.tag == "comment": + new = comment.Comment(bug) + new.from_xml(ElementTree.tostring(child)) + if new.uuid in uuids: + raise cmdutil.UserError( + "Clashing comment uuids: %s" % new.uuid) + uuids.append(new.uuid) + if new.in_reply_to in [settings_object.EMPTY, None]: + new.in_reply_to = parent.uuid + new_comments.append(new) + else: + print >> sys.stderr, "Ignoring unknown tag %s in %s" \ + % (child.tag, comment_list.tag) + comment.list_to_root(new_comments,bug,root=parent) # link new comments + # Protect against programmer error causing data loss: + kids = [c.uuid for c in parent.traverse()] + for nc in new_comments: + assert nc.uuid in kids, "%s wasn't added to %s" % (nc.uuid, parent.uuid) bd.save() def get_parser(): parser = cmdutil.CmdOptionParser("be comment ID [COMMENT]") parser.add_option("-c", "--content-type", metavar="MIME", dest="content_type", help="Set comment content-type (e.g. text/plain)", default=None) + parser.add_option("-x", "--xml", action="store_true", default=False, + dest='XML', help="Use COMMENT to specify an XML comment description rather than the comment body. The root XML element should be either <bug> or <comment-list> with one or more <comment> children. The syntax for the <comment> elements should match that generated by 'be show --xml COMMENT-ID'. Unrecognized tags are ignored. Missing tags are left at the default value (e.g. <content-type>) or auto-generated (e.g <uuid>). An exception is raised if <uuid> conflicts with an existing comment.") # Are comment UUIDs global? no. should match on alt_id anyway... return parser longhelp=""" diff --git a/becommands/tag.py b/becommands/tag.py index ab0324e..1b20ddb 100644 --- a/becommands/tag.py +++ b/becommands/tag.py @@ -102,7 +102,7 @@ def execute(args, test=False): else: # add the tag estrs.append(tag_string) bug.extra_strings = estrs # reassign to notice change - bug.save() + bd.save() tags = [] for estr in bug.extra_strings: diff --git a/libbe/cmdutil.py b/libbe/cmdutil.py index 0dd8ad0..ada0423 100644 --- a/libbe/cmdutil.py +++ b/libbe/cmdutil.py @@ -32,10 +32,10 @@ class UserError(Exception): def __init__(self, msg): Exception.__init__(self, msg) -class UserErrorWrap(UserError): - def __init__(self, exception): - UserError.__init__(self, str(exception)) - self.exception = exception +class UnknownCommand(UserError): + def __init__(self, cmd): + Exception.__init__(self, "Unknown command '%s'" % cmd) + self.cmd = cmd class UsageError(Exception): pass @@ -56,15 +56,17 @@ def iter_commands(): def get_command(command_name): """Retrieves the module for a user command - >>> get_command("asdf") - Traceback (most recent call last): - UserError: Unknown command asdf + >>> 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 UserError("Unknown command %s" % command_name) + raise UnknownCommand(command_name) return cmd diff --git a/libbe/comment.py b/libbe/comment.py index 9d4f744..c4000fa 100644 --- a/libbe/comment.py +++ b/libbe/comment.py @@ -2,7 +2,6 @@ # Copyright (C) 2008-2009 Chris Ball <cjb@laptop.org> # Thomas Habets <thomas@habets.pp.se> # W. Trevor King <wking@drexel.edu> -# <abentley@panoramicfeedback.com> # # 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 @@ -22,8 +21,11 @@ import email.mime.base, email.encoders import os import os.path import time +try: # import core module, Python >= 2.5 + from xml.etree import ElementTree +except ImportError: # look for non-core module + from elementtree import ElementTree import xml.sax.saxutils -import textwrap import doctest from beuuid import uuid_gen @@ -43,16 +45,25 @@ class InvalidShortname(KeyError): self.shortname = shortname self.shortnames = shortnames +class InvalidXML(ValueError): + def __init__(self, element, comment): + msg = "Invalid comment xml: %s\n %s\n" \ + % (comment, ElementTree.tostring(element)) + ValueError.__init__(self, msg) + self.element = element + self.comment = comment INVALID_UUID = "!!~~\n INVALID-UUID \n~~!!" -def _list_to_root(comments, bug): +def list_to_root(comments, bug, root=None): """ - Convert a raw list of comments to single (dummy) root comment. We - use a dummy root comment, because there can be several comment - threads rooted on the same parent bug. To simplify comment - interaction, we condense these threads into a single thread with a - Comment dummy root. + Convert a raw list of comments to single root comment. We use a + dummy root comment by default, because there can be several + comment threads rooted on the same parent bug. To simplify + comment interaction, we condense these threads into a single + thread with a Comment dummy root. Can also be used to append + a list of subcomments to a non-dummy root comment, so long as + all the new comments are descendants of the root comment. No Comment method should use the dummy comment. """ @@ -61,6 +72,10 @@ def _list_to_root(comments, bug): for comment in comments: assert comment.uuid != None uuid_map[comment.uuid] = comment + if root == None: + root = Comment(bug, uuid=INVALID_UUID) + else: + uuid_map[root.uuid] = root for comm in comments: rep = comm.in_reply_to if rep == None or rep == settings_object.EMPTY or rep == bug.uuid: @@ -69,9 +84,8 @@ def _list_to_root(comments, bug): parentUUID = comm.in_reply_to parent = uuid_map[parentUUID] parent.add_reply(comm) - dummy_root = Comment(bug, uuid=INVALID_UUID) - dummy_root.extend(root_comments) - return dummy_root + root.extend(root_comments) + return root def loadComments(bug, load_full=False): """ @@ -90,7 +104,7 @@ def loadComments(bug, load_full=False): comm.load_settings() dummy = comm.body # force the body to load comments.append(comm) - return _list_to_root(comments, bug) + return list_to_root(comments, bug) def saveComments(bug): path = bug.get_path("comments") @@ -265,6 +279,47 @@ class Comment(Tree, settings_object.SavedSettingsObject): sep = '\n' + istring return istring + sep.join(lines).rstrip('\n') + def from_xml(self, xml_string, verbose=True): + """ + Warning: does not check for comment uuid collision. + >>> commA = Comment(bug=None, body="Some\\ninsightful\\nremarks\\n") + >>> commA.uuid = "0123" + >>> commA.time_string = "Thu, 01 Jan 1970 00:00:00 +0000" + >>> xml = commA.xml(shortname="com-1") + >>> commB = Comment() + >>> commB.from_xml(xml) + >>> attrs=['uuid','in_reply_to','From','time_string','content_type','body'] + >>> for attr in attrs: + ... if getattr(commB, attr) != getattr(commA, attr): + ... estr = "Mismatch on %s: '%s' should be '%s'" + ... args = (attr, getattr(commB, attr), getattr(commA, attr)) + ... print estr % args + """ + comment = ElementTree.XML(xml_string) + if comment.tag != "comment": + raise InvalidXML(comment, "root element must be <comment>") + tags=['uuid','in-reply-to','from','date','content-type','body'] + for child in comment.getchildren(): + if child.tag == "short-name": + pass + elif child.tag in tags: + if child.text == None: + text = settings_object.EMPTY + else: + text = xml.sax.saxutils.unescape(child.text.strip()) + if child.tag == 'from': + attr_name = "From" + elif child.tag == 'date': + attr_name = 'time_string' + else: + attr_name = child.tag.replace('-','_') + if attr_name == "body": + text += '\n' # replace strip()ed trailing newline + setattr(self, attr_name, text) + elif verbose == True: + print >> sys.stderr, "Ignoring unknown tag %s in %s" \ + % (child.tag, comment.tag) + def string(self, indent=0, shortname=None): """ >>> comm = Comment(bug=None, body="Some\\ninsightful\\nremarks\\n") @@ -287,13 +342,10 @@ class Comment(Tree, settings_object.SavedSettingsObject): lines.append("From: %s" % (self._setting_attr_string("From"))) lines.append("Date: %s" % self.time_string) lines.append("") - #lines.append(textwrap.fill(self.body or "", - # width=(79-indent))) if self.content_type.startswith("text/"): lines.extend((self.body or "").splitlines()) else: lines.append("Content type %s not printable. Try XML output instead" % self.content_type) - # some comments shouldn't be wrapped... istring = ' '*indent sep = '\n' + istring @@ -335,9 +387,6 @@ class Comment(Tree, settings_object.SavedSettingsObject): def save(self): assert self.body != None, "Can't save blank comment" - #if self.in_reply_to == None: - # raise Exception, str(self)+'\n'+str(self.settings)+'\n'+str(self._settings_loaded) - #assert self.in_reply_to != None, "Comment must be a reply to something" self.save_settings() self._set_comment_body(new=self.body, force=True) @@ -362,7 +411,6 @@ class Comment(Tree, settings_object.SavedSettingsObject): """ reply = Comment(self.bug, body=body) self.add_reply(reply) - #raise Exception, "new reply added (%s),\n%s\n%s\n\t--%s--" % (body, self, reply, reply.in_reply_to) return reply def string_thread(self, string_method_name="string", name_map={}, diff --git a/test_usage.sh b/test_usage.sh index b2e2cab..05832d3 100755 --- a/test_usage.sh +++ b/test_usage.sh @@ -124,7 +124,13 @@ BUGB=`echo "$OUT" | sed -n 's/Created bug with ID //p'` be comment $BUGB "Blissfully unaware of a similar bug" be merge $BUG $BUGB # join BUGB to BUG be show $BUG # show bug details & comments +# you can also export/import XML bugs/comments +OUT=`be new 'yet more fun'` +BUGC=`echo "$OUT" | sed -n 's/Created bug with ID //p'` +be comment $BUGC "The ants go marching..." +be show --xml $BUGC | be comment --xml ${BUG}:2 - be remove $BUG # decide that you don't like that bug after all + cd / rm -rf $TESTDIR |