aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorW. Trevor King <wking@drexel.edu>2009-07-11 06:39:48 -0400
committerW. Trevor King <wking@drexel.edu>2009-07-11 06:39:48 -0400
commit71579e9906be1da2ec2e53a53beebece832cd04e (patch)
treed0f0f0a8aaff5d812b77b78f82fda9ccabd5c7fe
parente5c0d5f2f3f7637cad6baca9e33778d0c054195d (diff)
parentf0300038113e2754d83ee73f32a51072ad9b1f3b (diff)
downloadbugseverywhere-71579e9906be1da2ec2e53a53beebece832cd04e.tar.gz
Merged "be comment --xml" code from home.
-rwxr-xr-xbe30
-rw-r--r--becommands/comment.py43
-rw-r--r--becommands/tag.py2
-rw-r--r--libbe/cmdutil.py18
-rw-r--r--libbe/comment.py86
-rwxr-xr-xtest_usage.sh6
6 files changed, 137 insertions, 48 deletions
diff --git a/be b/be
index 2023daa..b68a414 100755
--- a/be
+++ b/be
@@ -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